SpringBoot3 的统一请求返回值和全局异常处理

前言

本文给出的案例代码兼容 SpringBoot2 和 SpringBoot3,且理论上适用于所有 Spring 应用程序。

统一请求返回值

  • ReturnCodeEnum 枚举类
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
import lombok.Getter;
import java.util.Arrays;

@Getter
public enum ReturnCodeEnum {

/**操作失败**/
RC999("999","操作XXX失败"),
/**操作成功**/
RC200("200","success"),
/**服务降级**/
RC201("201","服务开启降级保护,请稍后再试!"),
/**热点参数限流**/
RC202("202","热点参数限流,请稍后再试!"),
/**系统规则不满足**/
RC203("203","系统规则不满足要求,请稍后再试!"),
/**授权规则不通过**/
RC204("204","授权规则不通过,请稍后再试!"),
/**无访问权限**/
RC403("403","无访问权限,请联系管理员授予权限"),
/**无访问权限**/
RC401("401","匿名用户访问无权限资源时的异常"),
RC404("404","404页面找不到的异常"),
/**服务异常**/
RC500("500","系统异常,请稍后重试"),
RC375("375","数学运算异常,请稍后重试"),
RC380("380", "参数校验不通过,请提交合法的参数"),
/**认证授权**/
INVALID_TOKEN("2001","访问令牌不合法"),
ACCESS_DENIED("2003","没有权限访问该资源"),
CLIENT_AUTHENTICATION_FAILED("1001","客户端认证失败"),
USERNAME_OR_PASSWORD_ERROR("1002","用户名或密码错误"),
BUSINESS_ERROR("1004","业务逻辑异常"),
UNSUPPORTED_GRANT_TYPE("1003", "不支持的认证模式");

/**
* 自定义的状态码
*/
private final String code;

/**
* 自定义的描述信息
*/
private final String message;

ReturnCodeEnum(String code, String message) {
this.code = code;
this.message = message;
}

public static ReturnCodeEnum getReturnCodeEnumV1(String code) {
for (ReturnCodeEnum element : ReturnCodeEnum.values()) {
if (element.getCode().equalsIgnoreCase(code)) {
return element;
}
}
return null;
}

public static ReturnCodeEnum getReturnCodeEnumV2(String code) {
return Arrays.stream(ReturnCodeEnum.values()).filter(x -> x.getCode().equalsIgnoreCase(code)).findFirst()
.orElse(null);
}

}
  • ResultData 处理结果类
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
import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class ResultData<T> {

private String code;
private String message;
private T data;
private long timestamp;

public ResultData() {
this.timestamp = System.currentTimeMillis();
}

public static <T> ResultData<T> success() {
ResultData resultData = new ResultData();
resultData.setCode(ReturnCodeEnum.RC200.getCode());
resultData.setMessage(ReturnCodeEnum.RC200.getMessage());
resultData.setData(null);
return resultData;
}

public static <T> ResultData<T> success(T data) {
ResultData resultData = new ResultData();
resultData.setCode(ReturnCodeEnum.RC200.getCode());
resultData.setMessage(ReturnCodeEnum.RC200.getMessage());
resultData.setData(data);
return resultData;
}

public static <T> ResultData<T> fail(String code, String message) {
ResultData resultData = new ResultData();
resultData.setCode(code);
resultData.setMessage(message);
resultData.setData(null);
return resultData;
}

}
  • 使用例子
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
@RestController
@RequestMapping("/pay")
public class PayController {

@Autowired
private PayService payService;

@DeleteMapping("/del/{id}")
public ResultData<Integer> deletePay(
@Parameter(in = ParameterIn.PATH, description = "ID", required = true) @PathVariable("id") Integer id) {
return ResultData.success(payService.delete(id));
}


@GetMapping("/pay/get/{id}")
public ResultData<Pay> getById(
@Parameter(in = ParameterIn.PATH, description = "ID", required = true) @PathVariable("id") Integer id) {
return ResultData.success(payService.getById(id));
}

@GetMapping("/pay/getAll")
public ResultData<List<Pay>> getAll() {
return ResultData.success(payService.getAll());
}

}

全局异常处理

  • GlobalException 自定义全局异常类
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
import lombok.Data;

@Data
public class GlobalException extends RuntimeException {

private String msg;
private int code = 500;

public GlobalException(String msg) {
super(msg);
this.msg = msg;
}

public GlobalException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}

public GlobalException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}

public GlobalException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}

}
  • GlobalExceptionHandler 全局异常处理器
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
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResultData<String> exceptionHandler(Exception e) {
// 日志记录
log.error("Global exception message:{}", e.getMessage(), e);

if (e instanceof GlobalException) {
// 处理自定义的全局异常
return ResultData.fail(ReturnCodeEnum.RC500.getCode(), e.getMessage());
} else if (e instanceof BindException) {
// 处理基于SpringMVC的JSR303参数校验失败的情况
return ResultData.fail(ReturnCodeEnum.RC380.getCode(), e.getMessage());
} else {
// 处理其他未知的全局异常
return ResultData.fail(ReturnCodeEnum.RC500.getCode(), e.getMessage());
}
}

}