简介

当使用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. promise及异步编程async await

    前置说明 ECMAScript 6 新增了正式的 Promise(期约)引用类型,支持优雅地定义和组织异步逻辑.接下来几个版本增加了使用 async 和 await 关键字定义异步函数的机制 Java ...

  2. 没想到还有这种骚操作~如何使用Golang实现无头浏览器截图?

    前言 在Web开发中,有时需要对网页进行截图,以便进行页面预览.测试等操作. 而使用无头浏览器来实现截图功能,可以避免手动操作的繁琐和不稳定性. 这篇文章将介绍:使用Golang进行无头浏览器的截图, ...

  3. HTML渲染机制

    一直写页面但是很少对一些较深的运行机制的了解,这次趁休假查了一些相关的资料加上个人理解,记录一下关于html渲染的整个过程,也加深一下自己对html渲染的理解 一.先借一张图来看看html的整个加载过 ...

  4. Qt+QtWebApp开发笔记(四):http服务器使用Session和Cookie实现用户密码登录和注销功能

    前言   前面实现了基础的跳转,那么动态交互中登录是常用功能.  本篇实现一个动态交互的简单登录和注销功能,在Qt中使用Session和Cookie技术.   Demo    下载地址   链接:ht ...

  5. 在.net项目中添加Husky.Net提交验证

    参考:C# 项目添加 husky - jesn - 博客园 (cnblogs.com) 官方文档:Getting Started | Husky.Net (alirezanet.github.io) ...

  6. Python socket记录

    目录 网络编程 1.基本概念 Python中的网络编程 网络编程 1.基本概念 1.什么是客户端/服务器架构? 服务器就是一系列硬件或软件,为一个或多个客户端(服务的用户)提供所需的"服务& ...

  7. spring cloud gateway网关(一)之网关路由

    1.gateway相关介绍 在微服务架构中,系统往往由多个微服务组成,而这些服务可能部署在不同机房.不同地区.不同域名下.这种情况下,客户端(例如浏览器.手机.软件工具等)想要直接请求这些服务,就需要 ...

  8. Helm实战案例一:在Kubernetes上使用Helm搭建Prometheus Operator监控

    目录 一.系统环境 二.前言 三.Prometheus Operator简介 四.helm安装prometheus-operator 五.配置prometheus-operator 5.1 修改gra ...

  9. 前端vue uni-app百度地图定位组件,显示地图定位,标记点,并显示详细地址

    快速实现前端百度地图定位组件,显示地图定位,标记点,并显示详细地址; 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=12677 ...

  10. ApacheSpark:HowtoBuildandDeployaRealtimeDataProcessinga

    目录 2.1 基本概念解释 2.2 技术原理介绍 2.3 相关技术比较 3.1 准备工作:环境配置与依赖安装 3.2 核心模块实现 3.3 集成与测试 4.1 应用场景介绍 4.2 应用实例分析 4. ...