Spring @ResponseStatus


  @ResponseStatus這個注解確實是個令我頭疼的注解.

  先記錄下@ResponseStatus注解的定義. 記錄幾個得到的信息:@ResponseStatus聲明在方法、類上, Spring3.0開始才有的, 三個屬性其中 HttpStatus類型的  value  和 code是一個含義, 默認值就是 服務器 500錯誤的 HttpStatus.

image

 

 

用法一.標注在@RequestMapping方法上.

@Controller
@RequestMapping("/simple")
public class SimpleController {

    @RequestMapping("/demo2")
    @ResponseBody
    @ResponseStatus(code = HttpStatus.OK)
    public String demo2(){
        return "hello world";
    }
}

上面說了 code 和 value一個意思,這里我就用code了,相對而言比較喜歡code單詞.   這里作用就是改變服務器響應的狀態碼 ,比如一個本是200的請求可以通過@ResponseStatus 改成404/500等等.

常見的幾個狀態碼 HttpStatus.OK 就是 200 , HttpStatus.INTERNAL_SERVER_ERROR 就是 500 等等 ,具體的查看 HttpStatus枚舉 有詳細說明.

實現原理呢,注解底層還是通過設置  response.setStatus來實現.

 

在@RequestMapping方法執行完成,Spring解析返回值之前,進行了responseStatus設置.

代碼片段位於:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#setResponseStatus

this對象指當前的ServletInvocableHandlerMethod,看到 @ResponseStatus的reason不為空,就調用response.sendError  ; reason為空,就調用setStatus方法僅僅設置響應狀態碼.

image

 

記錄下在哪里調用了這個responseStatus方法?

代碼片段位於:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle

發現如果ServletInvocableHandlerMethod的responseReason有值,也就是@ResponseStatus有reason屬性,@RequestMapping方法返回值都不處理了,直接返回;

image

 

也就是說只要有@ResponseStatus的 reason屬性標注在 處理器Controller類或者方法上,比如響應狀態碼code設置為 404,reason設置為頁面沒找到 ,那 tomcat 展示界面是這樣大概,展示信息就是我們寫的reason屬性.

@ResponseStatus(code=A,reason=B)標注在 @RequestMapping方法上,作用效果與 response.sendError(A,B)是一樣的.

所以,@ResponseStatus我建議啊, 這種方式下使用千萬不要加 reason, 就把@ResponseStatus  當做一個用來改變響應狀態碼的方式!

image

 

用法二.標注在@ControllerAdvice中.

@ControllerAdvice
@ResponseStatus
public class MyControllerAdvice {

    @ExceptionHandler({ArithmeticException.class})
    public ModelAndView fix(Exception e){
        Map map=new HashMap();
        map.put("ex",e.getMessage());
        return new ModelAndView("error",map);
    }

}

@ControllerAdvice標注初衷我想就是程序運行過程中發生異常,對異常如何處理?  而@ResponseStatus標注在@ControllerAdvice類或者該類下的@ExceptionHandler上,區別大概就是,

原來比如請求程序拋出異常,異常被捕獲,走@ExceptionHandler,正常走完狀態碼是200.

@ControllerAdvice或者 @ExceptionHandler標注了@ReponseStatus,那走完狀態碼就是500.

如果你再給@ResponseStatus添加了reason屬性,不管捕獲異常方法咋返回,都是服務器的錯誤碼捕獲界面,比如上面我的例子,給@ResponseStatus添加reason=”your defined message”.

不管怎么說,下面界面比一大堆異常堆棧信息看起來更簡潔,但我還是不推薦使用誒,原因啊,界面不友好.

image

 

用法三.自定義類型的異常添加注解@ResponseStatus

@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR,reason = "not  an error , just info")
public class MyException extends RuntimeException {
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

這樣子,在SpringMvc中如果有某個 @RequestMapping方法拋出該異常,  只要開啟<mvc:annotation-driven/>,異常自動展示的界面都是如下的:

image

 

 

SpringMvc異常捕獲方法如下.

代碼片段位於:org.springframework.web.servlet.DispatcherServlet#processHandlerException

<mvc:annotation-driven/>注冊了三個HandlerExceptionResolver:ExceptionHandlerExceptionResolver用來處理@ExceptionHandler,而ResponseStatusExceptionResolver是用來處理拋出的異常上標注了@ResponseStatus的解析器.

image

 

ResponseStatusExceptionResolver解析異常方式:

代碼片段位於:org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#doResolveException

ex就是@RequestMapping方法拋出自定義的異常,使用工具類解析自定義異常上的@ResponseStatus注解,找到注解就調用resolveResponseStatus進行響應的處理。

protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) {

		ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
		if (responseStatus != null) {
			try {
				return resolveResponseStatus(responseStatus, request, response, handler, ex);
			}
			catch (Exception resolveEx) {
				logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);
			}
		}
		else if (ex.getCause() instanceof Exception) {
			ex = (Exception) ex.getCause();
			return doResolveException(request, response, handler, ex);
		}
		return null;
	}

 

ResponseStatusExceptionResolver如何根據異常上的@ResponseStatus處理響應?

代碼片段位於:org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#resolveResponseStatus

獲取了@ReponseStatus的 code   reason屬性,reason不為空就 response.sendError(statusCode, reason) ,並且返回一個空的ModelAndView,這里的ModelAndView已經沒有意義了,SendError方法簡單來說就是會跳轉到web.xml中配置的 錯誤狀態碼 對應的頁面, 沒有配置就是默認的服務器的那種錯誤界面,且只展示 前面的reason信息,即響應類型為text/html .

image

 

總結:

不管哪種方式,@ReponseStatus最后都是通過response.setStatus或response.sendError來處理.

如果只是為了返回狀態碼,建議只使用 @ResponseStatus(code=xxxx)這樣來設置響應狀態碼;

     如果拋出異常呢,不建議@ControllerAdvice里面的 @ResponseStatus和   自定義異常上的  @ResponseStatus一起使用,  按照我的閱讀理解,兩個一起使用肯定是一個生效,而且是 @ControllerAdvice中的@ResponseStatus生效.

    

場景分析:假設拋出異常不是我們自定義的異常,我們想改變響應的狀態碼,通過@ExceptionHandler來處理異常,並在@ExceptionHandler方法上也可以設置@ResponseStatus來達到效果;

              假如拋出自定義的異常,自己沒有定義異常處理界面,那在異常上標注@ResponseStatus就可以走 服務器默認的界面展示,或者通過web.xml 配置error-code \ error-page來自定義界面處理異常;


免責聲明!

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



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