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

UNSUPPORTED_GRANT_TYPE(1003, “不支持的认证模式”);

/自定义状态码/

private final int code;

/自定义描述/

private final String message;

ReturnCode(int code, String message){

this.code = code;

this.message = message;

}

public int getCode() {

return code;

}

public String getMessage() {

return message;

}

}

复制代码

统一返回格式

@GetMapping(“/hello”)

public ResultData getStr(){

return ResultData.success(“hello,javadaily”);

}

复制代码

此时调用接口获取到的返回值是这样:

{

“status”: 100,

“message”: “hello,javadaily”,

“data”: null,

“timestamp”: 1625736481648,

“httpStatus”: 0

}

复制代码

这样确实已经实现了我们想要的结果,我在很多项目中看到的都是这种写法,在Controller层通过 ResultData.success()对返回结果进行包装后返回给前端。

看到这里我们不妨停下来想想,这样做有什么弊端呢?

最大的弊端就是我们后面每写一个接口都需要调用 ResultData.success()这行代码对结果进行包装,重复劳动,浪费体力;而且还很容易被其他老鸟给嘲笑。

所以呢我们需要对代码进行优化,目标就是不要每个接口都手工制定 ResultData返回值。

高级实现方式

要优化这段代码很简单,我们只需要借助SpringBoot提供的 ResponseBodyAdvice即可。

ResponseBodyAdvice的作用:拦截Controller方法的返回值,统一处理返回值/响应体,一般用来统一返回格式,加解密,签名等等。

先来看下 ResponseBodyAdvice的源码:

public interface ResponseBodyAdvice {

/**

  • 是否支持advice功能

  • true 支持,false 不支持

*/

boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);

/**

  • 对返回的数据进行处理

*/

@Nullable

T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);

}

复制代码

我们只需要编写一个具体实现类即可

/**

  • @author jam

  • @date 2021/7/8 10:10 上午

*/

@RestControllerAdvice

public class ResponseAdvice implements ResponseBodyAdvice {

@Autowired

private ObjectMapper objectMapper;

@Override

public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {

return true;

}

@SneakyThrows

@Override

public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {

if(o instanceof String){

return objectMapper.writeValueAsString(ResultData.success(o));

}

return ResultData.success(o);

}

}

复制代码

需要注意两个地方:

  • @RestControllerAdvice注解

@RestControllerAdvice@RestController注解的增强,可以实现三个方面的功能:

  1. 全局异常处理

  2. 全局数据绑定

  3. 全局数据预处理

  • String类型判断

if(o instanceof String){

return objectMapper.writeValueAsString(ResultData.success(o));

}

复制代码

这段代码一定要加,如果Controller直接返回String的话,SpringBoot是直接返回,故我们需要手动转换成json。

经过上面的处理我们就再也不需要通过 ResultData.success()来进行转换了,直接返回原始数据格式,SpringBoot自动帮我们实现包装类的封装。

@GetMapping(“/hello”)

public String getStr(){

return “hello,javadaily”;

}

复制代码

此时我们调用接口返回的数据结果为:

{

“status”: 100,

“message”: “操作成功”,

“data”: “hello,javadaily”,

“timestamp”: 1626427373113

}

复制代码

是不是感觉很完美,别急,还有个问题在等着你呢。

接口异常问题

此时有个问题,由于我们没对Controller的异常进行处理,当我们调用的方法一旦出现异常,就会出现问题,比如下面这个接口

@GetMapping(“/wrong”)

public int error(){

int i = 9/0;

return i;

}

复制代码

返回的结果为:

这显然不是我们想要的结果,接口都报错了还返回操作成功的响应码,前端看了会打人的。

别急,接下来我们进入第二个议题,如何优雅的处理全局异常。

SpringBoot为什么需要全局异常处理器


  1. 不用手写try…catch,由全局异常处理器统一捕获

使用全局异常处理器最大的便利就是程序员在写代码时不再需要手写 try...catch了,前面我们讲过,默认情况下SpringBoot出现异常时返回的结果是这样:

{

“timestamp”: “2021-07-08T08:05:15.423+00:00”,

“status”: 500,

“error”: “Internal Server Error”,

“path”: “/wrong”

}

复制代码

这种数据格式返回给前端,前端是看不懂的,所以这时候我们一般通过

复制代码

try...catch来处理异常

@GetMapping(“/wrong”)

public int error(){

int i;

try{

i = 9/0;

}catch (Exception e){

log.error(“error:{}”,e);

i = 0;

}

return i;

}

复制代码

我们追求的目标肯定是不需要再手动写 try...catch了,而是希望由全局异常处理器处理。

  1. 对于自定义异常,只能通过全局异常处理器来处理

@GetMapping(“error1”)

public void empty(){

throw new RuntimeException(“自定义异常”);

}

复制代码

  1. 当我们引入Validator参数校验器的时候,参数校验不通过会抛出异常,此时是无法用 try...catch捕获的,只能使用全局异常处理器。

SpringBoot集成参数校验请参考这篇文章SpringBoot开发秘籍 - 集成参数校验及高阶技巧

如何实现全局异常处理器

@Slf4j

@RestControllerAdvice

public class RestExceptionHandler {

/**

  • 默认全局异常处理。

  • @param e the e

  • @return ResultData

*/

@ExceptionHandler(Exception.class)

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)

public ResultData exception(Exception e) {

log.error(“全局异常信息 ex={}”, e.getMessage(), e);

return ResultData.fail(ReturnCode.RC500.getCode(),e.getMessage());

}

}

复制代码

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-Vor66QMd-1712763920326)]
[外链图片转存中…(img-Vi8JtRK0-1712763920327)]
[外链图片转存中…(img-llHVfEra-1712763920327)]
[外链图片转存中…(img-6knY8fGa-1712763920327)]
[外链图片转存中…(img-cVnt28nr-1712763920328)]
[外链图片转存中…(img-ldeTUam4-1712763920328)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-Lz4k8x4s-1712763920328)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值