Feign interceptor and decoder
Business requirements
In the Spring Cloud project, service A uses Feign to call an interface of service B. if it is necessary to pass A global authentication token or parameter, it is obviously not advisable to add A corresponding field in the method parameter.
The first thought is the AOP method, which uses the aspect to intercept the Feign method, adds data to the method parameters in the AOP aspect, and obtains the returned data from the response object after the execution of the Feign method. This method can solve the transmission and reception of data, but it will also need the support of the method parameters and response object to couple with the business, It is not a reasonable architecture implementation scheme.
If there is a mechanism to intercept Feign's request object and response object, the request header and response header can be obtained, and the request header and response header can be used to transfer data.
After some investigation, it is learned that Feign's RequestInterceptor can intercept Feign requests and obtain request objects and request headers, but the RequestInterceptor cannot process the response.
Therefore, the investigation shows that the Decoder is a component that decodes the response, and can obtain the response object and response header.
During the investigation, there is another harvest: FeignClientsConfiguration class.
FeignClientsConfiguration class
Feign's default configuration class is FeignClientsConfiguration class, which defines feign's default encoder, decoder, contract, etc.
Spring Cloud allows you to customize Feign configuration by annotating the configuration attribute of @ FeignClient. The priority of customized configuration is higher than FeignClientsConfiguration.
The core code of this class is as follows:
@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); } }
Feign request interceptor
Referring to FeignClientsConfiguration class, we can write a configuration class, inject a custom RequestInterceptor implementation class object, obtain the request RestTemplate object in the apply(RequestTemplate requestTemplate) method, and use the RequestTemplate object to set request parameters and add request headers.
Examples are as follows:
@Configuration public class MyConfig { @Bean("myInterceptor") public RequestInterceptor getRequestInterceptor() { return new MyClientInterceptor(); } }
MyClientInterceptor class:
class MyClientInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { requestTemplate.query("name", "Allen"); requestTemplate .header("token", "token") .header("id", id); } }
Feign decoder
Decoder overview
Feign decoder is responsible for decoding the response and returning objects that meet feign interface requirements.
We can refer to the FeignClientsConfiguration class to write and inject custom decoders.
@Bean @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder( new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); }
Inherit spring decoder custom decoder
class TraceDecoder extends SpringDecoder { TraceDecoder(ObjectFactory<HttpMessageConverters> messageConverters) { super(messageConverters); } @Override public Object decode(Response response, Type type) throws IOException, FeignException { // Here you can get the response header and response body from the response object // Get response header Map<String, Collection<String>> headers = response.headers(); return super.decode(response, type); } }
Inject custom decoder
The feignDecoder() method completely refers to the writing of FeignClientsConfiguration class.
@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))); } }