记录关于spring boot 统一返回数据及全局异常处理的操作实现。
一、统一返回数据
1、定义一个超类:BaseResponseVo
@Data
@NoArgsConstructor
public class BaseResponseVo{
protected Integer rtn;
protected String message;
}
2、定义一个枚举类来管理返回状态码:ResponseEnum
public enum ResponseEnum {
/**
* 请求成功
*/
SUCCESS(0, "ok"),
/**
* 前台请求参数错误
*/
BAD_REQUEST_PARAMETER(1, "请求参数不符合规定"),
/**
* 请求错误
*/
BAD_REQUEST(2, "请求错误"),
/**
* 请求的Content-Type错误
*/
MEDIA_TYPE_NOT_SUPPORTED(3, "请求的Content-Type错误");
private Integer rtn;
private String message;
ResponseEnum(Integer rtn, String message) {
this.rtn = rtn;
this.message = message;
}
public Integer getRtn() {
return rtn;
}
public String getMessage() {
return message;
}
}
3、定义带数据的返回模型:ResponseDataVo
@Data
@NoArgsConstructor
public class ResponseDataVo<T> extends BaseResponseVo {
private T data;
}
4、定义分页返回数据模型:PageResponseDataVo
public class PageResponseDataVo<T> extends BaseResponseVo {
/**
* 当前页
*/
private Integer pageNum;
/**
* 每页记录数
*/
private Integer pageSize;
/**
* 总记录数
*/
private Long total;
/**
* 当前页记录
*/
private Integer pageCount;
/**
* 总页数
*/
private Integer tatalPage;
/**
* 数据起始位置
*/
private Integer start;
/**
* 数据
*/
private List<T> list=new ArrayList<>();
}
5、定义返回数据处理工具类:ResponseDataUtils
public class ResponseDataUtils {
/**
* 请求成功时封装数据
*
* @param data 数据
* @return 返回BaseResponseVo封装后的数据
*/
public BaseResponseVo success(Object data) {
ResponseDataVo result = new ResponseDataVo();
result.setRtn(SUCCESS.getRtn());
result.setMessage(SUCCESS.getMessage());
result.setData(data);
return result;
}
/**
* 请求成功时封装数据
*
* @return 返回BaseResponseVo封装后的数据
*/
public BaseResponseVo success() {
return success(null);
}
/**
* 请求失败结果封装
*
* @param responseEnum 响应状态码
* @param message 响应信息
* @return 返回BaseResponseVo封装后的数据
*/
public BaseResponseVo error(ResponseEnum responseEnum, String message) {
BaseResponseVo result = new BaseResponseVo();
result.setRtn(responseEnum.getRtn());
String msg = StringUtils.isEmpty(message) ? responseEnum.getMessage() : message;
result.setMessage(msg);
return result;
}
/**
* 请求失败结果封装
*
* @param responseEnum 响应状态码
* @return 返回BaseResponseVo封装后的数据
*/
public BaseResponseVo error(ResponseEnum responseEnum) {
return error(responseEnum, null);
}
}
6:定义GlobalResponseHandler 实现ResponseBodyAdvice接口统一拦截接口返回数据。
要对返回值是String的类型单独处理下。
@Slf4j
@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {
/**
* Whether this component supports the given controller method return type
* and the selected {@code HttpMessageConverter} type.
*
* @param returnType the return type
* @param converterType the selected converter type
* @return {@code true} if {@link #beforeBodyWrite} should be invoked;
* {@code false} otherwise
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
//判断支持的类型,因为我们定义的BaseResponseVo 里面的data可能是任何类型,这里就不判断统一放过
//如果你想对执行的返回体进行操作,可将上方的Object换成你自己的类型
return true;
}
/**
* Invoked after an {@code HttpMessageConverter} is selected and just before
* its write method is invoked.
*
* @param body the body to be written
* @param returnType the return type of the controller method
* @param selectedContentType the content type selected through content negotiation
* @param selectedConverterType the converter type selected to write to the response
* @param request the current request
* @param response the current response
* @return the body that was passed in or a modified (possibly new) instance
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
log.info("请求返回数据类型class={}", body.getClass().getName());
BaseResponseVo result = null;
//兼容原来的接口返回
if (body instanceof ResponseDataVo) {
result = (ResponseDataVo) body;
} else if (body instanceof PageResponseDataVo) {
result = (PageResponseDataVo) body;
} else if (body instanceof BaseResponseVo) {
result = (BaseResponseVo) body;
} else {
result = ResponseDataUtils.success(body);
}
//debug时打印响应结果
if (log.isDebugEnabled()) {
log.debug("响应参数:{} ", JSON.toJSONString(result));
}
//处理返回值是String的情况
if (body instanceof String) {
return JSON.toJSONString(result);
}
return result;
}
}
7、定义GlobalExceptionHandler类统一异常处理
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* Create by lrt<br/>
* Date:2018/10/24
* Description: 拦截异常进行处理返回前台友好信息
*
* @param e 异常对象
* @return java.lang.String 返回前台信息
*/
@ExceptionHandler(Exception.class)
public BaseResponseVo exceptionHandler(Exception e) {
e.printStackTrace();
//参数校验错误
if (e instanceof BindException) {
BindException bindException = (BindException) e;
List<ObjectError> objectErrors = bindException.getBindingResult().getAllErrors();
return getValidExceptionResult(objectErrors);
}
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException bindException = (MethodArgumentNotValidException) e;
List<ObjectError> objectErrors = bindException.getBindingResult().getAllErrors();
return getValidExceptionResult(objectErrors);
}
//post请求未传参数
if (e instanceof HttpMessageNotReadableException) {
return ResponseDataUtils.error(ResponseEnum.BAD_REQUEST);
}
//请求Content type不支持
if (e instanceof HttpMediaTypeNotSupportedException) {
return ResponseDataUtils.error(ResponseEnum.MEDIA_TYPE_NOT_SUPPORTED);
}
return ResponseDataUtils.error(ResponseEnum.SERVER_ERROR);
}
//参数校验异常处理
private BaseResponseVo getValidExceptionResult(List<ObjectError> objectErrors) {
StringBuilder sb = new StringBuilder();
for (ObjectError error : objectErrors) {
sb.append(error.getDefaultMessage()).append(";");
}
String message = sb.length() > 0 ? sb.toString().substring(0, sb.length() - 1) : sb.toString();
return ResponseDataUtils.error(ResponseEnum.BAD_REQUEST_PARAMETER, message);
}
}