Feign返回值统一处理
背景
服务端的接口一般有固定的返回格式,有数据、返回码和异常时错误信息。结构如下
@Data
public class BaseResponse<T> {private String code;private String message;private T data;public boolean isSuccess() {return "SUCCESS".equals(code);}
}
正常情况下我们只关注里面的data字段。不做任何处理情况下,需要将BaseResponse类型作为Feign Client方法的返回值,然后在调用Feign的业务代码处手动调用getData()方法来获取数据。这种重复的代码可以抽出来统一处理(请求数据也类似)。
解决方案
使用自定义Decoder来统一处理,重写Object decode(Response response, Type type)方法,其中Response 就是被调用接口返回的响应,Type就是Feign Client方法的返回值,它的实际类型有四种基本情况(其他都是这四种的排列组合),一种是不带泛型的类,一种带固定泛型的类,一种是不固定的T类型的泛型,最后一种是带?的分别如下




本文只讨论前面两种类型,后面两种类型实际传到
Decode中是没法知道实际类型的,除非通过某种方法把返回的实际类型传到Decode中(比如ThreadLocal、方法参数、请求头等等),或者泛型是有上界的,如<T ? extends UpUser>,那么可以通过type的getBounds()方法获取到上界类型,进行序列化。否则无法确定确定类型的,进行反序列化。
现在就是要将Response中的返回值转换成BaseResponse类型,而且是包括BaseResponse里面T这个泛型的,如果T中还带了泛型,不论嵌套几层都需要转换好,这样调用地方可以直接使用。我使用的序列化工具是Gson(ObjectMapper也是类似的)。
带泛型的转换其实是有现有的方法可以直接转的,但是这里有点难处理的是,将BaseResponse类型和参数中的Type合并成一个,作为参数传到Gson的fromJson方法中,查看Type类的实现类,发现有一个ParameterizedType接口,这个就是描述了对象的参数类型。每个方法说明如下
public interface ParameterizedType extends Type {/*** 返回里面的泛型,比如List<String>, 那么这个方法返回String,如果是Map<String, Integer>那么这个方法返回{String, Integer}的数组* @since 1.5*/Type[] getActualTypeArguments();/*** 返回当前这个类的类型,比如List<String>, 那么这个方法返回List,如果是Map<String, Integer>那么这个方法返回Map*/Type getRawType();/*** 如果是内部类的情况,这个方法返回的是最外层的类,也就是封闭类,比如O<T>.I<S>这种类型,返回的是O<T>*/Type getOwnerType();
}
要注意一点,Class对象也是实现了Type接口的。
ParameterizedType接口解决了参数合并的问题,自定一个参数类型类,实现这三个方法
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;public class MyParameterizedType implements ParameterizedType {private Type type;/*** 将Feign Client方法的返回值注入,只要两种类型,一种是ParameterizedTypeImpl,另一种是具体的Class对象*/public MyParameterizedType(Type type) {this.type = type;}/*** 属性Type就是BaseResponse的泛型类型,直接返回type就可以*/@Overridepublic Type[] getActualTypeArguments() {Type[] types = new Type[1];types[0] = type;return types;}/*** 最外层的类型就是我们要与type合并的BaseResponse类型*/@Overridepublic Type getRawType() {return BaseResponse.class;}/*** 这个Owner一般没用到,如果type是个内部类静态类情况下,需要返回最外部的类型,这里直接调用Class对象获取封闭类的方法*/@Overridepublic Type getOwnerType() {if (type instanceof ParameterizedTypeImpl) {ParameterizedTypeImpl typeImpl = (ParameterizedTypeImpl) type; return typeImpl.getRawType().getEnclosingClass();}if (type instanceof Class) {return ((Class) type).getEnclosingClass();}return null;}
}
这样序列化问题就能解决了,现在只要编写Decoder类就可以了。
import com.google.gson.Gson;
import feign.FeignException;
import feign.Response;
import feign.codec.Decoder;import java.io.IOException;
import java.lang.reflect.Type;public class MyDecode implements Decoder {private Gson gson = new Gson();@Overridepublic Object decode(Response response, Type type) throws FeignException, IOException {MyParameterizedType myType = new MyParameterizedType(type);BaseResponse baseResponse = gson.fromJson(response.body().asReader(), myType);if (type instanceof BaseResponse) {return baseResponse;}if (baseResponse.isSuccess()) {return baseResponse.getData();}throw new RuntimeException("返回异常");}
}
这里加了一个BaseResponse判断,如果需要返回整个数据,比如根据BaseResponse的返回码做业务逻辑,就可以在Feign Client的方法返回值直接写带泛型的BaseResponse类型。也加了一个统一的校验,如果要获取数据,需要返回码是正常才行。
总结
这种写法优点就是一次性反序列化到位,后续使用根据泛型里面的类型直接使用,如果不进行泛型合并,只转成BaseResponse类型,如果data的类型是有很多泛型嵌套的,那么可能反序列化类型是有问题的,比如data的类型是List<User>,那么不指定详细的泛型类型,直接转成BaseResponse类型,那么data字段序列化结果会是List<Map<String, String>,没法直接使用的。
关于参数化合并问题,这种思路可以借鉴,运用到其他场景。还有像请求数据统一封装其实也是类似,自定义一个Encoder即可,请求就没有参数泛型的问题了。
相关文章:
Feign返回值统一处理
背景 服务端的接口一般有固定的返回格式,有数据、返回码和异常时错误信息。结构如下 Data public class BaseResponse<T> {private String code;private String message;private T data;public boolean isSuccess() {return "SUCCESS".equals(cod…...
探究如何在Linux系统中修改进程资源限制:四种方法调整进程限制,让你的系统高效运行(包含应用层getrlimit和setrlimit API)
探究如何在Linux系统中修改进程资源限制1.进程资源限制的概念2.修改进程资源限制的意义与应用场景1.软限制与硬限制2.常见资源限制类型Linux中的资源限制1.ulimit命令a. 语法及选项b. 示例与应用2./etc/security/limits.conf配置文件a. 配置文件结构b.示例与应用3. 使用cgroups…...
9.5. 机器翻译与数据集
笔记 9.5. 机器翻译与数据集 — 动手学深度学习 2.0.0 documentation 1.下载文件 读文件 2.处理数据 在所有标点符号前面加空格 后面用于分割 因为法语英语可能有半角全角的字符区分用utf编码的方式统一成半角字符的空格 3.因为分隔用的是空格split 所有vocab是没有空格的 …...
跟着凯新生物2 Arm PEG Biotin,2-Branched PEG Biotin,生物素-聚乙二醇-二臂/支,学试剂知识
中英文名:2 Arm/Branched PEG Biotin,2 ArmPEG Biotin,二臂/支 PEG 生物素一、Product specifications: 1.CAS No:N/A 2.Packaging specification:10mg,25mg,50mg, flexible packagi…...
react组件进阶(四)
文章目录1. 组件通讯介绍2. 组件的 props3. 组件通讯的三种方式3.1 父组件传递数据给子组件3.2 子组件传递数据给父组件3.3 兄弟组件4. Context5. props 深入5.1 children 属性5.2 props 校验5.3 props 的默认值6. 组件的生命周期6.1 组件的生命周期概述6.2 生命周期的三个阶段…...
阿维塔城区NCA智驾导航辅助,复杂路口,全面胜任
阿维塔11城区NCA智驾导航辅助将于3月在上海、深圳等城市分阶段开启体验,以看得清、判得准、控得稳的“智驾”,进一步巩固业界智能天花板的地位。智能驾驶里程碑,拨杆两下开启都市安适旅程作为AVATRANS智能领航系统的重要组成部分,…...
[Pandas] div()函数
div()方法将DataFrame中的每个值除以指定的值,并返回一个计算处理后的Dataframe结果 DataFrame.div()函数其实是除法运算,表格中的每个数据都是被除数 导入数据 import pandas as pd df pd.DataFrame({"col1":[5, 3, None, 4], "col2…...
c++并发与多线程
c并发与多线程 子线程结束,主线程不能结束,否则会出错,和java不一样。 可以用join的方式让主线程等待子线程执行结束。 quickStart 线程相关头文件 #include <thread> 使用全局函数构造一个线程对象 #include <iostream> #…...
Vinylsulfone PEG Biotin,Biotin-PEG-VS,生物素聚乙二醇乙烯砜,VS基团容易与游离巯基发生反应
●中文名:乙烯砜PEG生物素,生物素聚乙二醇乙烯砜 ●英文名:Vinylsulfone PEG Biotin, VS-PEG-Biotin,Vinyl sulfone-PEG-Biotin,Biotins-PEG-sulfone Vinyl●产品理化指标: CAS号:N/A 分子量&am…...
论文学习——Tune-A-Video
Tune-A-Video: One-Shot Tuning of Image Diffusion Models for Text-to-Video Generation Abstract 本文提出了一种方法,站在巨人的肩膀上——在大规模图像数据集上pretrain并表现良好的 text to image 生成模型——加入新结构并进行微调,训练出一套 …...
C++类与对象part1
目录 1.类的6个默认函数 2.构造函数(相当于init) 3.析构函数 (相当于destroy) 4.拷贝构造函数 赋值运算符重载 运算符重载 赋值运算符重载 引入: 你知道为什么cout可以自动识别类型吗? 其实cout是一…...
记一次抓取网页内容
已打码 // UserScript // name --------- // namespace http://tampermonkey.net/ // version 0.1 // description https://---------oups/{id}/topics?scopeall&count20&begin_time2022-09-01T00%3A00%3A00.000%2B0800&end_time2022-10-01T00%…...
parasoft帮助史密斯医疗通过测试驱动开发提供安全、高质量的医疗设备
parasoft是一家专门提供软件测试解决方案的公司,Parasoft通过其经过市场验证的自动化软件测试工具集成套件,帮助企业持续交付高质量的软件。Parasoft的技术支持嵌入式、企业和物联网市场,通过将静态代码分析和单元测试、Web UI和API测试等所有…...
SpringBoot整合Oauth2开放平台接口授权案例
<!-- SpringBoot整合Web组件 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId>&l…...
Linux_创建用户
创建一个名为hello的用户,并指定/home/hello为根目录useradd -d /home/hello -m hello 设置密码 ,密码会输入两次,一次设置密码,一次确认密码,两次密码要输入的一样passwd hellouseradd的常用参数含义-d指定用户登入时的主目录&am…...
RDD(弹性分布式数据集)总结
文章目录一、设计背景二、RDD概念三、RDD特性四、RDD之间的依赖关系五、阶段的划分六、RDD运行过程七、RDD的实现一、设计背景 1.某些应用场景中,不同计算阶段之间会重用中间结果,即一个阶段的输出结果会作为下一个阶段的输入。如:迭代式算法…...
服务器版RstudioServer安装与配置详细教程
Docker部署Rstudio server 背景:如果您想在服务器上运行RstudioServer,可以按照如下方法进行操作,笔者测试时使用腾讯云服务器(系统centos7),需要在管理员权限下运行 Rstudio 官方提供了使用不同 R 版本的 …...
如何在Java中将一个列表拆分为多个较小的列表
在Java中,有多种方法可以将一个列表拆分为多个较小的列表。在本文中,我们将介绍三种不同的方法来实现这一目标。 方法一:使用List.subList()方法 List接口提供了一个subList()方法,它可以用来获取列表中的一部分元素。我们可以使…...
TryHackMe-Inferno(boot2root)
Inferno 现实生活中的机器CTF。该机器被设计为现实生活(也许不是?),非常适合刚开始渗透测试的新手 “在我们人生旅程的中途,我发现自己身处一片黑暗的森林中,因为直截了当的道路已经迷失了。我啊…...
微信原生开发中 JSON配置文件的作用 小程序中有几种JSON配制文件
关于json json是一种数据格式,在实际开发中,JSON总是以配制文件的形式出现,小程序与不例外,可对项目进行不同级别的配制。Q:小程序中有几种配制文件A:小程序中有四种配制文件分别是:project.config.json si…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
