【问题标题】:How to intercept http response in feign and spring boot before ErrorDecoder invoked如何在调用 ErrorDecoder 之前拦截 feign 和 spring boot 中的 http 响应
【发布时间】:2022-11-30 10:24:31
【问题描述】:
我需要拦截 HTTP 响应并更改 HTTP 状态代码。
例如,我可能会将 200 更改为 400(基于响应对象本身)以便调用 ErrorDecoder。
我需要在不扩展 feign.Client 类的情况下执行此操作! (事实上,我使用的 feign.httpclient.ApacheHttpClient 是最终的!)
我试图为 ResponseMapper 提供一个实现,但似乎只有在 AsyncResponseHandler#handleResponse 内部完成 HTTP 状态检查后才会调用响应映射器(以及我用来覆盖的 Decoder)!
我在这里主要谈论SynchronousMethodHandler和AsyncResponseHandler的实现是如何工作的。
把它们加起来,我的主要问题是 AsyncResponseHandler#handleResponse 不会在 ErrorDecoder 之前调用 Decoder(因此调用 ResponseMapper),如果调用了,那么 ResponseMapper 可以执行我需要的 HTTP 状态代码。
【问题讨论】:
标签:
java
spring-boot
feign
【解决方案1】:
为了克服这个缺点,Id在代码中引用实现类(而不是依赖于spring boot的自动配置)
@Bean
public Client feignClient(HttpClient httpClient) {
var client = new ApacheHttpClient(httpClient);
return (request, options) -> convertResponse(client.execute(request, options));
}
private Response convertResponse(Response response) throws IOException {
// ...
}
【解决方案2】:
同步方法处理器使用假装记录器除非日志级别为 NONE,否则在假请求过程之前和之后打印日志。调用顺序如下:
logger.logResuest(configKey, logLevel, request)
response = client.execute(request, options)
-
response = logger.logAndRebufferResponse(configKey, logLevel, response, elapsedTime)
这个记录器是由默认FeignLoggerFactory#create().如果您定义自己的记录器并将其注册为 SringBean,DefaultFeignLoggerFactory应该使用自己的logger来打印日志,在FeignClientsConfigurations可以看到logger是@Autowired(required = false)。
所以你应该定义自己的记录器,例如:
public class MyFeignLogger extends Slf4jLogger {
@Override
protected Response logAndRebufferResponse(String configKey, Logger.Level logLevel, Response response, long elapsedTime) {
Response rsp = super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
//you can change response status here by java reflect.
if(xxxx) {
ReflectUtil.setFieldValue(rsp, "status", 400);
}
return rsp;
}
}
然后将这个类注册为Bean:
@Configuration
@AutoConfiguration(FeignClientsConfiguration.class)
public class MyFeignConfiguration {
@Bean
@Primary
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
@Primary
public Logger feignLogger() {
return new MyFeignLogger();
}
}
有用。