feign interceptor and decoder

Posted by chintupintu03 on Thu, 20 Jan 2022 06:27:33 +0100

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)));
	}
}

Topics: Java Spring Spring Cloud