一、HttpMessageConverter<T>
1、HttpMessageConverter 简介
HttpMessageConverter<T> 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型T)输出为响应信息;
2、HttpMessageConverter<T> 接口定义的方法
先看一下接口的源代码,对源代码进行分析一波。
public interface HttpMessageConverter {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
List<MediaType> getSupportedMediaTypes();
default List<MediaType> getSupportedMediaTypes(Class<?> clazz);
MediaType类:表示互联网中多媒体数据类型的格式;例如:text/html,text/plain,application/json…
@Nullable注解:带有该注解修饰的参数表示该参数可以没有,如果没有就会采用默认的值!
canRead方法:检查clazz对象是否能转换为mediaType表示的数据类型,这个mediaType是前端页面请求时设定的contentType格式!
read方法:如果canRead方法返回值为true则调用read方法将数据进行格式转换!
canWrite方法:检查clazz对象是否能转换为mediaType类型,此时的mediaType表示后端想要响应给前端的数据格式!
write方法:如果canWrite返回值为true,则将数据进行格式的转换然后响应!
其他:另外两个方法是针对媒体类型进行查看的,媒体类型点进去有非常多的类型
HttpInputMessage inputMessage ):将请求信息转换为 T 类型的对象;
HttpOutputMessage outputMessage):将 T 类型的对象写到响应流中,同时指定相应的媒体类型为 contentType。
// HttpInputMessage
import java.io.IOException;
import java.io.InputStream;
public interface HttpInputMessage extends HttpMessage {
InputStream getBody() throws IOException;
}
//HttpOutputMessage
import java.io.IOException;
import java.io.OutputStream;
public interface HttpOutputMessage extends HttpMessage {
OutputStream getBody() throws IOException;
}
二、HttpMessageConverter<T> 的实现类
1、实现类
2、DispatcherServlet 默认装配的是 RequestMappingHandleAdapter,而 RequestMappingHandlerAdapter 默认装配如下 HttpMessageConverter:
3、加入 jackson 的 jar 包后,RequestMappingHandlerAdapter 装配的 HttpMessageConverter。如下:
默认情况下数组长度是6个:增加了 jackson 的包后多了一个 MappingJackson2HttpMessageConverter。
三、使用 HttpMessageConverter<T>
1、使用 HttpMessageConverter<T>
使用 HttpMessageConverter<T> 将请求信息转化并绑定到处理方法的形参中或将响应结果转为对应类型的响应信息,Spring 提供了两种途径:
@RequestBody / @ResponseBody 对处理方法进行标注;
(2)使用 HttpEntity<T> / ResponseEntity<T> 作为处理方法的形参或返回值
2、如何使用
当控制器处理方法使用到 @RequestBody / @ResponseBody 或 HttpEntity<T> / ResponseEntity<T> 时,Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter,进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter,若找不到可用的 HttpMessageConverter 将报错。
@RequestBody / @ResponseBody 不需要成对出现
3、@RequestBody 与 @ResponseBody 示例
示例:
(1)控制器
@Controller
public class TestHttpMessageConverter {
//@RequestBody:是将 Http 请求正文插入方法中,修饰目标方法的形参
@RequestMapping(value="/testHttpMessageConverter")
@ResponseBody //@ResponseBody 是将内容或对象作为 Http 响应正文返回
public String testHttpMessageConverter(@RequestBody String body) {
System.out.println("body=" + body);
return "Hello," + new Date(); //不再查找跳转的页面
}
}
(2)页面请求
<form action="testHttpMessageConverter" method="post" enctype="multipart/form-data">
文件:<input type="file" name="file" />
描述:<input type="text" name="desc" />
<input type="submit" value="提交"/>
</form>
(3)结果
body=------WebKitFormBoundary52UbVwlZZjWzjOhh
Content-Disposition: form-data; name="file"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundary52UbVwlZZjWzjOhh
Content-Disposition: form-data; name="desc"
------WebKitFormBoundary52UbVwlZZjWzjOhh--
4、HttpEntity<T> 与 ResponseEntity<T>
示例:
(1)HttpEntity 代码示例
@RequestMapping("/handle")
public String handle(HttpEntity<String> entity) {
System.out.println(entity.getHeaders().getContentLength());
System.out.println(entity.getHeaders().getContentType());
return "success";
}
(2)ResponseEntity 示例
@Controller
public class FileController {
@RequestMapping(value="/down")
public ResponseEntity<byte[]> downFile(HttpSession session) throws IOException {
//获取下载文件的路径
ServletContext servletContext = session.getServletContext();
String realPath = servletContext.getRealPath("img");
String finalPath = realPath + File.separator + "1.jpg";
//读取文件
InputStream is = new FileInputStream(finalPath);
//将整个文件放入到 byte 数组中, available() 获取输入流所读取的文件的最大字节数
byte[] b = new byte[is.available()];
is.read(b);
//设置请求头
//HttpHeaders headers = new HttpHeaders();
MultiValueMap<String, String> headers = new HttpHeaders();
//headers.add("Content-Disposition", "attachment;filename=中国.jpg"); 文件名为中文,不显示,存在问题
headers.add("Content-Disposition", "attachment;filename=aaa.jpg");
//设置响应状态
HttpStatus statusCode = HttpStatus.OK;
//将响应数据到客户端
// ResponseEntity<T>(T body, MultiValueMap<String,String> headers, HttpStatus statusCode)
ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(b, headers, statusCode);
return entity;
}
}
MultiValueMap<K,V> 继承关系:
四、springboot配置
通过实现WebMvcConfigurer接口来配置FastJsonHttpMessageConverter,springboot2.0版本以后推荐使用这种方式来进行web配置,这样不会覆盖掉springboot的一些默认配置。配置类如下:
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
@Configuration
public class MyWebmvcConfiguration implements WebMvcConfigurer{
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fjc = new FastJsonHttpMessageConverter();
FastJsonConfig fj = new FastJsonConfig();
fj.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
fjc.setFastJsonConfig(fj);
converters.add(fjc);
}
}
fastJson配置实体调用setSerializerFeatures方法可以配置多个过滤方式,常用的如下:
1、WriteNullListAsEmpty :List字段如果为null,输出为[],而非null
2、WriteNullStringAsEmpty : 字符类型字段如果为null,输出为"",而非null
3、DisableCircularReferenceDetect :消除对同一对象循环引用的问题,默认为false(如果不配置有可能会进入死循环)
4、WriteNullBooleanAsFalse:Boolean字段如果为null,输出为false,而非null
5、WriteMapNullValue:是否输出值为null的字段,默认为false。
五、扩展
1、HttpHeaders
该类继承了 MultiValueMap<K,V> 里面大多数都是与请求头相关。
2、HttpStatus
这个一个枚举类,这里定义了许多中请求状态。
如:OK:200 表示请求成功
404:路径错误或资源不存在
400:请求错误
405:请求方式和处理方式不匹配
406:客户端不能接收响应
500:服务器内部出现错误
被坑过后才知道学习HttpMessageConverter有多重要