这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战
封装返回格式
-
规范的后端返回格式在携带数据的同时,往往附带状态码、错误信息、其他 message 等
{ "code": "200", "msg": "执行成功", "data": [ { "name": "张三", "age": 18 }, { "name": "李四", "age": 19 } ] }
-
ResultStatus 请求状态枚举类
public enum ResultStatus { SUCCESS("200", "执行成功"), ERROR("500", "执行出错"), TOKEN_ERROR("400", "Token 错误"), NOT_EXIST("404", "目标资源不存在"), ; private String code; private String msg; ResultStatus(String code, String msg) { this.code = code; this.msg = msg; } public String getCode() { return code; } public String getMsg() { return msg; } }
-
BaseException 自定义异常类
public class BaseException extends RuntimeException { private String code; private String msg; public BaseException(ResultStatus status) { super(status.getMsg()); this.code = status.getCode(); this.msg = status.getMsg(); } public String getCode() { return code; } public String getMsg() { return msg; } }
-
封装 Result 类用来包装返回内容
public class Result { // 状态码 private String code; // 状态信息 private String msg; // 主要数据 @JsonInclude(JsonInclude.Include.NON_NULL) private Object data; private Result(String code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; } public String getCode() { return code; } public String getMsg() { return msg; } public Object getData() { return data; } /** * 构造自定义的消息返回 * * @param code * @param msg * @param data * @return */ public static Result of(String code, String msg, Object data) { return new Result(code, msg, data); } /** * 由 ResultStatus 构造消息返回 * * @param status * @param data * @return */ public static Result ofStatus(ResultStatus status, Object data) { return of(status.getCode(), status.getMsg(), data); } /** * 构造成功的消息返回 * * @param data * @return */ public static Result success(Object data) { return ofStatus(ResultStatus.SUCCESS, data); } /** * 由 Exception 构造消息返回 * * @param exception * @param data * @return */ public static Result ofException(Exception exception, Object data) { String code = exception instanceof BaseException ? ((BaseException) exception).getCode() : ResultStatus.ERROR.getCode(); String msg = exception != null ? "[" + exception.getClass() + "]" + exception.getMessage() : ResultStatus.ERROR.getMsg(); return of(code, msg, data); } }
统一处理异常
-
使用 @ControllerAdvice 和 @ExceptionHandler 统一处理异常
@ControllerAdvice public class GlobalExceptionHandler { @ResponseBody @ExceptionHandler(value = Exception.class) public Result handleException(Exception e) { return Result.ofException(e, null); } }
-
@ExceptionHandler 方法接收一个异常对象,可获取异常相关信息
-
Contoller 层不需要主动捕获异常,直接抛出即可
统一返回格式
-
使用 @ControllerAdvice,实现 ResponseBodyAdvice 接口
@ControllerAdvice public class CommonResponseHandler implements ResponseBodyAdvice { private ObjectMapper objectMapper = new ObjectMapper(); @Override public boolean supports(MethodParameter returnType, Class converterType) { // 返回 true 则 beforeBodyWrite 方法生效 return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body instanceof Result) { return body; } // String 类型特殊处理 if (body instanceof String) { try { // 将 Result 转为 json 字符串返回 return objectMapper.writeValueAsString(Result.success(body)); } catch (JsonProcessingException e) { e.printStackTrace(); } } return Result.success(body); } }
-
@ControllerAdvice 需要指定生效的包,否则容易影响其他功能,如 swagger 文档
处理 404 异常
-
上述方法无法捕捉 404 异常,可在 application.xml 进行如下配置
# 出现错误时, 直接抛出异常 spring.mvc.throw-exception-if-no-handler-found=true # 不要为我们工程中的资源文件建立映射 spring.resources.add-mappings=false
为什么这样配置:
-
throw-exception-if-no-handler-found 用来决定是否抛出 NoHandlerFoundException
-
SpringBoot 默认配置了
/**
的资源映射,即使地址错误也会匹配到静态资源地址
-
-
通过实现 ErrorController 接口解决
高版本的 SpringBoot 已经弃用了 getErrorPath 方法,但 ErrorController 接口还是有效的
@Controller public class NotFoundExceptionHandler implements ErrorController { @Override public String getErrorPath() { return "/error"; } @RequestMapping("/error") @ResponseBody public Object error(HttpServletRequest request) { return Result.ofStatus(ResultStatus.NOT_EXIST, null); } }
-
通过实现 ErrorAttributes 接口解决
可以继承 DefaultErrorAttributes,也可直接实现 ErrorAttributes
DefaultErrorAttributes 已经对异常信息进行了封装,更加容易使用
@Component public class SimpleExceptionHandler extends DefaultErrorAttributes { private static final String STATUS_KEY = "status"; private static final String PATH_KEY = "path"; private static final String ERROR_KEY = "error"; @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { // 获取填充好的 errorAttributes Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options); // 获取状态码与路径 Integer status = (Integer) errorAttributes.get(STATUS_KEY); String path = (String) errorAttributes.get(PATH_KEY); String error = (String) errorAttributes.get(ERROR_KEY); // 拼接错误信息 String msg = "[" + path + "]" + error; // 处理 404 异常 if (status == HttpStatus.NOT_FOUND.value()) { // return ... } // 处理 405 异常 if (status == HttpStatus.METHOD_NOT_ALLOWED.value()) { // return ... } // return ... } }