年轻人不讲武德,竟然重构出这么优雅后台 API 接口
Hello,早上好,我是楼下小黑哥~
最近偶然间在看到 Spring 官方文档的时候,新学到一个注解 @ControllerAdvice
,并且成功使用这个注解重构我们项目的对外 API 接口,去除繁琐的重复代码,使其开发更加优雅。
展示具体重构代码之前,我们先来看下原先对外 API 接口是如何开发的。
这个 API 接口主要是用来与我们 APP 交互,这个过程我们统一定义一个交互协议,APP 端与后台 API 接口统一都使用 JSON 格式。
另外后台 API 接口对 APP 返回时,统一一些错误码,APP 端需要根据相应错误码,在页面弹出一些提示。
下面展示一个查询用户信息返回的接口数据:
{"code": "000000","msg": "success","result": {"id": "1","name": "test"}
}
code
代表对外的错误码,msg
代表错误信息,result
代表具体返回信息。
前端 APP 获取这个返回信息,首先判断接口返回 code
是否为 000000,如果是代表查询成功,然后获取 result
信息作出相应的展示。否则,直接弹出相应的错误信息。
欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:studyidea.cn
重构之前
下面我们来看下,重构之前的,后台 API 层的如何编码。
/*** V1 版本** @return*/
@RequestMapping("testv1")
public APIResult testv1() {try {User user = new User();user.setId("1");user.setName("test");return APIResult.success(user);} catch (APPException e) {log.error("内部异常", e);return APIResult.error(e.getCode(), e.getMsg());} catch (Exception e) {log.error("系统异常", e);return APIResult.error(RetCodeEnum.FAILED);}
}
上面的代码其实很简单,内部统一封装了一个工具类 APIResult
,然后用其包装具体的结果。
@Data
public class APIResult<T> implements Serializable {private static final long serialVersionUID = 4747774542107711845L;private String code;private String msg;private T result;public static <T> APIResult success(T result) {APIResult apiResult = new APIResult();apiResult.setResult(result);apiResult.setCode("000000");apiResult.setMsg("success");return apiResult;}public static APIResult error(String code, String msg) {APIResult apiResult = new APIResult();apiResult.setCode(code);apiResult.setMsg(msg);return apiResult;}public static APIResult error(RetCodeEnum codeEnum) {APIResult apiResult = new APIResult();apiResult.setCode(codeEnum.getCode());apiResult.setMsg(codeEnum.getMsg());return apiResult;}
除了这个以外,还定义一个异常对象 APPException
,用来统一包装内部的各种异常。
上面的代码很简单,但是呢可以说比较繁琐,重复代码也比较多,每个接口都需要使用 try...catch
包装,然后使用 APIResult
包括正常的返回信息与错误信息。
第二呢,接口对象只能返回 APIResult
,真实业务对象只能隐藏在 APIResult
中。这样不太优雅,另外不能很直观知道真实业务对象。
重构之后
下面我们开始重构上面的代码,主要目的是去除重复的那一坨try...catch
代码。
这次重构我们需要使用Spring 注解 @ControllerAdvice
以及 ResponseBodyAdvice
,我们先来看下重构的代码。
ps:
ResponseBodyAdvice
来自 Spring 4.2 API,如果各位同学需要使用这个的话,可能需要升级 Spring 版本。
改写返回信息
首先我们需要实现 ResponseBodyAdvice
,实现我们自己的处理类。
@ControllerAdvice
public class CustomResponseAdvice implements ResponseBodyAdvice {/*** 是否需要处理返回结果* @param methodParameter* @param aClass* @return*/@Overridepublic boolean supports(MethodParameter methodParameter, Class aClass) {System.out.println("In supports() method of " + getClass().getSimpleName());return true;}/*** 处理返回结果* @param body* @param methodParameter* @param mediaType* @param aClass* @param serverHttpRequest* @param serverHttpResponse* @return*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {System.out.println("In beforeBodyWrite() method of " + getClass().getSimpleName());if (body instanceof APIResult) {return body;}return APIResult.success(body);}
}
实现上面的接口,我们就可以在 beforeBodyWrite
方法里,修改返回结果了。
上面代码中,只是简单使用 APIResult
包装了返回结果,然后返回。其实我们还可以在此增加一些额外逻辑,比如说如接口返回信息由加密的需求,我们可以在这一层统一加密。
另外,这里判断一下 body 是否 APIResult
类,如果是就直接返回,不做修改。
这么做一来兼容之前的老接口,这是因为默认情况下,我们自己实现的 CustomResponseAdvice
类,将会对所有的 Controller
生效。
如果不做判断,以前的老接返回就会被包装了两层 APIResul
,影响 APP 解析。
除此之外,如果大家担心这个修改对以前的老接口有影响的话,可以使用下面的方式,只对指定的方法生效。
首先自定义一个注解,比如说:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomResponse {
}
然后将其标注在需要改动的方法中,然后我们在 ResponseBodyAdvice#supports
中判断具体方法上有没有自定义注解 CustomResponse
,如果存在,返回 true
,这就代表最后将会修改返回类。如果不存在,则返回 false
,那么就会跟以前流程一样。
/*** 是否需要处理返回结果** @param methodParameter* @param aClass* @return*/
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {System.out.println("In supports() method of " + getClass().getSimpleName());Method method = methodParameter.getMethod();return method.isAnnotationPresent(CustomResponse.class);
}
全局异常处理
上面的代码重构之后,将重复代码抽取了出来,整体的代码就剩下我们的业务逻辑,这样就变得非常简洁优雅。
不过,上面的重构的代码,还是存在问题,主要是异常的处理。
如果上面的业务代码抛出了异常,那么接口将会返回堆栈错误信息,而不是我们定义的错误信息。所以下面我们这个,再次优化一下。
这次我们主要需要使用 @ExceptionHandler
注解,这个注解需要与 @ControllerAdvice
一起使用。
@Slf4j
@ControllerAdvice
public class CustomExceptionHandler {@ExceptionHandler(Exception.class)@ResponseBodypublic APIResult handleException(Exception e) {log.error("系统异常", e);return APIResult.error(RetCodeEnum.FAILED);}@ExceptionHandler(APPException.class)@ResponseBodypublic APIResult handleAPPException(APPException e) {log.error("内部异常", e);return APIResult.error(e.getCode(), e.getMsg());}}
使用这个 @ExceptionHandler
,将会拦截相应的异常,然后将会调用的相应方法处理异常。这里我们就使用 APIResult
包装一些错误信息返回。
总结
我们可以使用 @ControllerAdvice
加 ResponseBodyAdvice
拦截返回结果,统一做出一些修改。这样就可以使用的业务代码非常简洁,优雅。
另外,针对业务代码的中,我们可以使用 @ExceptionHandler
注解,统一做一个全局异常处理,这样就可以无缝的跟 ResponseBodyAdvice
结合。
不过这里需要一点,我们实现的 ResponseBodyAdvice
类,一定需要跟 @ControllerAdvice
配合一起使用哦,至于具体原因,下篇文章小黑哥分析原来的时候,再具体解释哦。敬请期待哦~
欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:studyidea.cn
年轻人不讲武德,竟然重构出这么优雅后台 API 接口相关推荐
- 年轻人不讲武德有多可怕?
1 孩子,走你! ▼ 2 这都是什么阴间燃料 (素材来源网络,侵删) ▼ 3 年轻人不讲武德 (素材来源网络,侵删) ▼ 4 当你偷瞄喜欢的男生 ▼ 5 贝多芬:我入土这么都多年了 (素材来源网络,侵 ...
- cout不明确什么意思_年轻人不讲武德是什么梗和意思 年轻人不讲武德梗出处
太极大师马保国的一句"年轻人不讲武德,偷袭我这个69岁的老人家"最近火了,很多人都在模仿这个句式,用法还挺广泛.那么年轻人不讲武德是什么梗?年轻人不讲武德梗的出处是什么?下面小编带 ...
- 年轻人不讲武德,TDengine边缘侧数据存储方案挑战SQLite
上周,涛思数据与EMQ在线上Meetup上联合发布了工业互联网一体化解决方案,基于TDengine.EMQ X搭建一个集工业数据采集.汇聚.清洗.存储分析以及可视化展示等能力于一体的轻量级边缘计算工业 ...
- 年轻人不讲武德,一起聊聊List集合(一)
文章目录 前言 一.List类图 二.源码剖析 1. ArrayList(此篇详解) 2. LinkedList 3. Vector 4. CopyOnWriteArrayList ~~ 码上福利 ...
- 年轻人不讲武德,一起聊聊List集合(三)
文章目录 前言 一.List类图 二.源码剖析 1. Vector(此篇详解) 2. ArrayList 3. LinkedList 4. CopyOnWriteArrayList ~~ 码上福利 ...
- 再见,米哈游!原神社区防f12控制台调试代码全解(年轻人不讲武德)
前言 刚刚逛原神社区,不经意间按到了 f12 打开了控制台,突然屏幕暗了,发生甚么事了! 我一看,嗷,原来是进到无限 debbuger 调试了,传统审查讲究点到为止,用了 debbuger 这还了得, ...
- 年轻人不讲武德,竟用Python让马老师表演闪电五连鞭!
11月份的头条,是属于马保国的. 一位69岁的老同志,惨遭年轻人偷袭,不讲武德. 看看把老同志欺负的... 要不是马老师讲仁义讲道德,甩手就是一个五连鞭. 哈哈哈,所以本期我们就用Python给马保国 ...
- 年轻人不讲武德,一起聊聊List集合(五)
文章目录 前言 一.List类图 二.集合总结 1. ArrayList与Vector集合区别 2. ArrayList与CopyOnWriteArrayList集合区别 3. 时间复杂度 / 空间复 ...
- 年轻人不讲武德!卢伟冰脱口秀:小米高端之路好自为之
11月26日,Redmi正式发布千元精品Note系列新机,三剑齐发:Note 9 Pro.Note 9和Note 9 4G三款产品. Redmi Note 9 Pro国内首发旗舰规格的一亿像素相机HM ...
- 如何优雅设计 API 接口,实现统一格式返回?
来源:老顾聊技术 前言 在移动互联网,分布式.微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前后端分离方式, (题外话:前后端的工作职责越来越明确,现在的前端都称之为大前端,技术栈以及生态圈都 ...
最新文章
- Unity中创建本地多人游戏完整案例视频教程 Learn To Create A Local Multiplayer Game In Unity
- Python培训常识:Python面试中常被问到的几种设计模式要知道
- Postman(使用指南)
- mysql 5.5.39 安装_CentOS7.2安装mysql5.5.39
- 命令行参数怎么输入_太好用了!谷歌开源的命令行接口工具fire
- 单应矩阵,基本矩阵,本质矩阵
- 给定一个0-1串,请找到一个尽可能长的子串,其中包含的0与1的个数相等。
- mysql ken len_MySQL EXPLAIN
- 关于举办“全国大学生大数据技能竞赛”的通知
- 《DSP using MATLAB》Problem 7.26
- ueditor1.4.3 jsp版在ssh下的配置
- 3.4 tensorflow2实现两总体样本尺度参数的秩检验法——python实战
- macOS Monterey 12.0.1(21A559) 正式版三分区原版黑苹果镜像
- 计算机绘图相切,第九章计算机绘图基础.
- jsp实现文件下载,out = pageContext.pushBody();out.close();不用写到jsp中
- 华为防火墙忘记密码,使用console口更改密码
- Ubuntu 安装QT 教程
- 物联网数据多又杂?好用的数据可视化服务来了
- 切比雪夫不等式证明及应用
- 奇异值分解(SVD)方法求解最小二乘问题
热门文章
- 利用Nginx+Mono+Fastcgi代替IIS对Asp.Net进行反向代理
- [转]Windows Shell 编程 第五章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987939】...
- 基于单片机的超市储物柜设计_毕业设计论-单片机储物柜
- 正则校验 4位数字_推荐收藏,工作中常用的正则表达式总结
- 拓端tecdat|R语言中基于混合数据抽样(MIDAS)回归的HAR-RV模型预测GDP增长
- 拓端tecdat|R语言使用最优聚类簇数k-medoids聚类进行客户细分
- L1-6 字母串 (15 分)
- 数据通信与计算机网络第2版,数据通信与计算机网络(第2版)习题1
- train_test_split按比例划分
- conda: command not found