如何实现后端开发框架(一)-自动封装返回结果
如何实现后端开发框架(一)-自动封装返回结果
1.问题描述
现在的系统一般都是前后端分离的开发模式,那么后端都是实现Restful API方法,为了前后端API对接方便,后端返回的结果数据结构都需要统一。
后端返回的数据结构一般为以下格式:
1
2
3
4
5
6
7
{
"code": 200,
"message": "成功",
"data": {
"xxx": "xxx",
"xxx": "xxx"
}
那么如何在开发框架层面来实现自动封装Restful API的返回结果的呢?
2.实现思路
Spring中存在一个ResponseBodyAdvice接口,这个接口可以统一对所有Restful API方法的返回值进行处理。
同时需要在每个方法上实现一个自定义注解,在ResponseBodyAdvice的方法中就可以根据方法上的注解值来判断是否要自动封装返回值。
为什么有时候需要取消自动封装返回值的操作?因为服务端API接口对接,文件下载返回文件流等场景下不封装返回值更方便处理。
3.实现步骤
3.1.实现自定义注解
1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface ResponseResult {
boolean autoPackage() default true;
}
3.2.定义Http状态码的枚举值
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
public enum ResultCode {
SUCCESS(HttpStatus.OK.value(), "成功"),
FAIL(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统内部错误"),
UNAUTHORIZED(HttpStatus.UNAUTHORIZED.value(), "未授权访问"),
FORBIDDEN(HttpStatus.FORBIDDEN.value(), "禁止访问"),
LOGIN_FAIL(HttpStatus.INTERNAL_SERVER_ERROR.value(), "用户账号或密码输入错误"),
CAPTCHA_FAIL(HttpStatus.INTERNAL_SERVER_ERROR.value(), "验证码输入错误"),
PASSWORD_EXPIRED(HttpStatus.INTERNAL_SERVER_ERROR.value(), "用户密码过期错误"),
LOGIN_SUCCESS(HttpStatus.OK.value(), "用户登录成功"),
LOGOUT_SUCCESS(HttpStatus.OK.value(), "用户登出成功"),
MULTIPLE_LOGIN(HttpStatus.UNAUTHORIZED.value(), "用户重复登录");
private Integer code;
private String message;
ResultCode() {}
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
3.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
58
59
60
61
62
63
64
public class MyResult<T> implements Serializable {
private Integer code;
private String message;
private T data;
public MyResult() {}
public MyResult(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public MyResult(ResultCode resultCode) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public MyResult<T> success(T data) {
MyResult<T> result = new MyResult();
result.setData(data);
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(ResultCode.SUCCESS.getMessage());
return result;
}
public MyResult<T> fail(ResultCode resultCode) {
MyResult<T> result = new MyResult();
result.setCode(resultCode.getCode());
result.setMessage(resultCode.getMessage());
return result;
}
public MyResult<T> fail(ResultCode resultCode, T data) {
MyResult result = new MyResult();
result.setCode(resultCode.getCode());
result.setMessage(resultCode.getMessage());
result.setData(data);
return result;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
3.4.实现统一封装返回值处理
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
@Slf4j
@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Autowired ObjectMapper objectMapper;
/**
* 判断是否要执行beforeBodyWrite方法,true为执行,false不执行
*
* @param methodParameter
* @param aClass
* @return
*/
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
boolean result = false;
// 如果方法上有自动封装返回值的注解,就以其为准
ResponseResult methodAnnotation = methodParameter.getMethodAnnotation(ResponseResult.class);
if (methodAnnotation != null) {
result = methodAnnotation.autoPackage();
} else {
// 如果方法上没自动封装返回值的注解,而类上有,就以其为准
ResponseResult classAnnotation =
methodParameter.getDeclaringClass().getAnnotation(ResponseResult.class);
if (classAnnotation != null) {
result = classAnnotation.autoPackage();
}
}
return result;
}
/**
* 对返回结果过滤字段和统一格式化
*
* @param body
* @param methodParameter
* @param mediaType
* @param aClass
* @param serverHttpRequest
* @param serverHttpResponse
* @return
*/
@SneakyThrows
@Override
public Object beforeBodyWrite(
Object body,
MethodParameter methodParameter,
MediaType mediaType,
Class aClass,
ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
// 当controller方法直接返回字符串结果时,会报String强转错误,所以这里需要手工对String返回值转json进行处理。
boolean isString = body instanceof String;
// 返回结果统一格式化
body = new MyResult().success(body);
if (isString) {
body = objectMapper.writeValueAsString(body);
}
return body;
}
}
4.测试代码
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
@Slf4j
@RestController
@RequestMapping("/test/user")
@ResponseResult
public class UserController {
/**
* 自动封装单个对象的返回值
*
* @return
*/
@GetMapping(value = "/test1")
public User test1() {
User user = new User();
user.setRealName("Randy");
user.setGender("male");
return user;
}
/**
* 自动封装多个对象集合的返回值
*
* @return
*/
@GetMapping(value = "/test2")
public List<User> test2() {
User user1 = new User();
user1.setRealName("Randy");
user1.setGender("male");
User user2 = new User();
user2.setRealName("Lucy");
user2.setGender("female");
List<User> result = new ArrayList();
result.add(user1);
result.add(user2);
return result;
}
/**
* 不自动封装返回值
*
* @return
*/
@ResponseResult(autoPackage = false)
@GetMapping(value = "/test3")
public List<User> test3() {
User user1 = new User();
user1.setRealName("Randy");
user1.setGender("male");
User user2 = new User();
user2.setRealName("Lucy");
user2.setGender("female");
List<User> result = new ArrayList();
result.add(user1);
result.add(user2);
return result;
}
/**
* 返回字符串类型的结果值
*
* @return
*/
@GetMapping(value = "/test4")
public String test4() {
return "success";
}
}
5.完整代码
完整代码见以下Git仓库中的response-result子项目:
https://github.com/randy0098/framework-samples
本文由作者按照
CC BY 4.0
进行授权