HttpMessageConverter 原理
HttpMessageConverter 是 Spring 3.0 提供的一个新接口。负责将请求消息转为一个对象。也负责把一个对象转为响应消息。
你可以通过源码查看:
ublic interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
Spring MVC 定义了该转换器的接口来表示转换消息的过程。通过下面例子观察
例子:
@ResponseBody
@RequestMapping(value="/string", method=RequestMethod.POST)
public String test(@RequestBody String string) {
return "result:" + string;
}
首先,在进入 test 方法之前,会先根据 @RequestBody 注解注解的参数,选择合适的 HttpMessageConverter 的消息转换器,来把消息体写入到 string 变量中,而这里的消息转换器也就是 StringHttpMessageConverter,它的 canRead 返回 true,后面执行 read 方法,把 HttpInputMessage 中的消息体读出来。
当执行完 test 方法,因为有 @ResponseBody 注解,则会调用 write() 方法,通过把 t(也就是返回值)通过 HttpOutMessage 来写出到响应体中。并且 canWrite() 返回 true。其中 MediaType 指的是对请求的Media Type属性的封装。
流程:
- 客户端发送请求报文
- 请求报文在通过前端控制器,交给 HttpInputMessage
- HttpInputMessage 可以把请求报文交给 HttpMessageConverter 转换器,变成 Java 对象。
- 最后把 Java 对象交给 Spring MVC 请求处理器
- 然后请求处理器把 Java 对象交给 HttpMessageConverter 转换器
- 转换器负责调用 HttpOutputMessage,输出到响应报文发送给客户端
那么 HttpMessageConverter 在 SpringMVC 如何使用体现?
HttpMessageConverter 使用
HttpMessageConverter 将请求的信息绑定到处理器目标方法的入参,或者将响应结果转为对应的类型消息。在 Spring 中提供了两种途径
- 使用 @RequestBody 和 @ResponseBody 对请求处理器的入参、目标方法进行注解。
- 使用 HttpEntity 和 ResponseEntity 作为请求处理器目标方法的入参和返回值。(HttpEntity 对应 @RequestBody, ResponseEntity 对应 @ResponseBody)
当请求处理器使用到上述的几种方式的时候,SpringMVC 首先根据请求头或下响应头的 Accept 属性匹配 HttpMesssageConverter ,进而根据参数类型或者泛型的类型的过滤得到匹配的HttpMessageConverter,如果找不到,则报错。
细节:
- 使用 @RequestBody 和 HttpEntity 的效果一样
- 使用 @ResponseBody 和 ResponseEntity 的效果是一样的
- @ResponseBody 使用在方法上,@RequestBody 使用在参数上
范例:
-
使用 @ResponseBody 返回一个字符串
@ResponseBody @RequestMapping("testResponseBody") public String testJson() { return "success;" }
-
使用 ResponseEntity 返回字符串
@RequestMapping("testResponseEntity") public ResponseEntity testDownload1(HttpServletRequest request) throws Exception{ ResponseEntity responseEntity = new ResponseEntity<String>("success", HttpStatus.OK); return responseEntity; }
-
使用 @RequestBody 来获取表单的所有请求参数
@RequestMapping("testRequestBody") public void testUpload1(@RequestBody String body) { System.out.println(body); }
表单
<form action="${pageContext.request.contextPath}/testRequestBody" method="post" > <input type="text" name="name" value="aaa" ><br> <input type="text" name="age" value="18" ><br> <input type="submit" value="提交"> </form>
结果
name=aaa&age=18
-
使用 HttpEntity 来获取表单的所有请求参数
@RequestMapping("testRequestEntity") public void testRequestEntity(HttpEntity<String> entity) { System.out.println(entity.getBody()); }
表单和上面一样,只需要修改提交的 URL 即可,结果也和上面一样。
注意:
- 对于注解和实体类(HttpEntity 和 ResponseEntity )的写法是不相同的。其中两个实体类需要指定泛型,该泛型也就表示调用哪个对应的消息转换器。
- 消息转换器有很多,包括 StringHttpMessageConverter,ObjectToStringHttpMessageConverter,GsonHttpMessageConverter(需要导入 Json 的 jar 包)等等。