美文网首页
spring boot数据通用返回格式和全局异常处理

spring boot数据通用返回格式和全局异常处理

作者: Hogwarts1024 | 来源:发表于2018-10-21 20:31 被阅读0次

    在项目中我们通常会有一个通用的数据返回格式来返回给前端

    首先定义了一个通用返回格式

    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @Description: 通用返回类
     */
    
    @Data
    public class CommonResult implements Serializable {
    
        /**
         * 0000为返回正常, 其它code均为请求错误
         */
        private String code;
    
        /**
         * 返回数据
         */
        private Object data;
    
        /**
         * 错误信息
         */
        private String message;
    
        public CommonResult() {
            this.code = "0000";
            this.message = "";
        }
    
        public CommonResult(Object data) {
            this();
            this.data = data;
        }
    
        public CommonResult(String message) {
            this.code = "9999";
            this.message = message;
        }
    
        public CommonResult(String code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public CommonResult(ErrorCode errorCode) {
            this.code = errorCode.getCode();
            this.message = errorCode.getMessage();
        }
    }
    
    

    有些异常要特殊处理所以定义了一个ErrorCode枚举

    import lombok.Getter;
    
    /**
     * @Description: 业务通用异常代码
     */
    
    @Getter
    public enum ErrorCode {
    
        SUCCESS("0000", "success"),
        SERVER_ERROR("9999", "system error"),
    
        REQUEST_ERROR("400", "请求错误"),
        UNAUTHORIZED("401", "未授权"),
        NOT_ACCESSIBLE("403", "不可访问"),
        METHOD_NOT_ALLOWED("405", "方法不被允许"),
        UNSUPPORTED_MEDIA_TYPE("415", "不支持当前媒体类型"),
    
    
        TOKEN_LOSE_EFFICACY("1001","您的登录令牌已失效,请重新登录"),
        ;
    
        private String code;
    
        private String message;
    
        ErrorCode(String code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public static ErrorCode getByCode(String code) {
    
            for (ErrorCode errorCode : ErrorCode.values()) {
                if (errorCode.getCode().equals(code)) {
                    return errorCode;
                }
            }
    
            return null;
        }
    }
    

    接下来对返回的格式做一层封装方便调用

    /**
     * @Description: 返回类封装
     */
    public class CommonResultTemplate {
    
        public static CommonResult execute(Callback callback) {
            CommonResult result;
    
            try {
                result = new CommonResult();
                result.setData(callback.execute());
    
            } catch (CommonException e) {
    
                LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[3].getClassName()).debug("business " +
                        "error", e);
    
                if (e.getErrorCode() != null) {
                    result = new CommonResult(e.getErrorCode());
                } else {
                    result = new CommonResult(e.getMessage());
                }
            } catch (Exception e) {
    
                LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[3].getClassName()).debug("business " +
                        "error", e);
    
                result = new CommonResult(ErrorCode.SERVER_ERROR);
            }
    
            return result;
        }
    
        public interface Callback {
            Object execute();
        }
    }
    

    通过以上封装就可以在controller层用一行代码调用

    public CommonResult getUserInfo() {
        return CommonResultTemplate.execute(()->userService.getCurrentUser());
    }
    

    但是当请求没有进入controller层,比如发生401,403等请求错误时就无法返回这个通过格式,这时候就得对全局异常进行处理

    在springboot中用ErrorController接口就可以了

    import com.alibaba.fastjson.JSON;
    import io.jsonwebtoken.JwtException;
    import org.springframework.boot.autoconfigure.web.ErrorController;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.WebAttributes;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.util.WebUtils;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**
     * 全局异常处理
     */
    @Controller
    public class GlobalErrorController implements ErrorController {
    
        //url不能替换
        @RequestMapping("/error")
        @ResponseBody
        public static void error(HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
    
            Throwable error = (Throwable) request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE);
            if (null == error) {
                error = (Throwable) request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
            }
            if (null == error) {
                error = (Throwable) request.getAttribute(WebAttributes.ACCESS_DENIED_403);
            }
    
            CommonResult result;
            if (error instanceof JwtException) {
                result = new CommonResult(ErrorCode.UNAUTHORIZED);
            } else if (error instanceof AuthenticationException || error instanceof AccessDeniedException) {
                result = new CommonResult(ErrorCode.NOT_ACCESSIBLE);
            } else if (error instanceof MethodArgumentNotValidException) {
                result = new CommonResult(ErrorCode.REQUEST_ERROR);
            } else if (error instanceof CommonException) {
                result = new CommonResult(((CommonException) error).getErrorCode());
            } else {
                result = new CommonResult(ErrorCode.SERVER_ERROR);
            }
    
            PrintWriter out = response.getWriter();
            out.print(JSON.toJSONString(result));
            out.flush();
            out.close();
        }
    
        @Override
        public String getErrorPath() {
            return "/error";
        }
    
    }
    
    

    不过ErrorController不能设置返回的http状态码,如果要想设置状态码需要使用@ControllerAdvice来处理

    import com.zero.common.base.result.CommonException;
    import com.zero.common.base.result.CommonResult;
    import com.zero.common.base.result.ErrorCode;
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.web.HttpRequestMethodNotSupportedException;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.ResponseStatus;
    
    /**
     * @Description: 全局异常处理
     */
    @ControllerAdvice
    public class ExceptionTranslator {
    
        @ResponseBody
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public CommonResult processValidationError(MethodArgumentNotValidException e) {
            return new CommonResult(ErrorCode.REQUEST_ERROR);
        }
    
    
        @ResponseBody
        @ExceptionHandler(CommonException.class)
        @ResponseStatus(HttpStatus.FORBIDDEN)
        public CommonResult processInvalidTokenException(CommonException e) {
            return new CommonResult(e.getErrorCode());
        }
    
        @ResponseBody
        @ExceptionHandler(AccessDeniedException.class)
        @ResponseStatus(HttpStatus.FORBIDDEN)
        public CommonResult processAccessDeniedException(AccessDeniedException e) {
            return new CommonResult(ErrorCode.NOT_ACCESSIBLE);
        }
    
        @ResponseBody
        @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
        @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
        public CommonResult processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
            return new CommonResult(ErrorCode.METHOD_NOT_ALLOWED);
        }
    
    
        @ExceptionHandler(Exception.class)
        public ResponseEntity<CommonResult> processRuntimeException(Exception e) {
            ResponseEntity.BodyBuilder builder;
            CommonResult commonResult;
            ResponseStatus responseStatus = AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class);
            if (responseStatus != null) {
                builder = ResponseEntity.status(responseStatus.value());
                commonResult = new CommonResult(responseStatus.value().value() + "", responseStatus.reason());
            } else {
                builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
                commonResult = new CommonResult(ErrorCode.INTERNAL_SERVER_ERROR);
            }
            return builder.body(commonResult);
        }
    }
    
    

    另外要注意的是使用了@ControllerAdvice注解之后ErrorController就会失效,@ControllerAdvice功能更全面一些,可以设置返回的http状态码,不过ErrorController中也可以用通用返回格式中的code字段来代替

    相关文章

      网友评论

          本文标题:spring boot数据通用返回格式和全局异常处理

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