接口標記為@ResponseBody卻不進入ResponseBodyAdvice


一、背景:

我們的接口為了統一,在ResponseBodyAdvice中對返回值做統一處理,默認添加了errorNo和errorInfo字段返回。

最近同事改接口代碼的時候,發現接口返回值是空的。乍一看,沒什么重大修改。

接口代碼大致就是下面這個樣子:

@ResponseBody
@RequestMapping("test")
public void test(HttpServletRequest request, HttpServletResponse response){
    // 業務邏輯處理
}

二、問題分析

順着這個接口,單步調試跟到Spring的源碼ServletInvocableHandlerMethod#invokeAndHandle方法

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				// 直接進入到這里就return出去了
				return;
			}
		} else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		try {
			// 要進到這里才會進入到ResponseBodyAdvice中
			this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		} catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
			}
			throw ex;
		}
	}

再對着源碼,看下return出去的4個條件,核對一下我們的接口

  • void類型,返回值確實是null

  • isRequestNotModified(webRequest) ,false

  • getResponseStatus() != null ,false

    ​ 如果在test方法上標記@ResponseStatus(HttpStatus.OK),這里取到的就不是null

  • mavContainer.isRequestHandled(),true

那么問題來了,mavContainer.isRequestHandled()=true是什么時候設置的呢?

重新debug,發現在ServletResponseMethodArgumentResolver#resolveArgument中設置的,源碼截取如下:

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

		if (mavContainer != null) {
            // 就是這里設置的了
			mavContainer.setRequestHandled(true);
		}

		// 略 ...
}

三、問題小結

通過上面的分析,問題已經明朗了。

  1. 接口一直都是返回null的。開發此次修改的時候,在接口入參里面加了HttpServletResponse
  2. 因為有HttpServletResponse入參,就會進入到ServletResponseMethodArgumentResolver
  3. 因為ModelAndViewContainer不為null,mavContainer.setRequestHandled(true);
  4. 滿足前面ServletInvocableHandlerMethod#invokeAndHandle的條件,直接return
  5. 返回空結果


免責聲明!

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



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