美文网首页
Springboot 统一异常处理和统一返回

Springboot 统一异常处理和统一返回

作者: 渐渐_薇薇 | 来源:发表于2020-05-27 15:49 被阅读0次

    实现对controller层的统一异常处理和统一的接口响应返回,以及加入参数校验
    整体项目层级:


    image.png

    1、定义接口响应返回

    public class CommonResult<T> {
    
        private long code;
    
        private String message;
    
        private T data;
    
        public long getCode() {
            return code;
        }
    
        public void setCode(long code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
        protected CommonResult() {
        }
    
        protected CommonResult(long code, String message, T data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }
    
        /**
         * 成功返回结果
         *
         * @param data 获取的数据
         */
        public static <T> CommonResult<T> success(T data) {
            return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
        }
    
        /**
         * 成功返回结果
         *
         * @param data 获取的数据
         * @param  message 提示信息
         */
        public static <T> CommonResult<T> success(T data, String message) {
            return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
        }
    
        /**
         * 失败返回结果
         * @param errorCode 错误码
         */
        public static <T> CommonResult<T> failed(IErrorCode errorCode) {
            return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
        }
    
        /**
         * 失败返回结果
         * @param errorCode 错误码
         * @param message 错误信息
         */
        public static <T> CommonResult<T> failed(IErrorCode errorCode,String message) {
            return new CommonResult<T>(errorCode.getCode(), message, null);
        }
    
        /**
         * 失败返回结果
         * @param message 提示信息
         */
        public static <T> CommonResult<T> failed(String message) {
            return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
        }
    
        /**
         * 失败返回结果
         */
        public static <T> CommonResult<T> failed() {
            return failed(ResultCode.FAILED);
        }
    
        /**
         * 参数验证失败返回结果
         */
        public static <T> CommonResult<T> validateFailed() {
            return failed(ResultCode.VALIDATE_FAILED);
        }
    
        /**
         * 参数验证失败返回结果
         * @param message 提示信息
         */
        public static <T> CommonResult<T> validateFailed(String message) {
            return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
        }
    
        /**
         * 未登录返回结果
         */
        public static <T> CommonResult<T> unauthorized(T data) {
            return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
        }
    
        /**
         * 未授权返回结果
         */
        public static <T> CommonResult<T> forbidden(T data) {
            return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
        }
    }
    
    

    2、封装错误码

    public interface IErrorCode {
        long getCode();
    
        String getMessage();
    }
    

    3、返回错误码统一定义

    public enum ResultCode implements IErrorCode {
        SUCCESS(200, "操作成功"),
        FAILED(500, "操作失败"),
        VALIDATE_FAILED(404, "参数检验失败"),
        UNAUTHORIZED(401, "暂未登录或token已经过期"),
        FORBIDDEN(403, "没有相关权限");
        private long code;
        private String message;
    
        private ResultCode(long code, String message) {
            this.code = code;
            this.message = message;
        }
        @Override
        public long getCode() {
            return code;
        }
        @Override
        public String getMessage() {
            return message;
        }
    }
    

    4、自定义异常

    public class ApiException extends RuntimeException {
        private IErrorCode errorCode;
    
        public ApiException(IErrorCode errorCode) {
            super(errorCode.getMessage());
            this.errorCode = errorCode;
        }
    
        public ApiException(String message) {
            super(message);
        }
    
        public ApiException(Throwable cause) {
            super(cause);
        }
    
        public ApiException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public IErrorCode getErrorCode() {
            return errorCode;
        }
    }
    

    5、断言处理类,用于抛出各种API异常

    public class Asserts {
    
        public static void fail(String message) {
            throw new ApiException(message);
        }
    
        public static void fail(IErrorCode errorCode) {
            throw new ApiException(errorCode);
        }
    }
    

    6、全局异常处理

    @ControllerAdvice
    public class GlobalExceptionHandler {
        @ResponseBody
        @ExceptionHandler(value = ApiException.class)
        public CommonResult handle(ApiException e) {
            if (e.getErrorCode() != null) {
                return CommonResult.failed(e.getErrorCode());
            }
            return CommonResult.failed(e.getMessage());
        }
    }
    

    7、日志切面

    @Aspect
    @Component
    @Order(1)
    public class WebLogAspect {
        private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);
    
        @Pointcut("execution(public * com.example.portal.controller.*.*(..))")
        public void webLog() {
        }
    
        @Before("webLog()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
        }
    
        @AfterReturning(value = "webLog()", returning = "ret")
        public void doAfterReturning(Object ret) throws Throwable {
        }
    
        @Around("webLog()")
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            long startTime = System.currentTimeMillis();
            //获取当前请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            WebLog webLog = new WebLog();
            Object result = joinPoint.proceed();
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method method = methodSignature.getMethod();
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation log = method.getAnnotation(ApiOperation.class);
                webLog.setDescription(log.value());
            }
            long endTime = System.currentTimeMillis();
            String urlStr = request.getRequestURL().toString();
            webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
            webLog.setIp(request.getRemoteUser());
            webLog.setMethod(request.getMethod());
            webLog.setParameter(getParameter(method, joinPoint.getArgs()));
            webLog.setResult(result);
            webLog.setSpendTime((int) (endTime - startTime));
            webLog.setStartTime(startTime);
            webLog.setUri(request.getRequestURI());
            webLog.setUrl(request.getRequestURL().toString());
            Map<String,Object> logMap = new HashMap<>();
            logMap.put("url",webLog.getUrl());
            logMap.put("method",webLog.getMethod());
            logMap.put("parameter",webLog.getParameter());
            logMap.put("spendTime",webLog.getSpendTime());
            logMap.put("description",webLog.getDescription());
            LOGGER.info("{}", JSONUtil.parse(webLog));
            return result;
        }
    
        /**
         * 根据方法和传入的参数获取请求参数
         */
        private Object getParameter(Method method, Object[] args) {
            List<Object> argList = new ArrayList<>();
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                //将RequestBody注解修饰的参数作为请求参数
                RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
                if (requestBody != null) {
                    argList.add(args[i]);
                }
                //将RequestParam注解修饰的参数作为请求参数
                RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
                if (requestParam != null) {
                    Map<String, Object> map = new HashMap<>();
                    String key = parameters[i].getName();
                    if (!StringUtils.isEmpty(requestParam.value())) {
                        key = requestParam.value();
                    }
                    map.put(key, args[i]);
                    argList.add(map);
                }
            }
            if (argList.size() == 0) {
                return null;
            } else if (argList.size() == 1) {
                return argList.get(0);
            } else {
                return argList;
            }
        }
    }
    
    public class WebLog {
        /**
         * 操作描述
         */
        private String description;
    
        /**
         * 操作用户
         */
        private String username;
    
        /**
         * 操作时间
         */
        private Long startTime;
    
        /**
         * 消耗时间
         */
        private Integer spendTime;
    
        /**
         * 根路径
         */
        private String basePath;
    
        /**
         * URI
         */
        private String uri;
    
        /**
         * URL
         */
        private String url;
    
        /**
         * 请求类型
         */
        private String method;
    
        /**
         * IP地址
         */
        private String ip;
    
        private Object parameter;
    
        private Object result;
    
        public String getDescription() {
            return description;
        }
    
        public void setDescription(String description) {
            this.description = description;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Long getStartTime() {
            return startTime;
        }
    
        public void setStartTime(Long startTime) {
            this.startTime = startTime;
        }
    
        public Integer getSpendTime() {
            return spendTime;
        }
    
        public void setSpendTime(Integer spendTime) {
            this.spendTime = spendTime;
        }
    
        public String getBasePath() {
            return basePath;
        }
    
        public void setBasePath(String basePath) {
            this.basePath = basePath;
        }
    
        public String getUri() {
            return uri;
        }
    
        public void setUri(String uri) {
            this.uri = uri;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getMethod() {
            return method;
        }
    
        public void setMethod(String method) {
            this.method = method;
        }
    
        public String getIp() {
            return ip;
        }
    
        public void setIp(String ip) {
            this.ip = ip;
        }
    
        public Object getParameter() {
            return parameter;
        }
    
        public void setParameter(Object parameter) {
            this.parameter = parameter;
        }
    
        public Object getResult() {
            return result;
        }
    
        public void setResult(Object result) {
            this.result = result;
        }
    }
    
    

    8、校验参数切面

    @Aspect
    @Component
    @Order(2)
    public class BindingResultAspect {
        @Pointcut("execution(public * com.example.portal.controller.*.*(..))")
        public void BindingResult() {
        }
    
        @Around("BindingResult()")
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            Object[] args = joinPoint.getArgs();
            for (Object arg : args) {
                if (arg instanceof BindingResult) {
                    BindingResult result = (BindingResult) arg;
                    if (result.hasErrors()) {
                        FieldError fieldError = result.getFieldError();
                        if(fieldError!=null){
                            return CommonResult.validateFailed(fieldError.getDefaultMessage());
                        }else{
                            return CommonResult.validateFailed();
                        }
                    }
                }
            }
            return joinPoint.proceed();
        }
    }
    

    9、controller
    校验参数需要添加@Valid 注解和BindingResult对象

    @RestController
    @Slf4j
    public class TestController {
    
        @Autowired
        private TestService testService;
        @RequestMapping(value = "/testQuery", method = RequestMethod.POST)
        @ResponseBody
        public CommonResult testQuery(@RequestBody @Valid TestQueryParam testQueryParam, BindingResult result) {
            Map rsMap=testService.testQuery(testQueryParam.getUsername());
            return CommonResult.success(rsMap);
        }
    }
    
    @Data
    public class TestQueryParam {
        @ApiModelProperty(value = "用户名", required = true)
        @NotEmpty(message = "用户名不能为空")
        private String username;
        @ApiModelProperty(value = "密码", required = true)
        @NotEmpty(message = "密码不能为空")
        private String password;
    }
    

    10、测试结果


    image.png

    异常处理:


    image.png

    相关文章

      网友评论

          本文标题:Springboot 统一异常处理和统一返回

          本文链接:https://www.haomeiwen.com/subject/ffwwahtx.html