SpringCloud中分析讲解Feign组件添加请求头有哪些坑梳理


Posted in Java/Android onJune 21, 2022
目录

按官方修改的示例:

#MidServerClient.java
import feign.Param;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(value = "edu-mid-server")
public interface MidServerClient {
    @RequestMapping(value = "/test/header", method = RequestMethod.GET)
    @Headers({"userInfo:{userInfo}"})
    Object headerTest(@Param("userInfo") String userInfo);
}

提示错误:

java.lang.IllegalArgumentException: method GET must not have a request body.

分析

通过断点debug发现feign发请求时把userInfo参数当成了requestBody来处理,而okhttp3会检测get请求不允许有body(其他类型的请求哪怕不报错,但因为不是设置到请求头,依然不满足需求)。

查阅官方文档里是通过Contract(Feign.Contract.Default)来解析注解的:

Feign annotations define the Contract between the interface and how the underlying client should work. Feign's default contract defines the following annotations:

Annotation Interface Target Usage
@RequestLine Method Defines the HttpMethod and UriTemplate for request. Expressions, values wrapped in curly-braces {expression} are resolved using their corresponding @Param annotated parameters.
@Param Parameter Defines a template variable, whose value will be used to resolve the corresponding template Expression, by name.
@Headers Method, Type Defines a HeaderTemplate; a variation on a UriTemplate. that uses @Param annotated values to resolve the corresponding Expressions. When used on a Type, the template will be applied to every request. When used on a Method, the template will apply only to the annotated method.
@QueryMap Parameter Defines a Map of name-value pairs, or POJO, to expand into a query string.
@HeaderMap Parameter Defines a Map of name-value pairs, to expand into Http Headers
@Body Method Defines a Template, similar to a UriTemplate and HeaderTemplate, that uses @Param annotated values to resolve the corresponding Expressions.

从自动配置类找到使用的是spring cloud的SpringMvcContract(用来解析@RequestMapping相关的注解),而这个注解并不会处理解析上面列的注解

