被坑过后才知道HttpMessageConverter多重要
一、HttpMessageConverter简介或许HttpMessageConverter没听过 , 但是@RequestBody和@ResponseBody这两个注解不会不知道吧 , 深入研究数据转换时 , 就会发现HttpMessageConverter这个接口 , 简单说就是HTTP的request和response的转换器 , 在遇到@RequestBody时候SpringBoot会选择一个合适的HttpMessageConverter实现类来进行转换 , 内部有很多实现类 , 也可以自己实现 , 如果这个实现类能处理这个数据 , 那么它的canRead()方法会返回true , SpringBoot会调用它的read()方法从请求中读出并转换成实体类 , 同样canWrite也是 。
但是我并不是从这里认识到HttpMessageConverter的 , 而是从RestTemplate , RestTemplate是一个使用同步方式执行HTTP请求的类 , 因此不需要加入OkHttp或者其他HTTP客户端的依赖 , 使用它就可以和其他服务进行通信 , 但是容易出现转换问题 , 如果对微信接口或者qq接口有所了解的话 , 那么在使用RestTemplate调用他们服务的时候 , 必定会报一个错误 。
如下面在调用qq互联获取用户信息的接口时 , 报的错误 。
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [class xxx.xxx.xxxxx] and content type [text/html;charset=utf-8]复制代码
错误信息是未知的ContentType , 这个ContentType就是第三方接口返回时候在HTTP头中的Content-Type , 如果通过其他工具查看这个接口返回的HTTP头 , 会发现它的值是text/html , 通常我们见的都是application/json类型 。 (微信接口返回的是text/plain) , 由于内部没有HttpMessageConverter能处理text/html的数据 , 没有一个实现类的canRead()返回true , 所以最后报错 。
文章插图
通常使用OkHttp或者其他框架时不会遇到这个错误 。
二、深入报错源码只有了解了报错原因以及源码 , 才能更好的解决问题 , 所以 , 我们根据报错源码的行数 , 定位到HttpMessageConverterExtractor下的extractData方法 , 从这个结构一眼就能看出大概逻辑:循环找出能处理这个contentType的HttpMessageConverter , 然后调用这个HttpMessageConverter的read()并返回 。
public T extractData(ClientHttpResponse response) throws IOException {MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);if (responseWrapper.hasMessageBody()try {//拿到messageConverters的迭代器Iterator var4 = this.messageConverters.iterator();while(var4.hasNext()) {//下一个HttpMessageConverterHttpMessageConverter> messageConverter = (HttpMessageConverter)var4.next();//如果是GenericHttpMessageConverter接口的实例 , 继承AbstractHttpMessageConverter会走这个if 。if (messageConverter instanceof GenericHttpMessageConverter) {GenericHttpMessageConverter> genericMessageConverter = (GenericHttpMessageConverter)messageConverter;//判断这个转换器是不能能转换这个类型if (genericMessageConverter.canRead(this.responseType, (Class)null, contentType)) {if (this.logger.isDebugEnabled()) {ResolvableType resolvableType = ResolvableType.forType(this.responseType);this.logger.debug("Reading to [" + resolvableType + "]");}//走到这代表当前的HttpMessageConverter能进行转换 , 则调用read并返回return genericMessageConverter.read(this.responseType, (Class)null, responseWrapper);}}//还是判断这个转换器能不能进行转换if (this.responseClass != nullthis.logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");}////走到这代表当前的HttpMessageConverter能进行转换 , 则调用read并返回return messageConverter.read(this.responseClass, responseWrapper);}}} catch (HttpMessageNotReadableException | IOException var8) {throw new RestClientException("Error while extracting response for type [" + this.responseType + "] and content type [" + contentType + "]", var8);}//走到这抛出异常 , 所有的消息转换器都不能进行处理 。throw new UnknownContentTypeException(this.responseType, contentType, response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), getResponseBody(response));} else {return null;} }复制代码
messageConverters集合中就保存着在RestTemplate构造方法中添加的HttpMessageConverter实现类 。
文章插图
三、自定义HttpMessageConverter找到了原因 , 我们就需要解决问题 , 下面使用一个简单的办法 , 即重新设置MappingJackson2HttpMessageConverter能处理的MediaType 。
@Beanpublic RestTemplate restTemplate(){RestTemplate restTemplate = new RestTemplate();MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML));restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);returnrestTemplate;}复制代码
- 面临|“熟悉的陌生人”不该被边缘化
- 俄罗斯手机市场|被三星、小米击败,华为手机在俄罗斯排名跌至第三!
- 先别|用了周冬雨的照片,我会成为下一个被告?自媒体创作者先别自乱阵脚
- 美国|印度宣布彻底突破5G难关,美英加澳一片欢呼,一周后白宫怒斥被骗
- 责令|1336款APP被责令整改,三大问题突出
- iPhone|接近8千万!苹果被罚款了!中国iPhone用户这次真的该生气了!
- 误导|苹果又吃巨额罚单,因iPhone防水宣传有误导被重罚9400万
- 覆盖|iPhone13Pro概念机:机身正面被屏幕全覆盖,库克想搞事情?
- 敢动|女生最害怕被“偷看”的3软件,QQ不算啥,第二敢动就“翻脸”
- 涉嫌|李佳琦店铺被罚是怎么回事?店内洗发水涉嫌虚假宣传