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