@Configuration
public class FeignClientsConfiguration {
	@Bean
	@ConditionalOnMissingBean
	public Contract feignContract(ConversionService feignConversionService) {
		return new SpringMvcContract(this.parameterProcessors, feignConversionService);
	}

解决

原因找到了:spring cloud使用了自己的SpringMvcContract来解析注解,导致默认的注解解析方式失效。 解决方案自然就是重新解析处理feign的注解,这里通过自定义Contract继承SpringMvcContract再把Feign.Contract.Default解析逻辑般过来即可(重载的方法是在SpringMvcContract基础上做进一步解析,否则Feign对RequestMapping相关对注解解析会失效)

代码如下(此处只对@Headers、@Param重新做了解析):

#FeignCustomContract.java
import feign.Headers;
import feign.MethodMetadata;
import feign.Param;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.core.convert.ConversionService;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
public class FeignCustomContract extends SpringMvcContract {
    public FeignCustomContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService conversionService) {
        super(annotatedParameterProcessors, conversionService);
    }
    @Override
    protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
        //解析mvc的注解
        super.processAnnotationOnMethod(data, methodAnnotation, method);
        //解析feign的headers注解
        Class<? extends Annotation> annotationType = methodAnnotation.annotationType();
        if (annotationType == Headers.class) {
            String[] headersOnMethod = Headers.class.cast(methodAnnotation).value();
            checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.", method.getName());
            data.template().headers(toMap(headersOnMethod));
        }
    }
    @Override
    protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
        boolean isMvcHttpAnnotation = super.processAnnotationsOnParameter(data, annotations, paramIndex);
        boolean isFeignHttpAnnotation = false;
        for (Annotation annotation : annotations) {
            Class<? extends Annotation> annotationType = annotation.annotationType();
            if (annotationType == Param.class) {
                Param paramAnnotation = (Param) annotation;
                String name = paramAnnotation.value();
                checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.", paramIndex);
                nameParam(data, name, paramIndex);
                isFeignHttpAnnotation = true;
                if (!data.template().hasRequestVariable(name)) {
                    data.formParams().add(name);
                }
            }
        }
        return isMvcHttpAnnotation || isFeignHttpAnnotation;
    }
    private static Map<String, Collection<String>> toMap(String[] input) {
        Map<String, Collection<String>> result =
                new LinkedHashMap<String, Collection<String>>(input.length);
        for (String header : input) {
            int colon = header.indexOf(':');
            String name = header.substring(0, colon);
            if (!result.containsKey(name)) {
                result.put(name, new ArrayList<String>(1));
            }
            result.get(name).add(header.substring(colon + 1).trim());
        }
        return result;
    }
}
#FeignCustomConfiguration.java
import feign.Contract;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class FeignCustomConfiguration {
    @Autowired(required = false)
    private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
    @Bean
    @ConditionalOnProperty(name = "feign.feign-custom-contract", havingValue = "true", matchIfMissing = true)
    public Contract feignContract(ConversionService feignConversionService) {
        return new FeignCustomContract(this.parameterProcessors, feignConversionService);
    }

改完马上进行新一顿的操作, 看请求日志已经设置成功,响应OK!:

请求: {"type":"OKHTTP_REQ","uri":"/test/header","httpMethod":"GET","header":"{"accept":["/"],"userinfo":["{"userId":"sssss","phone":"13544445678],"x-b3-parentspanid":["e49c55484f6c19af"],"x-b3-sampled":["0"],"x-b3-spanid":["1d131b4ccd08d964"],"x-b3-traceid":["9405ce71a13d8289"]}","param":""}

响应 {"type":"OKHTTP_RESP","uri":"/test/header","respStatus":0,"status":200,"time":5,"header":"{"cache-control":["no-cache,no-store,max-age=0,must-revalidate"],"connection":["keep-alive"],"content-length":["191"],"content-type":["application/json;charset=UTF-8"],"date":["Fri,11Oct201913:02:41GMT"],"expires":["0"],"pragma":["no-cache"],"x-content-type-options":["nosniff"],"x-frame-options":["DENY"],"x-xss-protection":["1;mode=block"]}"}

feign官方链接

spring cloud feign 链接

(另一种实现请求头的方式:实现RequestInterceptor,但是存在hystrix线程切换的坑)

到此这篇关于SpringCloud中分析讲解Feign组件添加请求头有哪些坑梳理的文章就介绍到这了,更多相关SpringCloud Feign组件内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Java/Android 相关文章推荐
解决Maven项目中 Invalid bound statement 无效的绑定问题
Jun 15 Java/Android
JVM入门之类加载与字节码技术(类加载与类的加载器)
Jun 15 Java/Android
浅谈@Value和@Bean的执行顺序问题
Jun 16 Java/Android
IDEA使用SpringAssistant插件创建SpringCloud项目
Jun 23 Java/Android
Java基础之this关键字的使用
Jun 30 Java/Android
详解Spring Boot使用系统参数表提升系统的灵活性
Jun 30 Java/Android
深入解读Java三大集合之map list set的用法
Nov 11 Java/Android
Java实现经典游戏泡泡堂的示例代码
Apr 04 Java/Android
Spring Boot配合PageHelper优化大表查询数据分页
Apr 20 Java/Android
Spring Data JPA框架持久化存储数据到数据库
Apr 28 Java/Android
Android studio 简单计算器的编写
May 20 Java/Android
解决spring.thymeleaf.cache=false不起作用的问题
Jun 10 Java/Android
Mybatis-plus配置分页插件返回统一结果集
SpringCloud超详细讲解Feign声明式服务调用
Jun 21 #Java/Android
使用Postman测试需要授权的接口问题
Jun 21 #Java/Android
springboot集成redis存对象乱码的问题及解决
Jun 16 #Java/Android
SpringBoot使用AOP实现统计全局接口访问次数详解
Jun 16 #Java/Android
Java中的Kotlin 内部类原理
Jun 16 #Java/Android
Spring Security动态权限的实现方法详解
You might like
解决phpmyadmin中文乱码问题。。。
2007/01/18 PHP
php 无限分类 树形数据格式化代码
2016/10/11 PHP
PHP安装BCMath扩展的方法
2019/02/13 PHP
jQuery MD5加密实现代码
2010/03/15 Javascript
使用jQuery轻松实现Ajax的实例代码
2010/08/16 Javascript
javascript中onmouse事件在div中失效问题的解决方法
2012/01/09 Javascript
jQuery setTimeout()函数使用方法
2013/04/07 Javascript
JS+CSS实现仿触屏手机拨号盘界面及功能模拟完整实例
2015/05/16 Javascript
JS获取当前脚本文件的绝对路径
2016/03/02 Javascript
Js 获取、判断浏览器版本信息的简单方法
2016/08/08 Javascript
JavaScript实现邮箱地址自动匹配功能代码
2016/11/28 Javascript
JS原生数据双向绑定实现代码
2017/08/14 Javascript
原生JS实现多个小球碰撞反弹效果示例
2018/01/31 Javascript
VUE + UEditor 单图片跨域上传功能的实现方法
2018/02/08 Javascript
Vue-router的使用和出现空白页,路由对象属性详解
2018/09/03 Javascript
koa2使用ejs和nunjucks作为模板引擎的使用
2018/11/27 Javascript
JavaScript如何把两个数组对象合并过程解析
2019/10/10 Javascript
基于js实现判断浏览器类型代码实例
2020/07/17 Javascript
在Django的模板中使用认证数据的方法
2015/07/23 Python
Python版名片管理系统
2018/11/30 Python
Empty test suite.(PyCharm程序运行错误的解决方法)
2018/11/30 Python
Python Image模块基本图像处理操作小结
2019/04/13 Python
django认证系统 Authentication使用详解
2019/07/22 Python
利用Python产生加密表和解密表的实现方法
2019/10/15 Python
Python数据可视化处理库PyEcharts柱状图,饼图,线性图,词云图常用实例详解
2020/02/10 Python
python 发送邮件的示例代码(Python2/3都可以直接使用)
2020/12/03 Python
使用pandas实现筛选出指定列值所对应的行
2020/12/13 Python
详解利用css3的var()实现运行时改变scss的变量值
2021/03/02 HTML / CSS
详解移动端HTML5音频与视频问题及解决方案
2018/08/22 HTML / CSS
配置H5的滚动条样式的示例代码
2018/03/09 HTML / CSS
Algenist奥杰尼官网:微藻抗衰老护肤品牌
2017/07/15 全球购物
书法比赛获奖感言
2014/02/10 职场文书
大学生工作自荐书
2014/06/16 职场文书
党性教育心得体会
2014/09/03 职场文书
2014年最新领导班子整改方案
2014/09/27 职场文书
党支部书记岗位职责
2015/02/15 职场文书