OpenFeign自定义Decoder,解析全局公共类判断status并返回data属性

需求出现

使用Feign调用服务接口返回公共类,使用时每次都需要判断status与获取data,十分冗余,可以使用一个自定义的解码器Decoder,在调用返回时对实体类进行统一判断status并获取data

331f7824ffba4c3590ad18bc19e6c84b.png

303cc35ff641455d8740e22e06520a31.png

fad1cdecc7c44cc587f6146ae4232a0b.png

最终实现

ce622645a4334aa3b8af4e4862887f24.png

思路与实现代码

自定义一个Decoder实现类,将其设置为ResponseEntityDecoder的decoder,利用SpringDecoder解析response为目标对象,判断状态+返回公共类中的泛型对象

自定义Decoder

public class FeignResponseDecoder implements Decoder {
    private final SpringDecoder springDecoder;
    public FeignResponseDecoder(SpringDecoder springDecoder){
        this.springDecoder = springDecoder;
    }
    @Override
    public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
        Type orginalType = null;
        if(type instanceof ParameterizedType){
            // T是泛型如List<> Map<>,会被封装为ParameterizedTypeImpl
            orginalType = ParameterizedTypeImpl.make(ApiRestResponse.class,new Type[]{type},null);
        }else {
            // T是Class类型
            orginalType = ParameterizedTypeImpl.make(ApiRestResponse.class,new Type[]{type},null);
        }
        ApiRestResponse<?> result = (ApiRestResponse<?>) springDecoder.decode(response,orginalType);
        if(result.getStatus() != ApiRestResponse.getOkCode()){
            throw new DecodeException(response.status(), "接口返回错误:"+response.reason() , response.request());
        }
        return result.getData();
    }
}

配置到容器中

    @Configuration
public class OpenFeignConfig {
    @Bean
    public Decoder feignDecoder(ObjectFactory<HttpMessageConverters> msgConverters,
                                ObjectProvider<HttpMessageConverterCustomizer> customizers) {
        // 自定义feign的解析器
        // OptionalDecoder(response,type) -> ResponseEntityDecoder(response,type) -> 自定义Decoder(response,type)
        FeignResponseDecoder feignResponseDecoder = new FeignResponseDecoder(
                new SpringDecoder(msgConverters,customizers));
        ResponseEntityDecoder responseEntityDecoder = new ResponseEntityDecoder(feignResponseDecoder);
        return new OptionalDecoder(responseEntityDecoder);
    }
}

测试成功

05563ada9569439ea7f2d942dda3ad1c.png

研究过程

Feign的默认响应解码链:OptionalDecoder, ResponseEntityDecoder, SpringDecoder

响应首先来到OptionalDecoder的decode,response是feign提供的响应对象,type是要转为的目标类型(FeignClient定义的接口的返回/接收值)

ParameterizedType继承与Type,表示一个参数化类型,如List<String>Map<Integer, String>,它可用来描述类、接口或方法中的泛型信息,提供了方法来获取原始类型(raw type)和类型参数(type arguments)的信息

7cb149c77c9e44a1bc245ef04446e45a.png

进入ResponseEntityDecoder的decode

97c11719eaaf460da01911587ab45637.png

最后来到的SpringDecoder的decode,在这里将Response对象及解析返回最终的type类型的java对象

f8a0dfe21c354278a83b12a6f2c998bf.png

SpringDecoder的decode大致过程(简单了解)

  1. 判断目标类型合法的
  2. 利用Spring框架的消息转换器messageConverters来执行这个任务,并可以通过自定义器来customizers自定义转换器的行为
  3. 创建一个HttpMessageConverterExtractor对象,最终通过调用该对象的extractData方法将Feign的响应Response对象转化为Java对象

 以上本人的代码实现:替换ResponseEntityDecoder的下一个解码器为我们自定义的Decoder

2de13d55f13743d08b12c53406f0d914.png

利用SpringDecoder解析response解析为type目标类型,需要的参数我们可以模仿ResponseEntityDecoder

首先需要知道ResponseEntityDecoder为了调用SpringDecoder的decode,需要传递的参数为response和ApiRestResponse为原类型的ParameterizedTypeImpl对象

看向ResponseEntityDecoder.decode方法

正常情况下的参数type

T为包含泛型的类(如List,Map)

26a31131d9dd435fa5ae8c0de812b2c8.png

08e02ea56c3e4ee78f7be4e680a431c8.png

 T为普通类(UserVo)

77912e6c3f79476d8cd5e35263006e90.png

122338a290ed42e0821d66a563fe81d6.png

 查看完毕

不使用ApiRestResponse的情况下的type

c79130bac86449479ea853dc9cfbcee3.png

 36ba88f6897d441682cb20d1831fb223.png

将type封装为上面有ApiRestResponse的类即可在我们自定义的Decoder中调用SpringDecoder的功能

Feign是一个声明式的Web服务客户端,它使得编写HTTP客户端变得更加简单。通过OpenFeign可以轻松地创建一个HTTP API客户端,且可以在运行时动态生成代理对象来发送请求。 对于**OpenFeign自定义Client**来说,主要有以下几个方面: ### 自定义配置 你可以为特定的服务名指定单独的`@FeignClient`注解,在这个注解里面添加configuration属性指向你所定制化的配置类。例如设置日志级别、超时时间等。 ```java @FeignClient(name = "service-name", configuration = CustomConfig.class) public interface MyServiceClient { } ``` 其中CustomConfig可能是这样的形式: ```java @Configuration public class CustomConfig { @Bean public Logger.Level feignLogLevel() { return Logger.Level.FULL; } // 更多bean... } ``` ### 拦截器与处理器 为了增强功能,比如统一处理header信息或者token鉴权等问题,可以通过实现RequestInterceptor接口来自定义拦截器;另外还可以注册Decoder或Encoder Bean用于自定义数据编码解码规则。 #### 示例 - Header Interceptor ```java @Component public class HeaderInterceptor implements RequestInterceptor { private final String token; public HeaderInterceptor(@Value("${your.token}") String token) { this.token = token; } @Override public void apply(RequestTemplate template) { if (this.token != null && !"".equals(this.token)) { template.header("Authorization", "Bearer " + this.token); } } } ``` 最后不要忘了将你的自定义组件扫描到spring上下文中以便于它们能被自动装配进feign client实例当中去。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菠萝追雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值