背景
公司的一个项目,没有统一接口返回值。前端对接接口时,由于不同接口返回对象的千差万别,导致抽象处理接口返回结果很复杂。
有的公司,会新增一个统一返回对象,每一个接口,都返回这个对象。这样是可以实现的,很多公司也是这么做的,但是对代码有一定的入侵性。
这里用另一个思路来实现:
- 程序异常返回时,通过异常拦截,捕捉异常来构造统一返回对象
- 程序正常返回时,自定义返回值处理器来实现统一处理返回值的功能。
对于一个新手来说,接口返回值很随意,下面这样写接口是很常见的:
@RequestMapping("getAll")
public List<GoodJobEveryDay> getAll() throws JsonProcessingException {
List<GoodJobEveryDay> value = new ArrayList<>();
// 判断文件是否存在
if (FileHandling.isFileExists(FileHandling.FILE_NAME)) {
// 如果文件存在,读取文件内容
String fileContent = FileHandling.readFileContent(FileHandling.FILE_NAME);
value = objectMapper.readValue(fileContent, new TypeReference<List<GoodJobEveryDay>>() {
});
}
return value;
}
@GetMapping("getOne")
public GoodJobEveryDay getOne(@RequestParam Integer id) throws JsonProcessingException {
List<GoodJobEveryDay> value = getAll();
if (!CollectionUtils.isEmpty(value)) {
if (value.stream().filter(it -> id.equals(it.getId())).count() <= 0) {
throw new BusinessException(ExceptionEnum.DATA_NOT_FOUND);
}
Optional<GoodJobEveryDay> first = value.stream().filter(it -> it.getId().equals(id)).findFirst();
return first.isPresent() ? first.get() : null ;
}
return null;
}
那么,怎么才能达到以下结果果:
正常返回:
## 正常返回对象
{
"headers": {},
"body": {
"id": 1,
"content": "做了五组运动减大肚子;开始了记录每天做得好的地方。",
"badContent": null,
"time": "2024-02-18"
},
"statusCodeValue": 200,
"statusCode": "OK"
}
## 正常返回数组
{
"headers": {},
"body": [
{
"id": 1,
"content": "做了五组运动减大肚子;开始了记录每天做得好的地方。",
"badContent": null,
"time": "2024-02-18"
},
{
"id": 2,
"content": "早上6:20起床做饭;做了五组运动减大肚子。",
"badContent": "晚上边做运动的间隙,喝了一瓶可乐!",
"time": "2024-02-19"
}
],
"statusCode": "OK",
"statusCodeValue": 200
}
异常返回:
## 程序框架 抛出的异常
{
"headers": {},
"body": {
"message": "Required Integer parameter 'id' is not present",
"code": 500
},
"statusCode": "INTERNAL_SERVER_ERROR",
"statusCodeValue": 500
}
## 业务代码主动 抛出的异常,前端可以根据code来做适当处理
{
"headers": {},
"body": {
"message": "数据不存在",
"code": 100002
},
"statusCodeValue": 500,
"statusCode": "INTERNAL_SERVER_ERROR"
}
条件
博主写代码时,是基于 spring-web-5.2.6.RELEASE 版本。
核心处理代码
@RestControllerAdvice
+@ExceptionHandler
即可实现异常拦截,从而进行异常统一处理,返回 ErrorResponse 自定义异常对象。- 通过
ResponseBodyAdvice<Object>
来实现返回值统一处理,重写beforeBodyWrite方法,程序返回之前,将正常返回和异常返回统一处理。
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 自定义返回值处理器 来实现统一处理返回值的功能。
*/
@RestControllerAdvice
public class ResponseBodyHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 支持所有类型的返回值
return true;
}
/**
* 正常或异常结果返回前处理 body
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
// 业务自定义错误,前端可以根据请求体body中错误码code来做对应逻辑
if (body instanceof ErrorResponse) {
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
} else if (body instanceof ResponseEntity) {
// 如果返回值已经是ResponseEntity类型,则不做处理
return body;
}
// 否则包装成ApiResponse对象
return ResponseEntity.ok(body);
}
/**
* 异常拦截,处理异常返回对象
* @param ex 异常信息
* @return ResponseEntity<ErrorResponse>
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
if (ex instanceof BusinessException) {
BusinessException exception = (BusinessException) ex;
return new ResponseEntity<>(new ErrorResponse(exception.getMessage(), exception.getCode()), HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(new ErrorResponse(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
附录代码
自定义异常返回类 ErrorResponse
public class ErrorResponse {
private String message;
private int code;
public ErrorResponse() {
}
public ErrorResponse(String message, int code) {
this.message = message;
this.code = code;
}
public ErrorResponse(ExceptionEnum exceptionEnum) {
this.message = exceptionEnum.getMessage();
this.code = exceptionEnum.getCode();
}
// Getters and setters
}
自定义业务异常 BusinessException
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(int code) {
super();
this.code = code;
}
public BusinessException() {
super();
}
public BusinessException(String message, int code) {
super(message);
this.code = code;
}
public BusinessException(ExceptionEnum exceptionEnum) {
super(exceptionEnum.getMessage());
this.code = exceptionEnum.getCode();
}
public BusinessException(String message, Throwable cause, int code) {
super(message, cause);
this.code = code;
}
public BusinessException(Throwable cause, int code) {
super(cause);
this.code = code;
}
public int getCode() {
return code;
}
}
自定义系统业务异常码
/**
* 业务错误码
*/
public enum ExceptionEnum {
REPEAT_LOGIN_NAME(100001, "用户名重复"),
DATA_NOT_FOUND(100002, "数据不存在"),
USER_NOT_FOUND(100003, "用户不存在");
private String message;
private int code;
public String getMessage() {
return message;
}
public int getCode() {
return code;
}
ExceptionEnum(int code, String message) {
this.message = message;
this.code = code;
}
}