springboot统一接口返回数据 springboot统一接口返回数据的实现
liangshui999 人气:4一,没有异常的情况,正常返回数据
希望接口统一返回的数据格式如下:
{ "status": 0, "msg": "成功", "data": null }
和接口数据对应的bean
/** * 统一返回结果的实体 * @param <T> */ public class Result<T> implements Serializable { private static final long serialVersionUID = 1L; /** * 错误码 */ private int status; /** * 提示消息 */ private String msg; /** * 返回的数据体 */ private T data; public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
操作Result实体的工具类
/** * 生成result的工具类,避免重复代码 */ public class ResultUtils { /** * 成功时生成result的方法,有返回数据 */ public static <T> Result<T> success(T t){ Result<T> result = new Result<>(); result.setStatus(ResultEnum.SUCCESS.getCode()); result.setMsg(ResultEnum.SUCCESS.getMsg()); result.setData(t); return result; } /** * 成功时生成result的方法,无返回数据 */ public static <T> Result<T> success(){ return success(null); } /** * 失败时生成result的方法 */ public static <T> Result<T> error(int status, String msg){ Result<T> result = new Result<>(); result.setStatus(status); result.setMsg(msg); return result; } }
封装错误码和错误消息的枚举类
/** * 所有返回结果的枚举 */ public enum ResultEnum { UNKNOWN_ERROR(-1, "未知错误"), SUCCESS(0, "成功"), BASIC_INFO_ID_IS_EMPTY(600, "基本信息中BasicInfoId为空"), BASIC_INFO_ADD_TO_DATABASE_FAILURE(601, "向数据库添加基本信息失败"), DETAILS_DATA_BASIC_INFO_ID_IS_EMPTY(602, "测试数据中BasicInfoId为空"), DETAILS_DATA_ADD_TO_DATABASE_FAILURE(603, "向数据库添加测试数据失败"); ResultEnum(int code, String msg) { this.code = code; this.msg = msg; } private int code; private String msg; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @Override public String toString() { return "ResultEnum{" + "code=" + code + ", msg='" + msg + '\'' + '}'; } }
统一封装返回结果的切面
之所以需要这个切面,是为了避免每个Controller方法中都要调用ResultUtils.success()。有了这个切面,Controller可以和原来一样正常返回对象,字符串,void,在切面里面将结果封装成Result实体,而不需要每个Controller方法都返回Result实体。
/** * 统一处理返回结果的切面,避免每个controller方法里面都要调用ResultUtils.success()这句话 * 统一在这个切面里面调用 */ @ControllerAdvice public class MyResponseAdvice implements ResponseBodyAdvice<Object> { @Autowired private ObjectMapper objectMapper; /** * 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) { 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) { if(body instanceof Result){ //发生异常之后,异常处理器里面返回的已经是Result了 return body; }else if(body instanceof String){ //String属于特殊情况,需要单独处理,否则会报错 try { return objectMapper.writeValueAsString(ResultUtils.success(body)); } catch (JsonProcessingException e) { e.printStackTrace(); return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), e.getMessage()); } } return ResultUtils.success(body); } }
二,有异常的情况下
service层为了自动回滚事务,会抛出一些自定义的RuntimeException。默认情况下,只有RuntimeException才会回滚事务。如果Controller里面直接处理service层抛出的异常,则Controller里面到处都是try catch块,代码会很难看。将异常集中在一个地方处理会好很多。
springboot中是通过@ControllerAdvice和@ExceptionHandler来完成统一异常处理的。这2个注解只能处理Controller和拦截器中抛出的异常,其他地方抛出的异常(比如Filter中抛出的异常),无法捕获。其他地方抛出的异常会转到/error的Controller方法来处理,默认是BasicErrorController来处理,为了能处理其他地方抛出的异常,我们会自定义ErrorController。
统一的异常处理类,处理Controller和拦截器抛出的异常
/** * 统一的异常处理类 */ @ControllerAdvice public class MyExceptionHandler { /** * 转发到/error,表示由BasicErrorController处理, * BasicErrorController是由springboot自动装配到容器中的 */ /*@ExceptionHandler(BasicInfoException.class) public String handleException(Exception ex, HttpServletRequest request){ request.setAttribute("javax.servlet.error.status_code", 401); request.setAttribute("exMsg", ex.getMessage()); return "forward:/error"; }*/ /** * 处理基本信息相关的异常 */ @ExceptionHandler(BasicInfoException.class) @ResponseBody public Result handleBasicInfoException(BasicInfoException ex){ return ResultUtils.error(ex.getCode(), ex.getMessage()); } /** * 处理测试数据相关的异常 */ @ExceptionHandler(DetailsDataException.class) @ResponseBody public Result handleDetailsDataException(DetailsDataException ex){ return ResultUtils.error(ex.getCode(), ex.getMessage()); } /** * 处理未知异常 */ @ExceptionHandler(Exception.class) @ResponseBody public Result handleUnKnowException(Exception ex){ return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), ex.getMessage()); } }
自定义的异常类示例
public class BasicInfoException extends RuntimeException { private int code; public BasicInfoException(int code, String msg){ super(msg); this.code = code; } public int getCode() { return code; } }
处理其他地方抛出的异常(不是Controller和拦截器抛出的异常),自定义ErrorController
/** * 自定义ErrorController,处理其他地方抛出的异常(不是Controller和拦截器抛出的异常) */ @Controller public class MyBasicErrorController extends AbstractErrorController { private Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 可以通过@Value获取到 */ @Value("${server.error.path}") private String myPath; private final ErrorProperties errorProperties; private ErrorAttributes mErrorAttributes; public MyBasicErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) { super(errorAttributes); this.errorProperties = serverProperties.getError(); this.mErrorAttributes = errorAttributes; } //@RequestMapping(value = "/error") @RequestMapping("${server.error.path}") //从properties文件中获取 @ResponseBody public Result<Object> error(HttpServletRequest request) throws Throwable { logger.debug("myPath = " + myPath); //发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理 WebRequest webRequest = new ServletWebRequest(request); Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause(); throw throwable; /*UserException ex; if(throwable instanceof UserException){ ex = (UserException) throwable; throw ex; }else{ throw throwable; }*/ /*HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return ResultUtils.error(status.value(), status.name()); } Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/ } /** * Determine if the stacktrace attribute should be included. * @param request the source request * @param produces the media type produced (or {@code MediaType.ALL}) * @return if the stacktrace attribute should be included */ private boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) { ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace(); if (include == ErrorProperties.IncludeStacktrace.ALWAYS) { return true; } if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) { return getTraceParameter(request); } return false; } /** * Provide access to the error properties. * @return the error properties */ private ErrorProperties getErrorProperties() { return this.errorProperties; } /** * Returns the path of the error page. * * @return the error path */ @Override public String getErrorPath() { return this.errorProperties.getPath(); } }
自定义ErrorController中错误处理的方法中,也可以直接将异常抛出,这样异常就会交给统一异常处理器进行处理。
//@RequestMapping(value = "/error") @RequestMapping("${server.error.path}") //从properties文件中获取 @ResponseBody public Result<Object> error(HttpServletRequest request) throws Throwable { logger.debug("myPath = " + myPath); //发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理 WebRequest webRequest = new ServletWebRequest(request); Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause(); UserException ex; if(throwable instanceof UserException){ ex = (UserException) throwable; throw ex; }else{ throw throwable; } /*HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return ResultUtils.error(status.value(), status.name()); } Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/ }
加载全部内容