先整体看下代码结构:
返回结果封装类RestResult:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RestResult {
private Integer code;
private String message;
private Object data;
public static RestResult of(Object data) {
return new RestResult(0, "success", data);
}
public static RestResult of(Exception e) {
if (e instanceof BusinessException) {
return new RestResult(((BusinessException) e).getCodeId(), e.getMessage(), ((BusinessException) e).getErrors());
} else {
return new RestResult(500, e.getMessage(), Collections.emptyMap());
}
}
public static RestResult of(Exception e, Integer code) {
if (e instanceof BusinessException) {
return new RestResult(((BusinessException) e).getCodeId(), e.getMessage(), ((BusinessException) e).getErrors());
} else {
return new RestResult(code, e.getMessage(), Collections.emptyMap());
}
}
}
统一异常封装类:
/**
* @description: 对于需要给用户展示的异常,统一抛出此异常
* @author: JJC
* @createTime: 2019/3/28
*/
public class BusinessException extends RuntimeException implements Serializable {
private final ErrorCode code;
private final Map<String, ? extends Object> errors;
public BusinessException(ErrorCode code, Map<String, Object> errors) {
super(code.getReason());
this.code = code;
this.errors = errors;
}
public BusinessException(String message, ErrorCode code, Map<String, ? extends Object> errors) {
super(message);
this.code = code;
this.errors = errors;
}
public BusinessException(String message, Throwable cause, ErrorCode code, Map<String, ? extends Object> errors) {
super(message, cause);
this.code = code;
this.errors = errors;
}
public BusinessException(Throwable cause, ErrorCode code, Map<String, ? extends Object> errors) {
super(cause.getMessage(), cause);
this.code = code;
this.errors = errors;
}
public BusinessException(ErrorCode code) {
super(code.getReason());
this.code = code;
this.errors = new HashMap<>();
}
public BusinessException(String message, ErrorCode code) {
super(message);
this.code = code;
this.errors = new HashMap<>();
}
public BusinessException(String message, Throwable cause, ErrorCode code) {
super(message, cause);
this.code = code;
this.errors = cause instanceof ServiceException ? ((ServiceException) cause).getErrors() : new HashMap<>();
}
public BusinessException(Throwable cause, ErrorCode code) {
super(cause.getMessage(), cause);
this.code = code;
this.errors = cause instanceof ServiceException ? ((ServiceException) cause).getErrors() : new HashMap<>();
}
public ErrorCode getCode() {
return this.code;
}
@Override
public String toString() {
Map<String, Object> map = new HashMap<>();
map.put("httpStatus", this.getCode().getHttpStatus());
map.put("code", this.getCode().getCode());
map.put("reason", this.getCode().getReason());
map.put("solution", this.getCode().getSolution());
map.put("message", this.getMessage());
return JSONObject.toJSONString(map, SerializerFeature.WriteMapNullValue);
}
public Map<String, ? extends Object> getErrors() {
return this.errors;
}
public Integer getCodeId() {
return this.code.getCode();
}
}
异常枚举:
@Getter
public enum ErrorCode {
// 通用
METHOD_ARGUMENT_NOT_VALID(HttpStatus.BAD_REQUEST.value(),HttpStatus.BAD_REQUEST.value(),"参数约束错误","调整参数");
private Integer httpStatus;
private Integer code;
private String reason;
private String solution;
ErrorCode(Integer httpStatus, Integer code, String reason, String solution) {
// FIXME: 自定义异常的 Http Status 统一为 200
this.httpStatus = HttpStatus.OK.value();
// 错误码的前缀为期望的HttpStatus
this.code = code;
this.reason = reason;
this.solution = solution;
}
}
统一返回值处理:
@ControllerAdvice(annotations = RestController.class)
public class ResultHandlerAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType mediaType, Class converterType, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (null == body) {
return RestResult.of(Collections.emptyMap());
}
return RestResult.of(body);
}
}
统一异常处理:
@ControllerAdvice(annotations = RestController.class)
public class PandaExceptionHandler extends ResponseEntityExceptionHandler {
private static Logger logger = LoggerFactory.getLogger(PandaExceptionHandler.class);
/**
* 处理业务异常
*/
@ExceptionHandler(value = Exception.class)
protected ResponseEntity<Object> handleException(Exception exception, HttpServletRequest request, HttpServletResponse response) {
this.logError(exception, request.getRequestURI(), request.getParameterMap());
RestResult errorBody = RestResult.of(exception);
ResponseEntity<Object> responseEntity = new ResponseEntity<>(errorBody, HttpStatus.INTERNAL_SERVER_ERROR);
return responseEntity;
}
private void logError(Exception exception, String uri, Map<String, String[]> parameterMap) {
logger.error("request error.request url:" + uri + ". params:" + JSONObject.toJSON(parameterMap).toString() + ". traceId:" + Tracer.id());
logger.error(exception.toString(), exception);
}
@Override
protected ResponseEntity<Object> handleExceptionInternal(Exception exception, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
this.logError(exception, ((ServletWebRequest) request).getRequest().getRequestURI(), request.getParameterMap());
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
request.setAttribute("javax.servlet.error.exception", exception, 0);
}
RestResult errorBody = RestResult.of(exception, status.value());
return new ResponseEntity(errorBody, headers, status);
}
/**
* 处理javax.validation参数校验不通过异常
*/
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException exception, HttpHeaders headers, HttpStatus status, WebRequest request) {
Map<String, Object> errors = new HashMap<>();
for (ObjectError error : exception.getBindingResult().getAllErrors()) {
errors.put(((FieldError) error).getField(), error.getDefaultMessage());
}
BusinessException businessException = new BusinessException(ErrorCode.METHOD_ARGUMENT_NOT_VALID, errors);
return this.handleException(businessException, RequestContextHolderUtil.getRequest(), RequestContextHolderUtil.getResponse());
}
}
有个坑需要注意,因为统一对@RestController进行了拦截,因此对于健康检查等返回String的Controller会抛异常,因此对于健康检查等返回String的Controller使用@Controller与@ResponseBody代替@RestController即可