SpringBoot统一响应处理

目前很多项目都是前后端分离,在这样的大趋势下,后端向前端的响应结果规范格外地重要。

自定义响应结果

响应实体

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
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseEntityDemo<T> {

/**
* 状态码
*/
private int code;
/**
* 响应信息
*/
private String message;
/**
* 响应数据
*/
private T data;


/**
* 请求处理成功且不需要返回数据时使用的工具方法
* @return
*/
public static <Type> ResponseEntityDemo<Type> successWithoutData() {
return new ResponseEntityDemo<Type>(ResultCode.SUCCESS, null);
}

/**
* 请求处理成功且需要返回数据时使用的工具方法
* @param data 要返回的数据
* @return
*/
public static <Type> ResponseEntityDemo<Type> successWithData(Type data) {
return new ResponseEntityDemo<Type>(ResultCode.SUCCESS, data);
}

/**
* 请求处理失败后使用的工具方法(需要传错误信息)
* @param message 失败的错误消息
* @return
*/
public static <Type> ResponseEntityDemo<Type> failed(String message) {
return new ResponseEntityDemo<Type>(ResultCode.FAILED.getCode(), message, null);
}

/***
* 请求处理失败后使用的工具方法(需要传状态码以及错误信息)
* @param resultCode
* @param <Type>
* @return
*/
public static <Type> ResponseEntityDemo<Type> failed(ResultCode resultCode) {
return new ResponseEntityDemo<Type>(resultCode, null);
}

/***
* 请求处理失败后使用的工具方法(使用默认错误信息)
* @param <Type>
* @return
*/
public static <Type> ResponseEntityDemo<Type> failed() {
return new ResponseEntityDemo<Type>(ResultCode.FAILED, null);
}

private ResponseEntityDemo(ResultCode resultCode, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMsg();
this.data = data;
}

}

响应状态码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Getter
@AllArgsConstructor
public enum ResultCode {

SUCCESS(200, "操作成功"),

FAILED(1001, "响应失败"),

VALIDATE_FAILED(1002, "参数校验失败"),

ERROR(5000, "未知错误");

private int code; //状态码
private String msg; //响应信息

}

响应数据的统一处理

先创建一个类加上注解使其成为全局处理类并使用同异常处理的全局处理所使用的@RestControllerAdvice注解。然后继承ResponseBodyAdvice接口重写其中的方法,即可对我们的controller进行增强操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestControllerAdvice(basePackages = {"com.wht.houtaidemo.controller"})
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {

@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
// 如果接口返回的类型本身就已经包装好了直接返回就行,返回false不用执行beforeBodyWrite进行后续处理
return !returnType.getParameterType().equals(ResponseEntityDemo.class);
}

@Override
public Object beforeBodyWrite(Object result, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//对数据进行包装
return ResponseEntityDemo.successWithData(result);
}
}

Tip:ResponseBodyAdvice中supports方法要返回为true才会执行beforeBodyWrite方法

上述写法会有一个小问题:

  • 如果返回类型为String的话会报类型转换异常,所以需要进行特判

    image-20220723112607503

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // String类型不能直接包装,会被其他转换器识别,所以要进行些特别的处理
    if (returnType.getGenericParameterType().equals(String.class)) {
    ObjectMapper objectMapper = new ObjectMapper();
    try {
    // 将数据包装在ResponseEntity里后,再转换为json字符串响应给前端
    return objectMapper.writeValueAsString(ResponseEntityDemo.successWithData(result));
    } catch (JsonProcessingException e) {
    //返回自定义异常
    throw new APIException("返回String类型错误");
    }
    }

完整版本

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
@RestControllerAdvice(basePackages = {"com.wht.houtaidemo.controller"})
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {

@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
// 如果接口返回的类型本身就已经包装好了直接返回就行,返回false不用执行beforeBodyWrite进行后续处理
return !(returnType.getParameterType().equals(ResponseEntityDemo.class)||returnType.getParameterType().equals(ResponseEntity.class));
}

@Override
public Object beforeBodyWrite(Object result, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//结果为空也返回错误
if(result == null){
return ResponseEntityDemo.failed();
}

// String类型不能直接包装,所以要进行些特别的处理
if (returnType.getGenericParameterType().equals(String.class)) {
ObjectMapper objectMapper = new ObjectMapper();
try {
// 将数据包装在ResponseEntity里后,再转换为json字符串响应给前端
return objectMapper.writeValueAsString(ResponseEntityDemo.successWithData(result));
} catch (JsonProcessingException e) {
//返回自定义异常
throw new APIException("返回String类型错误");
}
}
//如果返回false返回错误
if(returnType.getGenericParameterType().equals(Boolean.class) && (Boolean)result == false){
return ResponseEntityDemo.failed();
}

//对数据进行包装
return ResponseEntityDemo.successWithData(result);
}
}

测试

直接返回数据不进行响应包装

1
2
3
4
5
6
7
@GetMapping("/getDemo")
public TestDemo getTestDemo(){
TestDemo demo = new TestDemo();
demo.setId(1L);
demo.setUsername("test");
return demo;
}

结果:

image-20220723123942747

参考