简介

当使用SpringBoot开发Web项目的API时,为了与前端更好地通信,通常会约定好接口的响应格式。例如,以下是一个JSON格式的响应,通过返回码和返回信息告知前端具体的操作结果或错误信息。如果操作成功,前端可以通过"data"字段获取响应内容。

{
"code":"000000",
"message":"操作成功",
"data": true
}

如果所有接口都这样一个一个的封,那开发人员估计就先一个一个的疯了,为了减少手动的处理过程,让开发人员可以专注于业务本身,本文将向你展示一种非常优雅的方式。

除了统一处理接口的响应内容以外,在一般的业务流程中,不管是自定义的异常还是因为各种问题而抛出的异常,在前端接收到的接口响应状态都是500错误,接口响应的内容上也非常不友好,如下图。



而实际的业务中,我们更希望可以给用户一个相对友好的提示,之前展示了一个成功的响应,这里我们也可以将异常情况处理为同样的结构,例如:

{
"code":"010101",
"message":"订单当前不支持此操作",
"data": ""
}

这样也就可以让前端通过特定的方式将异常信息以更友好的方式展示给用户,而不是干巴巴的代码,接下来我们废话不多说,开干!!!

前期准备

首先我们需要准备一个示例工程用于实操演示,这里给大家准备了一个项目并开源到了Gitee,大家可以执行以下命令获取当前需要使用的示例项目,就是最基础的SpringBoot项目,大家也可以自己初始化一个,这个项目后续我会继续往里添加功能,大家可以关注下,说不定什么时候就可以用上了,也欢迎大家通过各种方式与我沟通相关问题。

git clone https://gitee.com/itartisans/itartisans-framework.git
git checkout base-springboot-web

统一封装报文

为了统一处理业务代码返回的逻辑,我们需要准备一个实体类,核心代码就是以下属性,分别对应着开头的报文各个属性,而其他的getter和setter方法则因为篇幅原因省略了。

public class Result {
private String code;
private String message;
private Object data;
// todo 添加的省略getter和setter方法
}

报文的统一处理需要依赖Spring提供的AOP支持,Spring官方针对响应内容的处理提供了ResponseBodyAdvice接口,对响应报文的封装则需要实现此接口。

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> { @Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
} @Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
Result result = new Result();
result.setCode("000000");
result.setMessage("操作成功");
result.setData(body);
return result;
}
}

说明:

  • 第一行的@ControllerAdvice声明此类是一个切片处理类;
  • supports方法用于标识切面的覆盖范围,如果返回true则会执行beforeBodyWrite方法里的代码,如果返回false则直接响应;
  • beforeBodyWrite方法用于对响应值进行自定义操作,此处通过Result进行封装;

    完成以上处理后,我们可以创建个Controller请求下看看返回结果,如果示例代码是返回的不是String类型的话,那么应该是可以正常响应的,但是如果你尝试的是String,你就会收到这么一个异常:
class org.itartisans.framework.model.entity.Result cannot be cast to class java.lang.String

收到这个异常的原因是Spring默认使用的序列化器是StringHttpMessageConverter,这个序列化器无法正常的将对象转换为String类型,需要手动指定使用MappingJackson2HttpMessageConverter进行处理,而指定序列化处理器则需要实现WebMvcConfigurer并通过重写configureMessageConverters方法实现,具体代码如下:

@Configuration
public class WebConfiguration implements WebMvcConfigurer { @Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new MappingJackson2HttpMessageConverter());
}
}

完成序列化处理器的配置后即可愉快的测试所有类型的返回值了。

统一异常处理

在完成以上的报文封装后,如果代码执行过程中出现了异常,则会出现以下内容,此处是手动throw异常模拟的情况。

{
"code": "000000",
"message": "操作成功",
"data": {
"timestamp": 1691503634161,
"status": 500,
"error": "Internal Server Error",
"path": "/demo/getObject"
}
}

报文即使在失败时也显示了操作成功,这明显不是正常的情况,也不符合我们最初可以定制错误码和错误信息的需求,统一处理异常同样需要使用AOP,创建一个针对异常的切面处理器,代码如下:

@RestControllerAdvice
public class ExceptionAdvice { @ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
Result result = new Result();
result.setCode("999999");
result.setMessage("操作失败");
return result;
} }

这部分代码和封装报文时有共同指出,这里介绍下不一样的地方:

  • 第一行的@RestControllerAdvice与@RestController类似,表示此类中的方法返回值均为JSON类型
  • 第四行的@ExceptionHandler指定该方法时一个异常处理器,参数对应处理的异常,这里指定的Exception.class表示处理所有异常

