为什么?
前后端分离的时代,如果没有统一的返回格式,给前端的结果各式各样,估计前端的小伙伴就要骂娘了。 我们想对自定义异常抛出指定的状态码排查错误,对系统的不可预知的异常抛出友好一点的异常信息。 我们想让接口统一返回一些额外的数据,例如接口执行的时间等等。 … 所以嘛,不管是日常和前端小伙伴对接,还是和其他部门进行接口对接,都应该返回固定的格式。 本文给出一个简单通用的返回格式,小伙伴们有需求,可以基于此版本根据自己的业务需求丰富返回格式。
返回数据格式如下👇,有兴趣的小伙伴们可以继续往下看SpringBoot是怎么来实现的。
{
"status": true,
"code": "0000",
"msg": "",
"data": {
"id": 1,
"deptId": 103,
"userName": "admin",
"nickName": "笑小枫",
"userType": "00",
"email": "xxf@163.com",
"phone": "15888888888",
"status": "0",
"remark": "管理员"
}
}
怎么做?🧐
首先创建一个测试的Controller,代码如下👇
package com.maple.demo.controller;
import com.maple.demo.util.ResultJson;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping("/example")
public class TestResultController {
@GetMapping("/testResult")
public Test testResult() {
Test test = new Test();
test.setName("笑小枫");
test.setAge(18);
test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
return test;
}
@GetMapping("/testResultJson")
public ResultJson testResultJson() {
Test test = new Test();
test.setName("笑小枫");
test.setAge(18);
test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
return new ResultJson(test);
}
@Data
static class Test {
private String name;
private Integer age;
private String remark;
}
}
我们先看看这是调用返回的结果👀
{
"name": "笑小枫",
"age": 18,
"remark": "大家好,我是笑小枫,喜欢我的小伙伴点个赞"
}
然后在util包下定义一个统一的返回格式类,代码如下👇
package com.maple.demo.util;
import lombok.Data;
@Data
public class ResultJson {
private static final String SUCCESS_CODE = "0000";
private Boolean status;
private String code;
private String msg;
private Object data;
public ResultJson() {
this.status = true;
this.code = SUCCESS_CODE;
this.msg = "";
}
public ResultJson(Object data) {
this.status = true;
this.code = SUCCESS_CODE;
this.msg = "";
this.data = data;
}
public ResultJson(String code, String msg) {
this.status = SUCCESS_CODE.equals(code);
this.code = code;
this.msg = msg;
}
public ResultJson(Boolean status, String code, String msg, Object data) {
this.status = status;
this.code = code;
this.msg = msg;
this.data = data;
}
}
普通版,代码侵入性强
这样我们可以把结果统一放在ResultJson里面返回,代码如下👇
package com.maple.demo.controller;
import com.maple.demo.util.ResultJson;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping("/example")
public class TestResultController {
@GetMapping("/testResult")
public Test testResult() {
Test test = new Test();
test.setName("笑小枫");
test.setAge(18);
test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
return test;
}
@GetMapping("/testResultJson")
public ResultJson testResultJson() {
Test test = new Test();
test.setName("笑小枫");
test.setAge(18);
test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
return new ResultJson(test);
}
@Data
static class Test {
private String name;
private Integer age;
private String remark;
}
}
我们看一下返回的结果👇
{
"status": true,
"code": "0000",
"msg": "",
"data": {
"name": "笑小枫",
"age": 18,
"remark": "大家好,我是笑小枫,喜欢我的小伙伴点个赞呗"
}
}
切面处理,代码无侵入
上述代码虽然实现了功能,但所有的返回结果都要处理,对代码有比较强的侵入,Spring拥有各种切面的支持,让我们看看如何代码无侵入的实现这个功能。
最后创建一个配置类,添加@RestControllerAdvice 注解,代码如下👇
package com.maple.demo.config;
import com.alibaba.fastjson.JSON;
import com.maple.demo.util.ResultJson;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
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.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@Slf4j
@RestControllerAdvice
public class RestResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(@NotNull MethodParameter returnType,
@NotNull Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body,
@NotNull MethodParameter returnType,
@NotNull MediaType selectedContentType,
@NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
@NotNull ServerHttpRequest request,
ServerHttpResponse response) {
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
ResultJson result = new ResultJson(body);
if (body == null) {
if (returnType.getParameterType().isAssignableFrom(String.class)) {
return JSON.toJSONString(result);
}
} else if (body instanceof ResultJson) {
return body;
} else if (body instanceof String) {
return JSON.toJSONString(result);
}
return result;
}
}
这样就完成了统一结果的返回?
我们再请求文章最开始写的测试方法http://127.0.0.1:6666/testResult 看一下返回的结果👀
{
"status": true,
"code": "0000",
"msg": "",
"data": {
"name": "笑小枫",
"age": 18,
"remark": "大家好,我是笑小枫,喜欢我的小伙伴点个赞呗"
}
}
这样统一返回格式就完成了,对代码没有的侵入,原来的代码该怎么写还是怎么写,是不是很nice。
@RestControllerAdvice介绍😴
首先了解一下SpringBoot的@RestControllerAdvice 注解,它是Spring框架3.2新增的的注解 点进去这个注解源码,可以看到它是由@ControllerAdvice 和@ResponseBody 的组合注解 它通常用来定义@ExceptionHandler , @InitBinder 以及@ModelAttribute 适用于所有方法@RequestMapping 的方法。 直白点说,这就是一个增强Controller的注解。主要实现以下三个方面的功能
- 全局异常处理
- 全局数据预处理
- 全局数据绑定
我们看一下@RestControllerAdvice 注解的源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
@AliasFor(
annotation = ControllerAdvice.class
)
String[] value() default {};
@AliasFor(
annotation = ControllerAdvice.class
)
String[] basePackages() default {};
@AliasFor(
annotation = ControllerAdvice.class
)
Class<?>[] basePackageClasses() default {};
@AliasFor(
annotation = ControllerAdvice.class
)
Class<?>[] assignableTypes() default {};
@AliasFor(
annotation = ControllerAdvice.class
)
Class<? extends Annotation>[] annotations() default {};
}
使用@RestControllerAdvice 常做的两个功能的实现
- 返回统一格式的结果
- 异常统一处理
小结
好啦,本文就到这里了,我们简单的总结一下,主要介绍了以下内容👇👇
- 为什么要返回统一的结果信息
- 手动封装统一的返回信息
- 使用@RestControllerAdvice自动封装返回信息
- 简单介绍@RestControllerAdvice注解
关于笑小枫💕
本章到这里结束了,喜欢的朋友关注一下我呦😘😘,大伙的支持,就是我坚持写下去的动力。 老规矩,懂了就点赞收藏;不懂就问,日常在线,我会就会回复哈~🤪 后续文章会陆续更新,文档会同步在微信公众号、个人博客、CSDN和GitHub保持同步更新。😬 微信公众号:笑小枫 笑小枫个人博客:https://www.xiaoxiaofeng.com CSDN:https://zhangfz.blog.csdn.net 本文源码:https://github.com/hack-feng/maple-demo
|