HttpMessageConverters

背景知识

  • Content-Type必须是具体确定的类型,不能包含 *;
  • Accept匹配规则:最明确的优先匹配;
  • @RequestMapping:consumes属性指定处理请求的提交内容类型(Content-Type),produces属性指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

在这里插入图片描述

HttpMessageConverters

作用:Http请求/响应与Java对象之间的转换,如下图所示。
在这里插入图片描述

请求转换过程:readWithMessageConverters

首先,从请求头中获取content-type,如下:

       MediaType contentType;
		try {
			contentType = inputMessage.getHeaders().getContentType();
		}
		catch (InvalidMediaTypeException ex) {
			throw new HttpMediaTypeNotSupportedException(ex.getMessage());
		}

其次,依次遍历HttpMessageConverter,判断其是否支持请求的content-type,如果支持则直接读取数据,然后返回,后面的HttpMessageConverter不再执行,如下:

        for (HttpMessageConverter<?> converter : this.messageConverters) {
				Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
				GenericHttpMessageConverter<?> genericConverter =
						(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
				if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
						(targetClass != null && converter.canRead(targetClass, contentType))) {
					if (logger.isDebugEnabled()) {
						logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
					}
					if (message.hasBody()) {
						HttpInputMessage msgToUse =
								getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
						body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
								((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
						body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
					}
					else {
						body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
					}
					break;
				}
			}

响应转换过程:writeWithMessageConverters

首先,根据请求的accept和@RequestMapping的属性produces指定的返回类型确定最具体的返回类型,如下:

HttpServletRequest request = inputMessage.getServletRequest();
			List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
			List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);

			if (outputValue != null && producibleMediaTypes.isEmpty()) {
				throw new HttpMessageNotWritableException(
						"No converter found for return value of type: " + valueType);
			}
			mediaTypesToUse = new ArrayList<>();
			for (MediaType requestedType : requestedMediaTypes) {
				for (MediaType producibleType : producibleMediaTypes) {
					if (requestedType.isCompatibleWith(producibleType)) {
						mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
					}
				}
			}
			if (mediaTypesToUse.isEmpty()) {
				if (outputValue != null) {
					throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
				}
				return;
			}
			MediaType.sortBySpecificityAndQuality(mediaTypesToUse);

其次,依次遍历HttpMessageConverter,判断是否支持最终确定的MediaType,如果支持,则直接进行转换,然后返回;否则继续遍历。

for (HttpMessageConverter<?> converter : this.messageConverters) {
				GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
						(GenericHttpMessageConverter<?>) converter : null);
				if (genericConverter != null ?
						((GenericHttpMessageConverter) converter).canWrite(declaredType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {
					outputValue = getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);
					if (outputValue != null) {
						addContentDispositionHeader(inputMessage, outputMessage);
						if (genericConverter != null) {
							genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);
						}
						else {
							((HttpMessageConverter) converter).write(outputValue, selectedMediaType, outputMessage);
						}
						if (logger.isDebugEnabled()) {
							logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType +
									"\" using [" + converter + "]");
						}
					}
					return;
				}
			}

SpringBoot 默认的HttpMessageConverters

详情可查看WebMvcConfigurationSupport的源码
在这里插入图片描述

添加额外的HttpMessageConverter

方式:向Spring容器注册HttpMessageConverters即可,比如添加FastJsonHttpMessageConverter;

 @Bean
    public HttpMessageConverters fastJsonHttpMessageConverters() {
        FastJsonHttpMessageConverter fastConverter= new FastJsonHttpMessageConverter();
       // fastConverter.setFastJsonConfig(getFastJsonConfig());
        HttpMessageConverter<?> converter = fastConverter;
        return new HttpMessageConverters(converter);
    }

使用注意:FastJsonHttpMessageConverter默认支持的MediaType为*/*,因此如果其被被添加到HttpMessageConverters列表的第一个,则会处理所有的MediaType,此时最好缩小其支持的MediaType范围;

附录

当你有如下Accept头,将遵守如下规则进行应用:
①Accept:text/html,application/xml,application/json
将按照如下顺序进行produces的匹配 ①text/html ②application/xml ③application/json
②Accept:application/xml;q=0.5,application/json;q=0.9,text/html
将按照如下顺序进行produces的匹配 ①text/html ②application/json ③application/xml
参数为媒体类型的质量因子,越大则优先权越高(从0到1)
③Accept:/,text/,text/html
将按照如下顺序进行produces的匹配 ①text/html ②text/
③*/*

参考

  1. https://my.oschina.net/lichhao/blog/172562;
  2. https://segmentfault.com/a/1190000012659486;
  3. https://www.cnblogs.com/111testing/p/6037579.html;
  4. https://github.com/alibaba/fastjson/wiki/在-Spring-中集成-Fastjson;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值