SpringMVC對HTTP報文體的處理

SpringMVC對HTTP報文體的處理

   客戶端和服務端HTTP報文傳遞消息,而HTTP報文包含報文頭和報文體。通常,解析請求參數以及返回頁面都不需要我們關心HTTP報文體的讀取和生成過程。但在某些特定場景下需要直接到請求報文中讀取報文體,或者將返回的數據直接寫入到報文體中。

  在SpringMVC中,可以利用RequestBody註解表示一個參數,說明解析它需要讀取報文體,也可以直接將參數類型聲明成HttpEntity<T>類型。與處理請求類似,如果想要將對象寫入到響應報文的報文體中,則可以返回HttpEntity<T>類型的數據或者在方法上標註ResponseBody。

 RequestBody與ResponseBody對應的處理器是RequestResponseBodyMethodProcessor,它同時負責消息體的讀取和寫入,它既是參數解析器也是結果處理器。下面看看它讀取和寫入報文體的邏輯:
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getParameterType());
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation annot : annotations) {
			if (annot.annotationType().getSimpleName().startsWith("Valid")) {
				String name = Conventions.getVariableNameForParameter(parameter);
				WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
				Object hints = AnnotationUtils.getValue(annot);
				binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
				BindingResult bindingResult = binder.getBindingResult();
				if (bindingResult.hasErrors()) {
					throw new MethodArgumentNotValidException(parameter, bindingResult);
				}
			}
		}
		return arg;
	}

	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException {

		mavContainer.setRequestHandled(true);
		if (returnValue != null) {
			writeWithMessageConverters(returnValue, returnType, webRequest);
		}
	}

  由上面的兩個方法可以看出,不管是消息體的讀取還是寫入都涉及到了HttpMessageConverter,這個對象就是spring負責消息體與Java對象互相轉換的工具。另外,HttpEntity類型的數據處理器爲HttpEntityMethodProcessor,它對消息體的讀取和寫入也是利用HttpMessageConverter:
	public Object resolveArgument(
			MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
			throws IOException, HttpMediaTypeNotSupportedException {

		HttpInputMessage inputMessage = createInputMessage(webRequest);
		Class<?> paramType = getHttpEntityType(parameter);

		Object body = readWithMessageConverters(webRequest, parameter, paramType);
		return new HttpEntity<Object>(body, inputMessage.getHeaders());
	}
	public void handleReturnValue(
			Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws Exception {

		mavContainer.setRequestHandled(true);

		if (returnValue == null) {
			return;
		}

		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		Assert.isInstanceOf(HttpEntity.class, returnValue);
		HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
		if (responseEntity instanceof ResponseEntity) {
			outputMessage.setStatusCode(((ResponseEntity<?>) responseEntity).getStatusCode());
		}

		HttpHeaders entityHeaders = responseEntity.getHeaders();
		if (!entityHeaders.isEmpty()) {
			outputMessage.getHeaders().putAll(entityHeaders);
		}
		
		Object body = responseEntity.getBody();
		if (body != null) {
			writeWithMessageConverters(body, returnType, inputMessage, outputMessage);
		}
		else {
			// flush headers to the HttpServletResponse
			outputMessage.getBody();
		}
	}

  既然HttpMessageConverter這麼重要,就來看看它的接口定義:

public interface HttpMessageConverter<T> {

	boolean canRead(Class<?> clazz, MediaType mediaType);

	boolean canWrite(Class<?> clazz, MediaType mediaType);

	List<MediaType> getSupportedMediaTypes();

	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
	
	void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

}
   每個HttpMessageConverter都要實現canRead和canWrite方法,以此來判斷當前的HttpMessageConverter是否可以勝任此次報文體處理的任務。MediaType 對應的HTTP的Content-Type頭部,只有同時符合數據類型以及Content-Type的描述纔可以正確的讀取或者寫入報文體。與參數解析器和結果處理器一樣,HttpMessageConverter的設計也採用了策略模式,也是配置在HandlerAdapter中。
    在實際開發,若需要跟客戶端實現自己的協議,則可以自己實現HttpMessageConverter並配置到HandlerAdapter中。
發佈了51 篇原創文章 · 獲贊 11 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章