SpringBoot定义优雅全局统一Restful API 响应框架六
闲话不多说,继续优化 全局统一Restful API 响应框架 做到项目通用 接口可扩展。
如果没有看前面几篇文章请先看前面几篇
SpringBoot定义优雅全局统一Restful API 响应框架
SpringBoot定义优雅全局统一Restful API 响应框架二
SpringBoot定义优雅全局统一Restful API 响应框架三
SpringBoot定义优雅全局统一Restful API 响应框架四
SpringBoot定义优雅全局统一Restful API 响应框架五
这里讲一讲最后的版本和需要修复的一些问题
@PostMapping("/add/UserApiCombo")
public R addApiCombo(@RequestBody @Validated UserApplyApiComboDto userApplyApiComboDto) {
userApiComboService.addApiCombo(userApplyApiComboDto);
return R.success();
}
我们看看这个代码,有什么问题。 我们返回了统一的封装结果集R 但是后面所有的controller 都这么写不太友好。
- 返回内容这么不够明确具体
- 所有
controller这么写增加重复工作量
我们可以这么去优化:
Spirng 提供了 ResponseBodyAdvice 接口,支持在消息转换器执行转换之前,对接口的返回结果进行处理,再结合 @ControllerAdvice 注解即可轻松支持上述功能
package cn.soboys.springbootrestfulapi.common.handler;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.soboys.springbootrestfulapi.common.error.ErrorDetail;
import cn.soboys.springbootrestfulapi.common.resp.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/6/12 12:17 下午
* @webSite https://github.com/coder-amiao
* @Slf4j
* @ControllerAdvice
*/
@Slf4j
@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
/**
* supports方法: 判断是否要执行beforeBodyWrite方法,
* true为执行,false不执行.
* 通过该方法可以选择哪些类或那些方法的response要进行处理, 其他的不进行处理.
*
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
/**
* beforeBodyWrite方法: 对response方法进行具体操作处理
* 实际返回结果业务包装处理
*
* @param body
* @param returnType
* @param selectedContentType
* @param selectedConverterType
* @param request
* @param response
* @return
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof R) {
return body;
} else if (body == null) {
return R.success();
} else if (body instanceof ErrorDetail) {
return body;
} else if (body instanceof String) {
return body;
} else {
return R.success().data(body);
}
}
}
在实际controller 返回中我们直接返回数据内容就可以了
@GetMapping("/home")
public Student home() {
Student s = new Student();
s.setUserName("Tom");
s.setAge(22);
List hobby = new ArrayList();
hobby.add("抽烟");
hobby.add("喝酒");
hobby.add("烫头");
s.setHobby(hobby);
s.setBalance(2229891.0892);
s.setIdCard("420222199811207237");
return s;
}
我们目前版本中业务错误判断逻辑不是很友好,还需要优化,这里我们可以封装自己的业务异常
用 Assert(断言) 封装异常,让代码更优雅
符合 错误优先返回原则
正常我们业务异常代码是这样写的
// 另一种写法
Order order = orderDao.selectById(orderId);
if (order == null) {
throw new IllegalArgumentException("订单不存在。");
}
使用断言优化后
Order order = orderDao.selectById(orderId);
Assert.notNull(order, "订单不存在。");
两种方式一对比,是不是明显感觉第一种更优雅,第二种写法则是相对丑陋的 if {...} 代码块。那么 神奇的 Assert.notNull() 背后到底做了什么呢?
这里就是我们需要优化代码
其实很多框架都带有Assert 工具包括JAVA JDK . SpringBoot,spring 也有自己的Assert
但是不符合我们自己的异常抛出业务逻辑,这里我们可以自定义自定的Assert 工具
我们来看一下部分源码
public abstract class Assert {
public Assert() {
}
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
}
可以看到,Assert 其实就是帮我们把 if {...} 封装了一下,是不是很神奇。虽然很简单,但不可否认的是编码体验至少提升了一个档次。
那么我们是不是可以模仿Assert也写一个自定义断言类,不过断言失败后抛出的异常不是IllegalArgumentException 这些内置异常,而是我们自己定义的异常。
- 定义公共异常
package cn.soboys.springbootrestfulapi.common.exception;
import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
import lombok.Data;
/**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/6/12 10:32 下午
* @webSite https://github.com/coder-amiao
*/
@Data
public class BaseException extends RuntimeException {
/**
* 返回码
*/
protected ResultCode resultCode;
/**
* 异常消息参数
*/
protected Object[] args;
public BaseException(ResultCode resultCode) {
super(resultCode.getMessage());
this.resultCode = resultCode;
}
public BaseException(String code, String msg) {
super(msg);
this.resultCode = new ResultCode() {
@Override
public String getCode() {
return code;
}
@Override
public String getMessage() {
return msg;
}
@Override
public boolean getSuccess() {
return false;
}
;
};
}
public BaseException(ResultCode resultCode, Object[] args, String message) {
super(message);
this.resultCode = resultCode;
this.args = args;
}
public BaseException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
super(message, cause);
this.resultCode = resultCode;
this.args = args;
}
}
- 所有其他异常继承公共异常
package cn.soboys.springbootrestfulapi.common.exception;
import cn.soboys.springbootrestfulapi.common.resp.ResultCode;
/**
* @author 公众号 程序员三时
* @version 1.0
* @date 2023/4/29 00:15
* @webSite https://github.com/coder-amiao
* 通用业务异常封装
*/
public class BusinessException extends BaseException {
public BusinessException(ResultCode resultCode, Object[] args, String message) {
super(resultCode, args, message);
}
public BusinessException(ResultCode resultCode, Object[] args, String message, Throwable cause) {
super(resultCode, args, message, cause);
}
}
- 断言业务异常类封装
public interface Assert {
/**
* 创建异常
* @param args
* @return
*/
BaseException newException(Object... args);
/**
* 创建异常
* @param t
* @param args
* @return
*/
BaseException newException(Throwable t, Object... args);
/**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
*
* @param obj 待判断对象
*/
default void assertNotNull(Object obj) {
if (obj == null) {
throw newException(obj);
}
}
/**
* <p>断言对象<code>obj</code>非空。如果对象<code>obj</code>为空,则抛出异常
* <p>异常信息<code>message</code>支持传递参数方式,避免在判断之前进行字符串拼接操作
*
* @param obj 待判断对象
* @param args message占位符对应的参数列表
*/
default void assertNotNull(Object obj, Object... args) {
if (obj == null) {
throw newException(args);
}
}
}
具体使用
/**
* 异常返回模拟
*
* @return
*/
@GetMapping("/exception")
public Student exception() {
Student s = null;
BusinessErrorCode.Sign_Error.assertNotNull(s,"secret秘钥不正确");
return s;
}
在业务中我们可以通过这个方式直接抛出枚举异常。这样代码就简洁干净很多
代理已经更新到 github仓库脚手架项目
关注公众号,程序员三时 持续输出优质内容 希望给你带来一点启发和帮助
SpringBoot定义优雅全局统一Restful API 响应框架六的更多相关文章
- Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!
前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...
- Java Fluent Restful API自动化测试框架
这是一个Restful API自动化测试框架,这是一个能让你写出高可读性测试代码的测试框架! 项目目标 话说目前行业内,Restful API自动化测试框架已经不是稀罕物了,各个语言都有自己的实现机制 ...
- 只需一步,在Spring Boot中统一Restful API返回值格式与统一处理异常
## 统一返回值 在前后端分离大行其道的今天,有一个统一的返回值格式不仅能使我们的接口看起来更漂亮,而且还可以使前端可以统一处理很多东西,避免很多问题的产生. 比较通用的返回值格式如下: ```jav ...
- Slim - 超轻量级PHP Restful API构建框架
下载源码包: http://www.slimframework.com/ 基于Slim的Restful API Sample: <?php require '/darjuan/Slim/Slim ...
- springboot集成swagger2构建RESTful API文档
在开发过程中,有时候我们需要不停的测试接口,自测,或者交由测试测试接口,我们需要构建一个文档,都是单独写,太麻烦了,现在使用springboot集成swagger2来构建RESTful API文档,可 ...
- 【从0到1,搭建Spring Boot+RESTful API+Shiro+Mybatis+SQLServer权限系统】03、创建RESTful API,并统一处理返回值
本节应用Spring对RESTful的支持,使用了如@RestController等注解实现RESTful控制器. 如果对Spring中的RESTful不太明白,请查看相关书籍 1.创建一个数据对象, ...
- restful api 错误
简介 随着移动开发和前端开发的崛起,越来越多的 Web 后端应用都倾向于实现 Restful API.Restful API 是一个简单易用的前后端分离方案,它只需要对客户端请求进行处理,然后返回结果 ...
- Restful API 中的错误处理
简介 随着移动开发和前端开发的崛起,越来越多的 Web 后端应用都倾向于实现 Restful API. Restful API 是一个简单易用的前后端分离方案,它只需要对客户端请求进行处理,然后返回结 ...
- 探讨Morest在RESTful API测试的行业实践
摘要:在本文中,我们将重点探讨使用自动化智能化Morest测试技术在RESTful API测试的行业实践. 本文分享自华为云社区<[智能化测试专题]华为云API智能测试工具--Morest测试框 ...
- MongoDB最简单的入门教程之五-通过Restful API访问MongoDB
通过前面四篇的学习,我们已经在本地安装了一个MongoDB数据库,并且通过一个简单的Spring boot应用的单元测试,插入了几条记录到MongoDB中,并通过MongoDB Compass查看到了 ...
随机推荐
- 有执行语句:console.log(fn2(2)[3]),补充函数,使执行结果为"hello"
function fn2(a){ return [1,2,3,"hello"];}console.log(fn2(2)[3])//hello 这个2是混淆视线的,即使没有这个2.函 ...
- 【LeetCode回溯算法#extra01】集合划分问题【火柴拼正方形、划分k个相等子集、公平发饼干】
火柴拼正方形 https://leetcode.cn/problems/matchsticks-to-square/ 你将得到一个整数数组 matchsticks ,其中 matchsticks[i] ...
- python对图片进行最大边大小缩放
def split_image_bs4(file, max_len=720): """ 切割图片 :param file: 二进制文件 :param max_len: 最 ...
- Redis(八)redis新功能
redis6新功能 1 ACL Redis ACL是AccessControlList(访问控制表)的缩写,该功能允许根据可以执行的命令和访问的键来限制某些连接. 2 基本命令 acl help &g ...
- 帝国cms 随机调取新闻
<?php $hits_r = $empire->query("select * from {$dbtbpre}ecms_music as t1 join (select rou ...
- Go语言实现基于TCP的内存缓存服务
接上文: https://www.cnblogs.com/N3ptune/p/16623738.html HTTP/REST的解析导致基于HTTP的内存缓存服务性能不佳,本次实现一个基于TCP的缓存服 ...
- 2023成都.NET线下技术沙龙圆满结束
2023年4月15日周六,由MASA技术团队和成都.NET俱乐部共同主办的2023年成都.NET线下技术沙龙活动在成都市世纪城新会展中心知域空间举行,共计报名人数90多人,实际到场60多人,13:30 ...
- 谈谈selenium中的clear后输入内容异常的处理
谈谈selenium中的clear后输入内容异常的处理 案例 在线考试项目的登录:http://124.223.31.21:9097/#/ 代码 from selenium import webdri ...
- 【深入浅出Spring原理及实战】「源码调试分析」深入源码探索Spring底层框架的的refresh方法所出现的问题和异常
学习Spring源码的建议 阅读Spring官方文档,了解Spring框架的基本概念和使用方法. 下载Spring源码,可以从官网或者GitHub上获取. 阅读Spring源码的入口类,了解Sprin ...
- mysql大表修改工具: pt-online-schame-change
在表数据量很大的时候直接添加字段,以及其他表结构修改,会严重影响线上使用,而且耗费时间很长:使用这个工具可以很好的在线修改表结构. 好处: 降低主从延时的风险 可以限速.限资源,避免操作时MySQL负 ...