经过切面处理后报文如下,可以返现data中的返回内容正是我们想要的响应报文。

{
"code": "000000",
"message": "操作成功",
"data": {
"code": "999999",
"message": "操作失败",
"data": null
}
}

这里则是因为上一步统一封装报文时我们没有对方法的返回值进行区分,一刀切的对所有返回值进行了封装,结果出现了这种情况。既然知道了问题所在,那就好解决了,在封装报文时我们知道ResponseAdvice中的supports方法是用于判断是否执行beforeBodyWrite的,那我们只需要在supports中判断返回值类型是否为Result即可,如果不是才进行封装,如果是则直接返回。代码如下:

@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return !returnType.getParameterType().equals(Result.class);
}

修改后重启应用再次请求接口即可发现报文格式符合我们预期了。

{
"code": "999999",
"message": "操作失败",
"data": null
}

自定义异常信息

到此为止我们完成了异常的统一处理,但是目前所有的异常都会返回999999,无法进行自定义错误内容,显然还没有填完开头挖的坑,所以我们继续对程序进行改造,在介绍@ExceptionHandler我们提到参数里可以指定处理的异常类型,那我们就从此处入手,自定义一个异常,然后增加一个对应的异常处理器,在异常处理器中根据异常中的错误信息进行转换。

因为要自定义异常,不可能将异常信息散落在代码里,所以要选择一个地方统一维护异常代码和对应的异常信息,这里作者选择的是枚举类,如果项目有其他要求也可以选择properties文件等载体,只是读取信息时方式不一样而已。枚举类代码如下,添加此枚举之后大家也可以把之前涉及SUCCESS和FAILED的地方重构一下,这里就不展开了:

public enum ResultCode {

    SUCCESS("000000", "操作成功"),
FAILED("999999", "操作失败"),
DEMO_ERROR("010101", "订单当前不支持此操作")
; private final String code;
private final String message; ResultCode(String code, String message) {
this.code = code;
this.message = message;
}
// TODO 添加getter和setter方法
}

有了异常信息列表后我们需要创建一个自定义异常作为信息载体,以便可以在代码中使用throw触发异常处理器,代码如下:

public class FrameworkException extends Exception{

    private ResultCode resultCode;

    public FrameworkException(ResultCode resultCode) {
super(resultCode.getMessage() + "(" + resultCode.getCode() + ")");
this.resultCode = resultCode;
}
// TODO 添加getter和setter方法
}

之后我们要添加对应的异常处理器,在ExceptionAdvice中增加如下方法,用于处理我们刚刚添加的自定义异常。

@ExceptionHandler(FrameworkException.class)
public Result handleFrameworkException(FrameworkException e) {
ResultCode code = e.getResultCode();
Result result = new Result();
result.setCode(code.getCode());
result.setMessage(code.getMessage());
return result;
}

之后我们在测试方法中任意位置添加如下测试代码,注意添加之后之前的代码会报错,因为异常后的代码无法执行到会导致编译报错,这里注释掉即可。

throw new FrameworkException(ResultCode.DEMO_ERROR);

更新后重启项目再次请求接口即可看到已经完成了异常信息的自定义,前端也可以针对性的进行业务处理了。完结!撒花~

{
"code": "010101",
"message": "订单当前不支持此操作",
"data": null
}

