• @RestControllerAdvice全局异常统一处理


      spring项目中,我们通常规定了返回的格式(成功-失败-异常),特别是异常怎么处理方便呢?

    1.自定义状态码实体

    package com.ruoyi.common.constant;
    
    /**
     * 返回状态码
     * 
     * @author ruoyi
     */
    public class HttpStatus
    {
        /**
         * 操作成功
         */
        public static final int SUCCESS = 200;
    
        /**
         * 对象创建成功
         */
        public static final int CREATED = 201;
    
        /**
         * 请求已经被接受
         */
        public static final int ACCEPTED = 202;
    
        /**
         * 操作已经执行成功,但是没有返回数据
         */
        public static final int NO_CONTENT = 204;
    
        /**
         * 资源已被移除
         */
        public static final int MOVED_PERM = 301;
    
        /**
         * 重定向
         */
        public static final int SEE_OTHER = 303;
    
        /**
         * 资源没有被修改
         */
        public static final int NOT_MODIFIED = 304;
    
        /**
         * 参数列表错误(缺少,格式不匹配)
         */
        public static final int BAD_REQUEST = 400;
    
        /**
         * 未授权
         */
        public static final int UNAUTHORIZED = 401;
    
        /**
         * 访问受限,授权过期
         */
        public static final int FORBIDDEN = 403;
    
        /**
         * 资源,服务未找到
         */
        public static final int NOT_FOUND = 404;
    
        /**
         * 不允许的http方法
         */
        public static final int BAD_METHOD = 405;
    
        /**
         * 资源冲突,或者资源被锁
         */
        public static final int CONFLICT = 409;
    
        /**
         * 不支持的数据,媒体类型
         */
        public static final int UNSUPPORTED_TYPE = 415;
    
        /**
         * 系统内部错误
         */
        public static final int ERROR = 500;
    
        /**
         * 接口未实现
         */
        public static final int NOT_IMPLEMENTED = 501;
    }

    2.创建返回实体

      按照规定的格式创建返回实体,这样子就可以规范返回的格式-下面是一个自定义的返回实体

    package com.ruoyi.common.core.domain;
    
    import java.util.HashMap;
    import com.ruoyi.common.constant.HttpStatus;
    import com.ruoyi.common.utils.StringUtils;
    
    /**
     * 操作消息提醒
     * 
     * @author ruoyi
     */
    public class AjaxResult extends HashMap<String, Object>
    {
        private static final long serialVersionUID = 1L;
    
        /** 状态码 */
        public static final String CODE_TAG = "code";
    
        /** 返回内容 */
        public static final String MSG_TAG = "msg";
    
        /** 数据对象 */
        public static final String DATA_TAG = "data";
    
        /**
         * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
         */
        public AjaxResult()
        {
        }
    
        /**
         * 初始化一个新创建的 AjaxResult 对象
         * 
         * @param code 状态码
         * @param msg 返回内容
         */
        public AjaxResult(int code, String msg)
        {
            super.put(CODE_TAG, code);
            super.put(MSG_TAG, msg);
        }
    
        /**
         * 初始化一个新创建的 AjaxResult 对象
         * 
         * @param code 状态码
         * @param msg 返回内容
         * @param data 数据对象
         */
        public AjaxResult(int code, String msg, Object data)
        {
            super.put(CODE_TAG, code);
            super.put(MSG_TAG, msg);
            if (StringUtils.isNotNull(data))
            {
                super.put(DATA_TAG, data);
            }
        }
    
        /**
         * 返回成功消息
         * 
         * @return 成功消息
         */
        public static AjaxResult success()
        {
            return AjaxResult.success("操作成功");
        }
    
        /**
         * 返回成功数据
         * 
         * @return 成功消息
         */
        public static AjaxResult success(Object data)
        {
            return AjaxResult.success("操作成功", data);
        }
    
        /**
         * 返回成功消息
         * 
         * @param msg 返回内容
         * @return 成功消息
         */
        public static AjaxResult success(String msg)
        {
            return AjaxResult.success(msg, null);
        }
    
        /**
         * 返回成功消息
         * 
         * @param msg 返回内容
         * @param data 数据对象
         * @return 成功消息
         */
        public static AjaxResult success(String msg, Object data)
        {
            return new AjaxResult(HttpStatus.SUCCESS, msg, data);
        }
    
        /**
         * 返回错误消息
         * 
         * @return
         */
        public static AjaxResult error()
        {
            return AjaxResult.error("操作失败");
        }
    
        /**
         * 返回错误消息
         * 
         * @param msg 返回内容
         * @return 警告消息
         */
        public static AjaxResult error(String msg)
        {
            return AjaxResult.error(msg, null);
        }
    
        /**
         * 返回错误消息
         * 
         * @param msg 返回内容
         * @param data 数据对象
         * @return 警告消息
         */
        public static AjaxResult error(String msg, Object data)
        {
            return new AjaxResult(HttpStatus.ERROR, msg, data);
        }
    
        /**
         * 返回错误消息
         * 
         * @param code 状态码
         * @param msg 返回内容
         * @return 警告消息
         */
        public static AjaxResult error(int code, String msg)
        {
            return new AjaxResult(code, msg, null);
        }
    }

    3.使用@RestControllerAdvice创建全局处理器

      @RestControllerAdvice是@ControllerAdvice和@ResponseBody的合并。此注解标记的类就是全局处理类,在这个类中可以自定义一个个的方法,用 @ExceptionHandler(异常类型)注解,那么它就回去拦截对应的异常,在该方法中进行处理,且把处理结果返回给页面。

    3.1创建全局异常处理器

    package com.ruoyi.framework.web.exception;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.authentication.AccountExpiredException;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.validation.BindException;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;
    import org.springframework.web.servlet.NoHandlerFoundException;
    import com.ruoyi.common.constant.HttpStatus;
    import com.ruoyi.common.core.domain.AjaxResult;
    import com.ruoyi.common.exception.BaseException;
    import com.ruoyi.common.exception.CustomException;
    import com.ruoyi.common.exception.DemoModeException;
    import com.ruoyi.common.utils.StringUtils;
    
    /**
     * 全局异常处理器
     * 
     * @author ruoyi
     */
    @RestControllerAdvice  //标识这是全局异常处理器
    public class GlobalExceptionHandler
    {
        private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
        /**
         * 基础异常
         */
        @ExceptionHandler(BaseException.class)   //表示对指定异常进行拦截并处理
        public AjaxResult baseException(BaseException e)
        {
            return AjaxResult.error(e.getMessage());
        }
    
       /**
         * 业务异常
         */
        @ExceptionHandler(CustomException.class)
        public AjaxResult businessException(CustomException e)
        {
            if (StringUtils.isNull(e.getCode()))
            {
                return AjaxResult.error(e.getMessage());
            }
            return AjaxResult.error(e.getCode(), e.getMessage());
        }
    @ExceptionHandler(Exception.class) public AjaxResult handleException(Exception e) { log.error(e.getMessage(), e); return AjaxResult.error(e.getMessage()); } }

      注解1:@RestControllerAdvice //标识这是全局异常处理器

      注解2:@ExceptionHandler(BaseException.class) //表示对指定异常进行拦截并处理。当我们抛出指定的异常(可以自定义异常)时,会被ExceptionHandler拦截,并进行处理

    3.2上面使用了自定义的异常

    package com.ruoyi.common.exception;
    
    /**
     * 自定义异常
     * 
     * @author ruoyi
     */
    public class CustomException extends RuntimeException
    {
        private static final long serialVersionUID = 1L;
    
        private Integer code;
    
        private String message;
    
        public CustomException(String message)
        {
            this.message = message;
        }
    
        public CustomException(String message, Integer code)
        {
            this.message = message;
            this.code = code;
        }
    
        public CustomException(String message, Throwable e)
        {
            super(message, e);
            this.message = message;
        }
    
        @Override
        public String getMessage()
        {
            return message;
        }
    
        public Integer getCode()
        {
            return code;
        }
    }

    3.3业务中抛出异常

      public String login(String username, String password, String code, String uuid)
        {
            //1.校验验证码
            String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;  //缓存中存入验证码格式 {Constants.CAPTCHA_CODE_KEY + uuid:验证码}
            String captcha = redisCache.getCacheObject(verifyKey);
            redisCache.deleteObject(verifyKey);
            if (captcha == null)
            {  //缓存中没有验证码  发起一个异步任务-记录此次错误登录信息
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                throw new CaptchaExpireException();
            }
            if (!code.equalsIgnoreCase(captcha))  //验证码错误  发起一个异步任务-记录此次错误登录信息
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                throw new CaptchaException();
            }
            // 2.用户验证
            Authentication authentication = null;
            try
            {
                // 该方法会去调用package com.ruoyi.framework.web.service.UserDetailsServiceImpl.loadUserByUsername  若是校验失败,会抛出异常
                authentication = authenticationManager
                        .authenticate(new UsernamePasswordAuthenticationToken(username, password)); //loadUserByUsername方法会获取用户,匹配密码是自动完成的
            }
            catch (Exception e)
            {
                if (e instanceof BadCredentialsException) //用户密码不匹配 发起一个异步任务-记录此次错误登录信息
                {
                    AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                    throw new UserPasswordNotMatchException();
                }
                else
                {  //其它不匹配  比如没有该用户  该用户被停用等等  发起一个异步任务-记录此次错误登录信息
                    AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                    throw new CustomException(e.getMessage());
                }
            }
            
            //发起一个异步任务-记录此次成功登录信息
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            // 生成token
            return tokenService.createToken(loginUser);
        }

      throw new CustomException(e.getMessage());

      这里我们在service层就直接抛出异常CustomException,会被上面的全局异常处理器拦截,和@ExceptionHandler(CustomException.class)匹配,匹配到了下面的方法

    /**
         * 业务异常
         */
        @ExceptionHandler(CustomException.class)
        public AjaxResult businessException(CustomException e)
        {
            if (StringUtils.isNull(e.getCode()))
            {
                return AjaxResult.error(e.getMessage());
            }
            return AjaxResult.error(e.getCode(), e.getMessage());
        }

      抛出CustomException,该方法执行,最后返回一个AjaxResult对象给页面 ----return AjaxResult.error(e.getCode(), e.getMessage());

      {

    code: 状态码
    msg: 返回内容
    data :数据对象

      }

    3.4说明

      全局异常管理器实际上就是创建了拦截器对抛出的异常进行处理,并把处理结果返回给页面

       上面我们使用的是@RestControllerAdvice而不是@ControllerAdvice,它是@ControllerAdvice@ResponseBody的结合,返回的都是json数据。如果使用@ControllerAdvice,方法上需要添加@ResponseBody才能返回json格式数据。

  • 相关阅读:
    在Windows环境下使用docker
    C# 8.0中的模式匹配
    C# 8.0的新的using语法——Using declarations
    任务调度框架FluentScheduler简介
    任务调度框架Hangfire 简介
    Linux下的sqlserver简单试用
    使用Puppeteer进行数据抓取(五)——快速调试
    使用ptrace向已运行进程中注入.so并执行相关函数(转)
    ARM汇编编程基础之一 —— 寄存器
    ARM 汇编与C调用的若干问题(一般函数调用情况)
  • 原文地址:https://www.cnblogs.com/jthr/p/14000903.html
Copyright © 2020-2023  润新知