在前后端分离的项目中后端返回的格式一定要友好,不然会对前端的开发人员带来很多的工作量。那么SpringBoot如何做到统一的后端返回格式呢?今天我们一起来看看。
为什么要对SpringBoot返回统一的标准格式
在默认情况下,SpringBoot的返回格式常见的有三种:
返回String
1. @GetMapping("/hello")
2. publicString(){
3. return"hello";
4. }
此时调用接口获取到的返回值是这样:
hello
返回自定义对象
1. @GetMapping("/student")
2. publicStudent(){
3. Student=newStudent();
4. .setId(1);
5. .setName("didiplus");
6. return;
7. }
8.
9. //student的类
10. @Data
11. publicclassStudent{
12. privateInteger;
13. privateString;
14. }
此时调用接口获取到的返回值是这样:
{"id":1,"name":"didiplus"}
接口异常
1. @GetMapping("/error")
2.publicint(){
3. int=9/0;
4. return;
5. }
此时调用接口获取到的返回值是这样:
SpringBoot的版本是v2.6.7,
定义返回对象
1. package.didiplus.common.web.respons
3. import.Data;
5. import.io.Serializable;
7. /**
8. * Author: didiplus
9. * Email: 972479352@qq.com
10. * CreateTime: 2022/4/24
11. * Desc: Ajax 返 回 JSON 结 果 封 装 数 据
12. */
13.
14. @Data
15. publicclassResult<T>implementsSerializable{
16.
17. /**
18. * 是否返回成功
19. */
20. privateboolean;
21.
22. /**
23. * 错误状态
24. */
25. privateint;
26.
27. /***
28. * 错误信息
29. */
30. privateString;
31.
32. /**
33. * 返回数据
34. */
35. private;
36.
37. /**
38. * 时间戳
39. */
40. privatelong;
41.
42. publicResult(){
43. this.timestamp =System.currentTimeMillis();
44. }
45. /**
46. * 成功的操作
47. */
48. publicstatic<T>Result<T>(){
49. return(null);
50. }
51.
52. /**
53. * 成 功 操 作 , 携 带 数 据
54. */
55. publicstatic<T>Result<T>(T data){
56. return(ResultCode.RC100.getMessage(),data);
57. }
58.
59. /**
60. * 成 功 操 作, 携 带 消 息
61. */
62. publicstatic<T>Result<T>(String){
63. return(message,null);
64. }
65.
66. /**
67. * 成 功 操 作, 携 带 消 息 和 携 带 数 据
68. */
69. publicstatic<T>Result<T>(String,){
70. return(ResultCode.RC100.getCode(),,);
71. }
72.
73. /**
74. * 成 功 操 作, 携 带 自 定 义 状 态 码 和 消 息
75. */
76. publicstatic<T>Result<T>(int,String){
77. return(code,,null)
78. }
79.
80. publicstatic<T>Result<T>(int,String,T data){
81. Result<T>=newResult<T>();
82. .setCode(code);
83. .setMsg(message);
84. .setSuccess(true);
85. .setData(data);
86. return;
87. }
88.
89. /**
90. * 失 败 操 作, 默 认 数 据
91. */
92. publicstatic<T>Result<T>(){
93. return(ResultCode.RC100.getMessage());
94. }
95.
96. /**
97. * 失 败 操 作, 携 带 自 定 义 消 息
98. */
99. publicstatic<T>Result<T>(String){
100. return(message,null);
101. }
102.
103. /**
104. * 失 败 操 作, 携 带 自 定 义 消 息 和 数 据
105. */
106. publicstatic<T>Result<T>(String,){
107. return(ResultCode.RC999.getCode(),,);
108. }
109.
110. /**
111. * 失 败 操 作, 携 带 自 定 义 状 态 码 和 自 定 义 消 息
112. */
113. publicstatic<T>Result<T>(int,String){
114. return(ResultCode.RC999.getCode(),,null);
115. }
116.
117. /**
118. * 失 败 操 作, 携 带 自 定 义 状 态 码 , 消 息 和 数 据
119. */
120. publicstatic<T>Result<T>(int,String,){
121. Result<T>=newResult<T>();
122. .setCode(code);
123. .setMsg(message);
124. .setSuccess(false);
125. .setData(data);
126. return;
127. }
128.
129. /**
10. * Boolean 返 回 操 作, 携 带 默 认 返 回 值
131. */
132. publicstatic<T>Result<T>(boolean){
13. return(b,ResultCode.RC100.getMessage(),ResultCode.RC999.getMessage());
134. }
135.
136. /**
137. * Boolean 返 回 操 作, 携 带 自 定 义 消 息
138. */
139. publicstatic<T>Result<T>(boolean,String,String){
140. if(b){
141
142. }else{
143. return(failure);
144. }
145. }
146. }
定义状态码
1. package.didiplus.common.web.response;
2.
3. import.Getter;
4.
5. /**
6. * Author: didiplus
7. * Email: 972479352@qq.com
8. * CreateTime: 2022/4/24
9. * Desc: 统 一 返 回 状 态 码
10. */
11. publicenumResultCode{
12. /**操作成功**/
13. (100,"操作成功"),
14. /**操作失败**/
15. (999,"操作失败"),
16. /**服务限流**/
17. (200,"服务开启限流保护,请稍后再试!"),
18. /**服务降级**/
19. (201,"服务开启降级保护,请稍后再试!"),
20. /**热点参数限流**/
21. (202,"热点参数限流,请稍后再试!"),
22. /**系统规则不满足**/
23. (203,"系统规则不满足要求,请稍后再试!"),
24. /**授权规则不通过**/
25. (204,"授权规则不通过,请稍后再试!"),
26. /**access_denied**/
27. (403,"无访问权限,请联系管理员授予权限"),
28. /**access_denied**/
29. (401,"匿名用户访问无权限资源时的异常"),
30. /**服务异常**/
31. (500,"系统异常,请稍后重试"),
32.
3
34. (2003,"没有权限访问该资源"),
35. (1001,"客户端认证失败"),
36. (1002,"用户名或密码错误"),
37. (1003,"不支持的认证模式");
38.
39. /**自定义状态码**/
40. @Getter
41. privatefinalint;
42.
43. /**
44. * 携 带 消 息
45. */
46. @Getter
47. privatefinalString;
48. /**
49. * 构 造 方 法
50. */
51. ResultCode(int,String){
52.
53. this.code =;
54.
55. this.message =;
56. }
57. }
统一返回格式
1. @GetMapping("/hello")
2. publicResult<String>(){
3. returnResult.success("操作成功","hello");
4. }
此时调用接口获取到的返回值是这样:
1. {"success":true,"code":100,"msg":"操作成功","data":"hello","timestamp":1650785058049}
这样确实已经实现了我们想要的结果,我在很多项目中看到的都是这种写法,在Controller层通过Result.success()对返回结果进行包装后返回给前端。这样显得不够专业而且不够优雅。 所以呢我们需要对代码进行优化,目标就是不要每个接口都手工制定Result返回值。
高级实现方式
要优化这段代码很简单,我们只需要借助SpringBoot提供的ResponseBodyAdvice即可。
ResponseBodyAdvice的源码:
只需要编写一个具体实现类即可
1. publicinterfaceResponseBodyAdvice<T>{
2. /**
3. * 是否支持advice功能
4. * true 支持,false 不支持
5. */
6. boolean(MethodParameter,Class<?extendsHttpMessageConverter<?>>);
7.
8. /**
9. * 对返回的数据进行处理
10. */
11. @Nullable
12. (@Nullable,MethodParameter,MediaType,Class<?extendsHttpMessageConverter<?>>,ServerHttpRequest,ServerHttpResponse);
13. }
1. @RestControllerAdvice
2. publicclassResponseAdviceimplementsResponseBodyAdvice<Object>{
3.
4. @Autowired
5. ObjectMapper;
6.
7. @Override
8. publicboolean(MethodParameter,Class<?extendsHttpMessageConverter<?>>){
9. returntrue;
10. }
11.
12. @SneakyThrows
13. @Override
14. publicObject(Object,MethodParameter,MediaType,Class<?extendsHttpMessageConverter<?>>,ServerHttpRequest,ServerHttpResponse){
15. if(body instanceofString){
16. return.writeValueAsString(Result.success(ResultCode.RC100.getMessage(),body));
17. }
18. returnResult.success(ResultCode.RC100.getMessage(),body);
19. }
20. }
需要注意两个地方:
@RestControllerAdvice注解 @RestControllerAdvice是@RestController注解的增强,可以实现三个方面的功能:
- 全局异常处理
- 全局数据绑定
- 全局数据预处理
String类型判断
1. if(body instanceofString){
2. return.writeValueAsString(Result.success(ResultCode.RC100.getMessage(),body));
3. }
这段代码一定要加,如果Controller直接返回String的话,SpringBoot是直接返回,故我们需要手动转换成json。 经过上面的处理我们就再也不需要通过ResultData.success()来进行转换了,直接返回原始数据格式,SpringBoot自动帮我们实现包装类的封装。
1. @GetMapping("/hello")
2. publicString(){
3. return"hello,didiplus";
4. }
5.
6. @GetMapping("/student")
7. publicStudent(){
8. Student=newStudent();
9. .setId(1);
10. .setName("didiplus");
11. return;
12. }
此时我们调用接口返回的数据结果为:
1. {
2. "success":true,
3. "code":100,
4. "msg":"操作成功",
5. "data":"hello,didiplus",
6. "timestamp":1650786993454
7. }