自定义HttpMessageConverter
一、场景
在使用springMVC接收或响应的时候,spring提供了很多种HttpMessageConverter的实现,基本可以满足觉大多数场景,但还是有些定制化场景无法满足,如笔者在使用springMVC默认的jackson作为返回结果序列化时,无法满足当前场景等等
二、自定义HttpMessageConverter
spring预留了HttpMessageConverter的接口,只需实现自己定制即可如下
public class CustomHttpMessageConverter implements HttpMessageConverter {
@Override
public boolean canRead(Class clazz, MediaType mediaType) {
//可读开关
return false;
}
@Override
public boolean canWrite(Class clazz, MediaType mediaType) {
//可写开关
return true;
}
@Override
public List<MediaType> getSupportedMediaTypes() {
//定制媒体类型,可以在具体类型下执行
return MediaType.parseMediaTypes("application/test");
}
@Override
public Object read(Class clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException {
//定制参数解析
return null;
}
@Override
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException, IOException {
//定制响应逻辑
outputMessage.getBody().write(o.toString().getBytes());
outputMessage.getBody().flush();
}
}
自定义 HttpMessageConverter完成后,要添加给spring的converters,实际应用场景中会根据supportedMediaTypes和canWrite/canRead过滤出合适的converters,再按照顺序从头取第一个,所以如果有多converter匹配想使用自己指定的converter,可以将其添加到最前面
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//添加到最前面
//converters.add(0, new CustomHttpMessageConverter());
converters.add(new CustomHttpMessageConverter());
}
}
三、HttpMessageConcverter查找过程
1.通过getSupportedMediaTypes来获取所有支持的媒体类型
2.用http header-Accept中的MediaType和上面获取到的做匹配,匹配到的放到mediaTypesToUse列表中
3.从mediaTypesToUse列表中找出第一个具体的类型(非*),作为selectedMediaType
4.遍历所有的messageConverters,根据selectedMediaType和canWrite/canRead筛选出第一个匹配到的作为实际使用后结束
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
