一,统一返回数据结构
- 定义返回的数据结构
package com.futao.springmvcdemo.model.system; import org.joda.time.DateTime; import java.sql.Timestamp; /** * 统一返回Rest风格的数据结构 * * @author futao * Created on 2018/9/22-21:47. */ public class RestResult { /** * 请求是否成功(这个字段不需要,可以不加) */ private boolean success; /** * 成功或者失败的code错误码 */ private String code; /** * 成功时返回的数据,失败时返回具体的异常信息 */ private Object data; /** * 请求失败返回的提示信息,给前端进行页面展示的信息 */ private Object errorMessage; /** * 服务器当前时间(添加该字段的原因是便于查找定位请求时间,因为实际开发过程中服务器时间可能跟本地时间不一致,加上这个时间戳便于日后定位) */ private Timestamp currentTime; public RestResult() { } @Override public String toString() { return "RestResult{" + "success=" + success + ", code='" + code + '\'' + ", data=" + data + ", errorMessage=" + errorMessage + ", currentTime=" + currentTime + '}'; } public RestResult(boolean success, String code, Object data, Object errorMessage) { this.success = success; this.code = code; this.data = data; this.errorMessage = errorMessage; this.currentTime = new Timestamp(new DateTime().getMillis()); } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Object getErrorMessage() { return errorMessage; } public void setErrorMessage(Object errorMessage) { this.errorMessage = errorMessage; } public Timestamp getCurrentTime() { return currentTime; } public void setCurrentTime(Timestamp currentTime) { this.currentTime = currentTime; } }
- 将返回数据包装成Rest风格
实现ResponseBodyAdvice<T>
package com.west.lake.blog.foundation; import com.lazy.rest.rest.RestResult; import com.west.lake.blog.annotation.RestSkip; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import springfox.documentation.swagger.web.ApiResourceController; import springfox.documentation.swagger2.web.Swagger2Controller; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 返回Rest风格的数据 * * @author futao * Created on 2018/9/22-20:24. */ @RestControllerAdvice public class RestResultWrapper implements ResponseBodyAdvice<Object> { /** * 不需要拦截的类路径,这里写的是Class * 如果该类所在项目没有相关的依赖,可以换成String-类的全路径 */ private static final List<Class<?>> SKIP_CLASS_LIST = new ArrayList<>(2); static { //Swagger SKIP_CLASS_LIST.add(ApiResourceController.class); //Swagger SKIP_CLASS_LIST.add(Swagger2Controller.class); } /** * 可指定针对某些返回值的类型才进行rest风格的封装 * * @param returnType 返回值类型 * @param converterType * @return */ @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { if (SKIP_CLASS_LIST.contains(returnType.getDeclaringClass())) { return false; } Method returnTypeMethod = returnType.getMethod(); if (returnTypeMethod != null) { return !returnTypeMethod.isAnnotationPresent(RestSkip.class); } return true; } /** * 封装正常返回的数据 * * @param body * @param returnType * @param selectedContentType * @param selectedConverterType * @param request * @param response * @return */ @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (MediaType.IMAGE_JPEG.getType().equalsIgnoreCase(selectedContentType.getType())) { return body; } if (body instanceof RestResult) { return body; } return new RestResult(RestResult.SUCCESS_CODE, body, null); } }
- 请求测试
@GetMapping("get") public User get() { User user = new User(); user.setUsername("NiuBist"); user.setId("123"); user.setAge("18"); user.setEmail("12312@qq.com"); user.setMobile("12312321312"); user.setAddress("浙江省杭州市"); return user; }
结果:
二,统一异常处理,返回统一异常数据结构
- 定义一个类来存放所以的异常提示信息,便于管理
package com.futao.springmvcdemo.model.entity.constvar; /** * 错误提示集合类 * 错误码构成: 01程序员编号 * 001该程序员定义的错误码 * 后面再跟上错误信息 * * @author futao * Created on 2018/9/21-15:29. */ public final class ErrorMessage { public static final String SYSTEM_EXCEPTION = "系统繁忙,请稍后再试"; public static final String NOT_LOGIN = "01001_您还未登陆或者登陆已超时,请重新登陆"; public static final String MOBILE_ALREADY_REGISTER = "01002_该手机号已经被注册了"; public static final String LOGIC_EXCEPTION = "01003_对不起,你是真的没有我帅"; }
- 定义逻辑异常类
package com.futao.springmvcdemo.foundation; /** * 业务逻辑异常类、 * * @author futao * Created on 2018/9/20-15:22. */ public class LogicException extends RuntimeException { /** * 异常信息 */ private String errorMsg; /** * 错误码 */ private String code; public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } private LogicException(String errorMsg) { super(errorMsg); this.code = errorMsg.substring(0, 5); this.errorMsg = errorMsg.substring(6); } /** * 抛出逻辑异常 * * @param errorMsg * @return */ public static LogicException le(String errorMsg) { return new LogicException(errorMsg); } }
- 对异常(包括系统异常与业务逻辑异常)进行统一处理
package com.futao.springmvcdemo.foundation; import com.alibaba.fastjson.JSONObject; import com.futao.springmvcdemo.model.entity.constvar.ErrorMessage; import com.futao.springmvcdemo.model.system.RestResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 异常统一处理, * * @author futao * Created on 2018/9/21-15:13. */ @ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(value = Exception.class) @ResponseBody public Object logicExceptionHandler(HttpServletRequest request, Exception e, HttpServletResponse response) { //系统级异常,错误码固定为-1,提示语固定为系统繁忙,请稍后再试 RestResult result = new RestResult(false, "-1", e.getMessage(), ErrorMessage.SYSTEM_EXCEPTION); //如果是业务逻辑异常,返回具体的错误码与提示信息 if (e instanceof LogicException) { LogicException logicException = (LogicException) e; result.setCode(logicException.getCode()); result.setErrorMessage(logicException.getErrorMsg()); } else { //对系统级异常进行日志记录 logger.error("系统异常:" + e.getMessage(), e); } return JSONObject.toJSON(result); } }
- 测试
package com.futao.springmvcdemo.controller; import com.futao.springmvcdemo.foundation.LogicException; import com.futao.springmvcdemo.model.entity.constvar.ErrorMessage; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author futao * Created on 2018/9/23-0:28. * 统一异常处理测试接口 */ @RequestMapping(path = "exception", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) @RestController public class ExceptionTestController { /** * 业务逻辑异常 */ @GetMapping(path = "logicException") public void logicException() { throw LogicException.le(ErrorMessage.LOGIC_EXCEPTION); } /** * 系统异常 */ @GetMapping(path = "systemException") public void systemException() { throw new NullPointerException("空指针了,哥门!!!"); } }