1. 程式人生 > >使用Spring MVC的@ControllerAdvice註解做Json的異常處理

使用Spring MVC的@ControllerAdvice註解做Json的異常處理

一,本文介紹Spring MVC的自定義異常處理,即在Controller中丟擲自定義的異常時,客戶端收到更友好的JSON格式的提示。而不是常見的報錯頁面。

二,場景描述:實現公用API,驗證API key的邏輯,放在攔截器中判斷(等同於在Controller中)並丟擲異常,使用者收到類似下圖的提示:


其中,Http狀態Code也能自由控制。

三,解決方案:

1,在RateLimitInterceptor.java攔截器中丟擲異常:

public class RateLimitInterceptor extends HandlerInterceptorAdapter{

	@Autowired private RedisService rs;

	/**
	 * 流量控制檢查入口
	 */
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws RequiredParameterException, SignException, RateLimitException,Exception {
		super.preHandle(request, response, handler);
		String appKey = request.getParameter("appKey");
		//判斷appKey是否為空或是否合法
		if(appKey == null){
			throw new RequiredParameterException("");
		}else if(AppKeyUtils.isFormatCorrect(appKey) || !rs.isExist(appKey)){
			
			throw new SignException();
			
		}else {
			try {
				AppCall appCall = AppCall.create(appKey, AppKeyUtils.getPlanDetails(appKey));
				appCall.decrease();
				rs.save(appCall);
				System.out.println("RateLimitInterceptor pass.");
			} catch (RateLimitException e) {
				//丟擲超限異常
				throw new RateLimitException();
			}
		}
		return true;
	}

}

當代碼走到(具體怎樣走到,需考慮具體業務邏輯,上述程式碼使用AppCall類來封裝,這是後話)
throw new SignException();

時,Spring將自動捕獲這個異常。然後做一些處理。這是正常的流程。那麼Spring如何自動不火

2,使用Spring MVC的@ControllerAdvice,在GlobalExceptionHandler.java類中實現全域性的異常處理類:

@ControllerAdvice
public class GlobalExceptionHandler {

	@ExceptionHandler(SQLException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ResponseBody
    public ExceptionResponse handleSQLException(HttpServletRequest request, Exception ex) {
        String message = ex.getMessage();
		return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
    }
     
    @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="IOException occured")
    @ExceptionHandler(IOException.class)
    @ResponseBody
    public void handleIOException(){
        //returning 404 error code
    }
	
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	@ExceptionHandler(SignException.class)
	public ExceptionResponse signException(SignException ex) {
		return ex.getEr();
	}

}

在方法的頭上註解:
@ExceptionHandler(SignException.class)

即表示讓Spring捕獲到所有丟擲的SignException異常,並交由這個被註解的方法處理。

註解:

@ResponseBody

即表示返回的物件,Spring會自動把該物件進行json轉化,最後寫入到Response中。

註解:

@ResponseStatus(HttpStatus.BAD_REQUEST)

表示設定狀態碼。如果應用級別的錯誤,此處其實永遠返回200也是可以接受的,但是要在你自定義的異常串和異常碼中做好交代。

本文的方案自定義了一個ExceptionResponse.java類,如下:

/**
 * 返回的json資料
 * @author craig
 *
 */
public class ExceptionResponse {
	
	private String message;
	private Integer code;
	
	/**
	 * Construction Method
	 * @param code
	 * @param message
	 */
	public ExceptionResponse(Integer code, String message){
		this.message = message;
		this.code = code;
	}
	
	public static ExceptionResponse create(Integer code, String message){
		return new ExceptionResponse(code, message);
	}
	
	public Integer getCode() {
		return code;
	}
	public String getMessage() {
		return message;
	}
	
}

如你所知,這個類就是最後傳到使用者面前的一個異常類,code和message根據業務定義並讓使用者知曉。而自定義的SignException.java實際上起到了一個橋樑的作用。Spring把SignException物件捕獲到,轉成相應的ExceptionResponse物件,剩下的就是如何優雅實現的問題了。 如下是SignException.java的實現:
/**
 * 簽名異常
 * @author tuxiao.czz
 *
 */
public class SignException extends Exception {

	private static final long serialVersionUID = 4714113994147018010L;
	private String message = "AppKey is not correct, please check.";
	private Integer code = 10002;
		
	private ExceptionResponse er;
	
	public SignException() {
		er = ExceptionResponse.create(code, message);
	}
	
	public ExceptionResponse getEr() {
		return er;
	}
	
}

所有關於這個異常的code和message都寫在這個類裡,個人感覺還是可以接受。當然還有另外一種實現,就是隻攔截、定義一種Exception類,然後傳不同的code和message進去,然後做相應的處理。這些都是比較靈活的。

以上便是實現“自定義異常json化處理”的相關程式碼和說明。