【SpringBoot专题】统一异常处理和统一数据返回之ResponseBodyAdvice
在实际开发中,我们希望对接口结果的返回,进行一次统一的封装,即便接口发生异常。今天主要是分享的是怎么接口层统一返回。
我们在做业务的时候通常都会封装一个通用的返回结果类,今天我也贴出来一个,如下所示:
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
public class ApiResponse<T> {
/**
* 系统状态
*/
@JsonProperty("code")
private Integer code;
/**
* 错误消息
*/
@JsonProperty("message")
private String message;
/**
* 错误内容列表
*/
@JsonProperty("errors")
private List<ErrorEntity> errors;
/**
* 返回结果内容
*/
@JsonProperty("data")
private T data;
/**
* 成功,没数据
*/
public ApiResponse() {
this(ApiResponseErrorCode.CODE_0.getCode(),
ApiResponseErrorCode.CODE_0.getMessage(), null, null);
}
/**
* 成功,有数据
*/
public ApiResponse(T data) {
this(ApiResponseErrorCode.CODE_0.getCode(),
ApiResponseErrorCode.CODE_0.getMessage(), data, null);
}
/**
* 指定code/message
*/
public ApiResponse(ApiResponseErrorCode apiResponseErrorCode) {
this(apiResponseErrorCode.getCode(), apiResponseErrorCode.getMessage(),
null, null);
}
/**
* 指定code + data
*/
public ApiResponse(ApiResponseErrorCode apiResponseErrorCode, T data) {
this(apiResponseErrorCode.getCode(), apiResponseErrorCode.getMessage(),
data, null);
}
/**
* 指定code + errors
*/
public ApiResponse(ApiResponseErrorCode apiResponseErrorCode,
List<ErrorEntity> errors) {
this(apiResponseErrorCode.getCode(), apiResponseErrorCode.getMessage(),
null, errors);
}
protected ApiResponse(Integer code, String message, T data,
List<ErrorEntity> errors) {
this.code = code;
this.message = message;
this.errors = errors;
this.data = data;
}
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 List<ErrorEntity> getErrors() {
return errors;
}
public void setErrors(List<ErrorEntity> errors) {
this.errors = errors;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
然后我们写自己的controller层,这里埋个伏笔,有心的同学可以看到我还做了参数校验(下篇博客中我会分享springboot如何做国际话校验,敬请期待)
@RestController
@RequestMapping("exception")
@Validated
public class ExceptionDemoController {
@GetMapping("demoA")
public ApiResponse<String> demoA() {
throw new BadRequestException("报错了。。。。");
}
@GetMapping("demoB")
public ApiResponse<String> demoB() {
throw new BadRequestException("报错了。。。。");
}
@GetMapping("/validate1")
@ResponseBody
public String validate1(
@Size(min = 1, max = 10, message = "姓名长度必须为1到10") @RequestParam("name") String name) {
return "validate1 测试";
}
/**
* http://localhost:8888/exception/validate2?lang=en_US
* http://localhost:8888/exception/validate2?lang=zh_CN
* @param user
* @return
*/
@PostMapping("/validate2")
@ResponseBody
public String validate2(@Valid
@RequestBody User user) {
return "validate1";
}
}
接着需要对这些带有ResponseBody注解的坐下处理,敲黑板这里将是今天的重点,我现实了ResponseBodyAdvice接口,通过这个接口的实现类来统一处理报文,代码如下:
@Slf4j
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class ApiResponseHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
String returnTypeName = returnType.getParameterType().getName();
return !"fast.cloud.nacos.common.model.response.ApiResponse".equals(returnTypeName) &&
!"org.springframework.http.ResponseEntity".equals(returnTypeName) && returnType.hasMethodAnnotation(ResponseBody.class);
}
@SuppressWarnings("rawtypes")
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest,
ServerHttpResponse serverHttpResponse) {
String name = methodParameter.getParameterType().getName();
if ("void".equals(name)) {
return new ApiResponse<>();
}
if (body instanceof String) {
return JsonUtils.toString(new ApiResponse<>(body));
}
return new ApiResponse<>(body);
}
}
这里有个坑,需要注意,当我们返回是string类型的时候,SpringMVC框架的MessageConverter会报错,你却返回ApiResponse,这样会出现ClassCastException ,解决方法就是将这个对象序列化一次。
github:如果觉得写得好的,可以给个star,谢谢