您的位置:首页 > 理论基础 > 计算机网络

SpringMVC对HTTP报文体的处理

2015-12-12 20:23 573 查看

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中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: