- 前言:本文试图以快速,清晰的方式自定义消息转换器,带大家理解springboot中如何将消息转换为需要的格式
1. 使用场景
目前主流的场景是:前端通过调用后端暴漏的HTTP接口,进行访问,大多以JSON格式传参,后端不再经过试图解析器,而是直接将response返回给前端。
@RequestBody,主要用来将请求体中的参数按要求提取,包装成需要的格式。 @ResponseBody,主要将响应体中的参数按要求提取,包装后返回给前端。 这都需要HttpMessageConverter,SpringBoot默认有很多,常用的如下图:
比如用application/json的格式请求,用application/json的格式返回,默认就会使用MappingJacksonHttpMessageConverter
所以,如果想使用XMl格式返回,甚至自己定义了一个格式接收和返回,怎么办呢?
我们只需要去定义一个新的HttpMessageConverter即可。 需要声明几个点:
1、要转换什么类型(Class)的数据
2、支持Content-Type是哪几种类型
3、读取request的代码
4、写入response的代码
上代码:
@Service
//1.实现AbstractHttpMessageConverter
public class ResponseToXlsConverter extends AbstractHttpMessageConverter<AjaxResponse> {
private static final MediaType EXCEL_TYPE = MediaType.valueOf("application/vnd.ms-excel");
public ResponseToXlsConverter() {
//2.声明支持的Content-Type类型
super(EXCEL_TYPE,MediaType.APPLICATION_JSON);
}
//3.声明支持那种类型的转换,也就是范型中的T
@Override
protected boolean supports(Class<?> clazz) {
return (AjaxResponse.class == clazz);
}
//4、对request修改,配合@RequestBody可以将请求体中的内容封装,在controllet的参数中获得
//此代码中我试图接受application/json的请求
@Override
protected AjaxResponse readInternal(Class<? extends AjaxResponse> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
System.out.println("readInternal 生效");
if (clazz == AjaxResponse.class) {
String s = StreamUtils.copyToString(inputMessage.getBody(), StandardCharsets.UTF_8);
return AjaxResponse.success(s, "heihei");
}
return null;
}
//5、对输出修改,配合@ResponseBody,直接跳过视图解析器,目前我返回了一个excel表格
@Override
protected void writeInternal(AjaxResponse ajaxResponse, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
System.out.println("writeInternal 生效");
final Workbook workbook = new HSSFWorkbook();
final Sheet sheet = workbook.createSheet();
final Row row = sheet.createRow(0);
row.createCell(0).setCellValue(ajaxResponse.getMessage());
row.createCell(1).setCellValue(ajaxResponse.getData().toString());
workbook.write(outputMessage.getBody());
}
}
将此转换器加入 Converters即可,
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
CustomHandlerInterceptor customHandlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(customHandlerInterceptor).addPathPatterns("/*");
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//加入头部,第一个被解析
converters.add(0, new ResponseToXlsConverter());
}
}
看下我的Controller: 我试图将json参数,解析成AjaxResponse,当然如果是application/vnd.ms-excel,我也可以解析,因为我的converter支持2种类型的格式。
@GetMapping(value = "/article/{id}")
public AjaxResponse article(@PathVariable Long id, @RequestBody AjaxResponse se) {
log.info("-->{}", se);
return AjaxResponse.success("hehe,nimade");
}
debug 看下原因
1、进入到了自定义的Converter中,比较supports方法和canRead方法
2、support肯定是true的,我们看下canRead, mediaType就是前端请求头中的Content-Type, 我们的supportsMediaTypes中支持2个,包含了json,就会成功。
3、检查请求体的内容,马上就会进入我们自己的readInternal方法, 没有请求体的请求方式是无法进行解析的,比如form-data。 我们的readInternal返回了AjaxResponse,赋予Controller中。
4、其实请求的解析 MappingJacksonHttpMessageConverter也可以做到,但是我们将自己的声明在List的第一位,所以先用自己的。
4、进行响应是流程一模一样
变化
1、converter如果有多个命中,那么会先用第一个,所以我们的add(0,converter)将自己的放在了第一位。
2、请求的content-type是由请求头带来的,但是响应时,可以通过controller中@RequestMapping的 produces来指定想要返回的类型,也就会影响到debug中的mediaType,所以比如说你想返回String,可以指定一下,就不会用自己的转换器了。