@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格式數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM