feign拦截器和解码器
原文链接:https://blog.csdn.net/xuguofeng2016/article/details/107882619
业务需求
在Spring Cloud的项目中,A服务使用Feign调用B服务的某个接口,如果需要传递全局认证token或参数,在方法参数里面加相应字段的方式显然是不可取的。
首先想到的是AOP方式,使用切面拦截Feign方法,在AOP切面里面向方法参数里面添加数据,Feign方法执行完成之后,从响应对象里面获取返回的数据,这样的方式可以解决数据的传递和接收,但也必将需要方法参数和响应对象的支持,与业务耦合,并不是合理的架构实现方案。
如果有某种机制可以拦截到Feign的请求对象和响应对象,便可以获取到请求头和响应头,就可以使用请求头和响应头来传递数据。
经过一番调查,了解到Feign的RequestInterceptor可以拦截到Feign请求,可以获取到请求对象和请求头,但是RequestInterceptor无法处理响应。
于是又进行调查,得知解码器Decoder是对响应进行解码的组件,可以获取到响应对象和响应头。
在调查过程中,还有另外的收获:FeignClientsConfiguration类。
FeignClientsConfiguration类
Feign默认配置类是FeignClientsConfiguration类,该类定义了Feign默认的编码器、解码器、所使用的契约等。
Spring Cloud允许通过注解@FeignClient的configuration属性自定义Feign配置,自定义配置的优先级比FeignClientsConfiguration要高。
这个类的核心代码如下:
@Configuration
public class FeignClientsConfiguration {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Autowired(required = false)
private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
@Autowired(required = false)
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
@Autowired(required = false)
private Logger logger;
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
public Encoder feignEncoder() {
return new SpringEncoder(this.messageConverters);
}
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
Feign请求拦截器
参考FeignClientsConfiguration类,我们可以编写一个configuration类,注入自定义的RequestInterceptor实现类对象,在apply(RequestTemplate requestTemplate)方法中获取到请求RestTemplate对象,使用RequestTemplate对象设置请求参数、添加请求头。
示例如下:
@Configuration
public class MyConfig {
@Bean("myInterceptor")
public RequestInterceptor getRequestInterceptor() {
return new MyClientInterceptor();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
MyClientInterceptor类:
class MyClientInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.query("name", "Allen");
requestTemplate
.header("token", "token")
.header("id", id);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Feign解码器
解码器概述
Feign解码器负责对响应进行解码,返回符合Feign接口需求的对象。
我们可以参考FeignClientsConfiguration类中的方式编写和注入自定义的解码器。
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
return new OptionalDecoder(
new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
- 1
- 2
- 3
- 4
- 5
- 6
继承SpringDecoder自定义解码器
class TraceDecoder extends SpringDecoder {
TraceDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
super(messageConverters);
}
@Override
public Object decode(Response response, Type type) throws IOException, FeignException {
// 这里可以从response对象里面获取响应头和响应体
// 获取响应头
Map<String, Collection<String>> headers = response.headers();
return super.decode(response, type);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
注入自定义解码器
feignDecoder()方法完全参考了FeignClientsConfiguration类的写法。
@Slf4j
@Configuration
@ConditionalOnClass({Feign.class})
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class CommonLogFeignConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public Decoder feignDecoder() {
return new OptionalDecoder(new ResponseEntityDecoder(new TraceDecoder(this.messageConverters)));
}
}