• spring boot 统一接口异常返回值


    创建业务 Exception

    一般在实际项目中,推荐创建自己的 Exception 类型,这样在后期会更容易处理,也比较方便统一,否则,可能每个人都抛出自己喜欢的异常类型,而造成代码混乱

    ServiceException 用于抛出业务逻辑校验异常

    UnauthorizedException 用于抛出用户未登录异常

    可根据自己的项目需求变化,但简单项目中这两个已经够用

    ServiceException

    package cn.myesn.exception;
    
    public class ServiceException extends RuntimeException {
    
        /**
         * Constructs a new runtime exception with {@code null} as its
         * detail message.  The cause is not initialized, and may subsequently be
         * initialized by a call to {@link #initCause}.
         */
        public ServiceException() {
            super();
        }
    
        /**
         * Constructs a new runtime exception with the specified detail message.
         * The cause is not initialized, and may subsequently be initialized by a
         * call to {@link #initCause}.
         *
         * @param message the detail message. The detail message is saved for
         *                later retrieval by the {@link #getMessage()} method.
         */
        public ServiceException(String message) {
            super(message);
        }
    
        /**
         * Constructs a new runtime exception with the specified detail message and
         * cause.  <p>Note that the detail message associated with
         * {@code cause} is <i>not</i> automatically incorporated in
         * this runtime exception's detail message.
         *
         * @param message the detail message (which is saved for later retrieval
         *                by the {@link #getMessage()} method).
         * @param cause   the cause (which is saved for later retrieval by the
         *                {@link #getCause()} method).  (A <tt>null</tt> value is
         *                permitted, and indicates that the cause is nonexistent or
         *                unknown.)
         * @since 1.4
         */
        public ServiceException(String message, Throwable cause) {
            super(message, cause);
        }
    
        /**
         * Constructs a new runtime exception with the specified cause and a
         * detail message of <tt>(cause==null ? null : cause.toString())</tt>
         * (which typically contains the class and detail message of
         * <tt>cause</tt>).  This constructor is useful for runtime exceptions
         * that are little more than wrappers for other throwables.
         *
         * @param cause the cause (which is saved for later retrieval by the
         *              {@link #getCause()} method).  (A <tt>null</tt> value is
         *              permitted, and indicates that the cause is nonexistent or
         *              unknown.)
         * @since 1.4
         */
        public ServiceException(Throwable cause) {
            super(cause);
        }
    
        /**
         * Constructs a new runtime exception with the specified detail
         * message, cause, suppression enabled or disabled, and writable
         * stack trace enabled or disabled.
         *
         * @param message            the detail message.
         * @param cause              the cause.  (A {@code null} value is permitted,
         *                           and indicates that the cause is nonexistent or unknown.)
         * @param enableSuppression  whether or not suppression is enabled
         *                           or disabled
         * @param writableStackTrace whether or not the stack trace should
         *                           be writable
         * @since 1.7
         */
        protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    

    UnauthorizedException

    package cn.myesn.exception;
    
    public class UnauthorizedException extends RuntimeException {
        /**
         * Constructs a new runtime exception with {@code null} as its
         * detail message.  The cause is not initialized, and may subsequently be
         * initialized by a call to {@link #initCause}.
         */
        public UnauthorizedException() {
            super();
        }
    
        /**
         * Constructs a new runtime exception with the specified detail message.
         * The cause is not initialized, and may subsequently be initialized by a
         * call to {@link #initCause}.
         *
         * @param message the detail message. The detail message is saved for
         *                later retrieval by the {@link #getMessage()} method.
         */
        public UnauthorizedException(String message) {
            super(message);
        }
    
        /**
         * Constructs a new runtime exception with the specified detail message and
         * cause.  <p>Note that the detail message associated with
         * {@code cause} is <i>not</i> automatically incorporated in
         * this runtime exception's detail message.
         *
         * @param message the detail message (which is saved for later retrieval
         *                by the {@link #getMessage()} method).
         * @param cause   the cause (which is saved for later retrieval by the
         *                {@link #getCause()} method).  (A <tt>null</tt> value is
         *                permitted, and indicates that the cause is nonexistent or
         *                unknown.)
         * @since 1.4
         */
        public UnauthorizedException(String message, Throwable cause) {
            super(message, cause);
        }
    
        /**
         * Constructs a new runtime exception with the specified cause and a
         * detail message of <tt>(cause==null ? null : cause.toString())</tt>
         * (which typically contains the class and detail message of
         * <tt>cause</tt>).  This constructor is useful for runtime exceptions
         * that are little more than wrappers for other throwables.
         *
         * @param cause the cause (which is saved for later retrieval by the
         *              {@link #getCause()} method).  (A <tt>null</tt> value is
         *              permitted, and indicates that the cause is nonexistent or
         *              unknown.)
         * @since 1.4
         */
        public UnauthorizedException(Throwable cause) {
            super(cause);
        }
    
        /**
         * Constructs a new runtime exception with the specified detail
         * message, cause, suppression enabled or disabled, and writable
         * stack trace enabled or disabled.
         *
         * @param message            the detail message.
         * @param cause              the cause.  (A {@code null} value is permitted,
         *                           and indicates that the cause is nonexistent or unknown.)
         * @param enableSuppression  whether or not suppression is enabled
         *                           or disabled
         * @param writableStackTrace whether or not the stack trace should
         *                           be writable
         * @since 1.7
         */
        protected UnauthorizedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    

    创建异常返回值包装类

    package cn.myesn.exception;
    
    import lombok.Data;
    import lombok.experimental.Accessors;
    
    @Data
    @Accessors(chain = true)
    public class GlobalExceptionResponseResult {
        private String message;
    }
    

    创建全局异常处理者

    创建 GlobalExceptionHandler 处理类,使用注解 @RestControllerAdvice 修改该类,该注解可以全局处理 spring boot rest controller 中抛出的所有异常,并且不用加 @ResponseBody,因为默认已经加上了

    package cn.myesn.handler;
    
    import cn.myesn.exception.GlobalExceptionResponseResult;
    import cn.myesn.exception.ServiceException;
    import cn.myesn.exception.UnauthorizedException;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.validation.BindException;
    import org.springframework.validation.ObjectError;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        // 根本就没有传递参数时的异常
        @ExceptionHandler(HttpMessageNotReadableException.class)
        @ResponseStatus(value = HttpStatus.BAD_REQUEST)
        public GlobalExceptionResponseResult handle(HttpMessageNotReadableException e) {
            return new GlobalExceptionResponseResult().setMessage("参数不能为空");
        }
    
        // 传了参数,但没有通过 validation 时的异常
        @ExceptionHandler(BindException.class)
        @ResponseStatus(value = HttpStatus.BAD_REQUEST)
        public GlobalExceptionResponseResult handle(BindException e) {
            final ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
            return new GlobalExceptionResponseResult().setMessage(objectError.getDefaultMessage());
        }
    
        // 代码中抛出的逻辑异常
        @ExceptionHandler(ServiceException.class)
        @ResponseStatus(value = HttpStatus.BAD_REQUEST)
        public GlobalExceptionResponseResult handle(ServiceException e) {
            return new GlobalExceptionResponseResult().setMessage(e.getMessage());
        }
    
        // 未登录异常
        @ExceptionHandler(UnauthorizedException.class)
        @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
        public void handle(UnauthorizedException e) {
        }
    
        // 但以上几种错误都未能匹配到时,catch 一个基础的异常类型,基本上能捕获所有该捕获的异常类型
        @ExceptionHandler(RuntimeException.class)
        @ResponseStatus(value = HttpStatus.BAD_REQUEST)
        public GlobalExceptionResponseResult handle(RuntimeException e) {
            return new GlobalExceptionResponseResult().setMessage(String.format("未处理的异常:%s", e.getMessage()));
        }
    }
    

    实际使用

    新建一个 TestController 文件,在里面添加如下端点:

    @GetMapping("check-username")
    public ResponseEntity<?> checkUsername(@RequestParam String username) {
        if (StringUtils.isBlank(username)) {
            throw new ServiceException("用户名不能为空");
        }
    
        if (userService.existsUserName(username)) {
            throw new ServiceException("用户名已存在");
        }
    
        return ResponseEntity.ok().build();
    }
    

    这样,写业务代码时,throw 就行了,代码整体更符合语义,也更加简洁明了

  • 相关阅读:
    [ICLR 2021] Revisiting Dynamic Convolution via Matrix Decomposition 学习笔记
    Codeforces Round #769 (Div. 2) ABCD
    Swin Transformer论文阅读笔记
    Leetcode 1765. 地图中的最高点(BFS)
    2022牛客寒假算法基础集训营3 ABCDEGIL
    Codeforces Round #767 (Div. 2) C. Meximum Array(主席树/整活解法)
    30
    27
    23
    29
  • 原文地址:https://www.cnblogs.com/myesn/p/15465083.html
Copyright © 2020-2023  润新知