当前位置: 首页 > news >正文

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>,那么可以通过typegetBounds()方法获取到上界类型,进行序列化。否则无法确定确定类型的,进行反序列化。

现在就是要将Response中的返回值转换成BaseResponse类型,而且是包括BaseResponse里面T这个泛型的,如果T中还带了泛型,不论嵌套几层都需要转换好,这样调用地方可以直接使用。我使用的序列化工具是Gson(ObjectMapper也是类似的)。

带泛型的转换其实是有现有的方法可以直接转的,但是这里有点难处理的是,将BaseResponse类型和参数中的Type合并成一个,作为参数传到GsonfromJson方法中,查看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返回值统一处理

背景 服务端的接口一般有固定的返回格式&#xff0c;有数据、返回码和异常时错误信息。结构如下 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,生物素-聚乙二醇-二臂/支,学试剂知识

中英文名&#xff1a;2 Arm/Branched PEG Biotin&#xff0c;2 ArmPEG Biotin&#xff0c;二臂/支 PEG 生物素一、Product specifications&#xff1a; 1.CAS No&#xff1a;N/A 2.Packaging specification&#xff1a;10mg&#xff0c;25mg&#xff0c;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月在上海、深圳等城市分阶段开启体验&#xff0c;以看得清、判得准、控得稳的“智驾”&#xff0c;进一步巩固业界智能天花板的地位。智能驾驶里程碑&#xff0c;拨杆两下开启都市安适旅程作为AVATRANS智能领航系统的重要组成部分&#xff0c;…...

[Pandas] div()函数

div()方法将DataFrame中的每个值除以指定的值&#xff0c;并返回一个计算处理后的Dataframe结果 DataFrame.div()函数其实是除法运算&#xff0c;表格中的每个数据都是被除数 导入数据 import pandas as pd df pd.DataFrame({"col1":[5, 3, None, 4], "col2…...

c++并发与多线程

c并发与多线程 子线程结束&#xff0c;主线程不能结束&#xff0c;否则会出错&#xff0c;和java不一样。 可以用join的方式让主线程等待子线程执行结束。 quickStart 线程相关头文件 #include <thread> 使用全局函数构造一个线程对象 #include <iostream> #…...

Vinylsulfone PEG Biotin,Biotin-PEG-VS,生物素聚乙二醇乙烯砜,VS基团容易与游离巯基发生反应

●中文名&#xff1a;乙烯砜PEG生物素&#xff0c;生物素聚乙二醇乙烯砜 ●英文名&#xff1a;Vinylsulfone PEG Biotin, VS-PEG-Biotin&#xff0c;Vinyl sulfone-PEG-Biotin&#xff0c;Biotins-PEG-sulfone Vinyl●产品理化指标&#xff1a; CAS号&#xff1a;N/A 分子量&am…...

论文学习——Tune-A-Video

Tune-A-Video: One-Shot Tuning of Image Diffusion Models for Text-to-Video Generation Abstract 本文提出了一种方法&#xff0c;站在巨人的肩膀上——在大规模图像数据集上pretrain并表现良好的 text to image 生成模型——加入新结构并进行微调&#xff0c;训练出一套 …...

C++类与对象part1

目录 1.类的6个默认函数 2.构造函数&#xff08;相当于init&#xff09; 3.析构函数 &#xff08;相当于destroy&#xff09; 4.拷贝构造函数 赋值运算符重载 运算符重载 赋值运算符重载 引入&#xff1a; 你知道为什么cout可以自动识别类型吗&#xff1f; 其实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是一家专门提供软件测试解决方案的公司&#xff0c;Parasoft通过其经过市场验证的自动化软件测试工具集成套件&#xff0c;帮助企业持续交付高质量的软件。Parasoft的技术支持嵌入式、企业和物联网市场&#xff0c;通过将静态代码分析和单元测试、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的用户&#xff0c;并指定/home/hello为根目录useradd -d /home/hello -m hello 设置密码 ,密码会输入两次&#xff0c;一次设置密码&#xff0c;一次确认密码&#xff0c;两次密码要输入的一样passwd hellouseradd的常用参数含义-d指定用户登入时的主目录&am…...

RDD(弹性分布式数据集)总结

文章目录一、设计背景二、RDD概念三、RDD特性四、RDD之间的依赖关系五、阶段的划分六、RDD运行过程七、RDD的实现一、设计背景 1.某些应用场景中&#xff0c;不同计算阶段之间会重用中间结果&#xff0c;即一个阶段的输出结果会作为下一个阶段的输入。如&#xff1a;迭代式算法…...

服务器版RstudioServer安装与配置详细教程

Docker部署Rstudio server 背景&#xff1a;如果您想在服务器上运行RstudioServer&#xff0c;可以按照如下方法进行操作&#xff0c;笔者测试时使用腾讯云服务器&#xff08;系统centos7&#xff09;&#xff0c;需要在管理员权限下运行 Rstudio 官方提供了使用不同 R 版本的 …...

如何在Java中将一个列表拆分为多个较小的列表

在Java中&#xff0c;有多种方法可以将一个列表拆分为多个较小的列表。在本文中&#xff0c;我们将介绍三种不同的方法来实现这一目标。 方法一&#xff1a;使用List.subList()方法 List接口提供了一个subList()方法&#xff0c;它可以用来获取列表中的一部分元素。我们可以使…...

TryHackMe-Inferno(boot2root)

Inferno 现实生活中的机器CTF。该机器被设计为现实生活&#xff08;也许不是&#xff1f;&#xff09;&#xff0c;非常适合刚开始渗透测试的新手 “在我们人生旅程的中途&#xff0c;我发现自己身处一片黑暗的森林中&#xff0c;因为直截了当的道路已经迷失了。我啊&#xf…...

微信原生开发中 JSON配置文件的作用 小程序中有几种JSON配制文件

关于json json是一种数据格式&#xff0c;在实际开发中&#xff0c;JSON总是以配制文件的形式出现&#xff0c;小程序与不例外&#xff0c;可对项目进行不同级别的配制。Q&#xff1a;小程序中有几种配制文件A:小程序中有四种配制文件分别是&#xff1a;project.config.json si…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...