自定义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;
				}
			}
		}
经验分享 程序员 微信小程序 职场和发展