SpringBoot项目统一处理返回值和异常的更多相关文章

  1. ASP.NET Core搭建多层网站架构【11-WebApi统一处理返回值、异常】

    2020/02/01, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[11-WebApi统一处理返回值.异常] 使用I ...

  2. asp.net core webapi 统一处理返回值、异常和请求参数验证

    现在的开发模式很少用asp.net mvc一个项目直接操作界面和数据库了.大部分都使用前后端分离,更多的是为了让API支持移动端. 后端写webapi的时候必然需要和前端约定请求值和返回值的格式,如果 ...

  3. SpringBoot统一处理返回结果和异常情况

    如果文章有帮助到你,还请点个赞或留下评论 原因 在springboot项目里我们希望接口返回的数据包含至少三个属性: code:请求接口的返回码,成功或者异常等返回编码,例如定义请求成功. messa ...

  4. Springboot项目统一异常处理

    Springboot项目统一异常处理 一.接口返回值封装 1. 定义Result对象,作为通用返回结果封装 2. 定义CodeMsg对象,作为通用状态码和消息封装 二.定义全局异常类 三.定义异常处理 ...

  5. SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!

    大家好,我是飘渺. 今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何友好的返回统一的标准格式以及如何优雅的处理全局异常. 首先我们来看看为什么要返回统一的标准格式? 为什么要对Sp ...

  6. .NetCore Web Api 利用ActionFilterAttribute统一接口返回值格式

    .Net Core 同 Asp.Net MVC一样有几种过滤器,这里不再赘述每个过滤器的执行顺序与作用. 在实际项目开发过程中,统一API返回值格式对前端或第三方调用将是非常必要的,在.NetCore ...

  7. Java开发学习(十八)----AOP通知获取数据(参数、返回值、异常)

    前面的博客我们写AOP仅仅是在原始方法前后追加一些操作,接下来我们要说说AOP中数据相关的内容,我们将从获取参数.获取返回值和获取异常三个方面来研究切入点的相关信息. 前面我们介绍通知类型的时候总共讲 ...

  8. 使用 ResponseBodyAdvice 拦截Controller方法默认返回参数,统一处理返回值/响应体

    使用 @ControllerAdvice & ResponseBodyAdvice 拦截Controller方法默认返回参数,统一处理返回值/响应体 1.Controller代码 以下是Con ...

  9. SpringBoot项目中处理返回json的null值

    在后端数据接口项目开发中,经常遇到返回的数据中有null值,导致前端需要进行判断处理,否则容易出现undefined的情况,如何便捷的将null值转换为空字符串? 以SpringBoot项目为例,SS ...

  10. SpringBoot 如何统一后端返回格式

    在前后端分离的项目中后端返回的格式一定要友好,不然会对前端的开发人员带来很多的工作量.那么SpringBoot如何做到统一的后端返回格式呢?今天我们一起来看看. 为什么要对SpringBoot返回统一 ...

随机推荐

  1. 2022-06-04:给定一个数字n,表示一开始有编号1~n的树木,列成一条直线, 给定一个有序数组arr,表示现在哪些树已经没了,arr[i]一定在[1,n]范围, 给定一个数字m,表示你可以补种多

    2022-06-04:给定一个数字n,表示一开始有编号1~n的树木,列成一条直线, 给定一个有序数组arr,表示现在哪些树已经没了,arr[i]一定在[1,n]范围, 给定一个数字m,表示你可以补种多 ...

  2. Grafana系列-统一展示-9-Jaeger数据源

    系列文章 Grafana 系列文章 配置 Jaeger data source Grafana内置了对Jaeger的支持,它提供了开源的端到端分布式跟踪.本文解释了针对Jaeger数据源的配置和查询. ...

  3. 小知识:设置archive_lag_target参数强制日志切换

    为客户测试一个ADG场景问题,发现测试环境的日志切换频率过低,总是需要定期手工切换,这非常影响测试心情. 实际上,可以设置archive_lag_target参数强制日志切换. 比如设置: alter ...

  4. flutter填坑之旅(配置本地资源文件)

    一.添加单个资源文件 项目下创建一个assets目录,在该目录下存放对应的资源文件(json.image.-) pubspec.yaml文件中配置资源文件路径(注意缩进要与上下文一直) assets: ...

  5. OWASP移动应用安全测试指南中文版

    OWASP移动应用安全测试指南(MASTG)是OWASP移动应用安全(MAS)旗舰项目的一部分,是一本涵盖移动应用安全分析过程.技术和工具的综合手册,也是一套详尽的测试案例,用于验证OWASP移动应用 ...

  6. STP生成树实验

    实验拓扑 实验需求 所有设备都运行STP 改变阻塞端口 实验步骤 1.所有设备都运行STP ,等到收敛完毕,观察状态 [SW1]stp mode stp [SW2]stp mode stp [SW3] ...

  7. Journal of Electronic Imaging投稿分享

    Journal of Electronic Imaging投稿分享 在研究生阶段中的第一篇论文,前后总共三个月,还是很开心的!!! 附下中稿图片 这个期刊从二月份开始投的,然后三月份给了大修,大修时间 ...

  8. Kubernetes——构建平台工程的利器

    作者|Loft Team 翻译|Seal软件 链接|https://loft.sh/blog/why-platform-engineering-teams-should-standardize-on- ...

  9. SAP ABAP 动态结构实现发送企业微信应用消息

    企业微信官方接口: 应用支持推送文本.图片.视频.文件.图文等类型. 请求方式:POST(HTTPS)请求地址: https://qyapi.weixin.qq.com/cgi-bin/message ...

  10. JVM之指针压缩

    做java开发的同学一般都比较熟悉JVM,那么关于指针压缩这块内容是不是也了解呢,不熟悉的小伙伴往下看吧. 首先说明,本文涉及的JDK版本是1.8,JVM虚拟机是64位的HotSpot实现为准. ja ...