解决:Could not extract response: no suitable HttpMessageConverter found for content type [xxx]
问题
使用
Spring-boot
的
RestTemplate
进行网络请求,
RestTemplate
把数据从
HttpResponse
转换成
Object
的时候找不到合适的
HttpMessageConverter
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.zhubayi.emp.entity.ResponseData] and content type [application/octet-stream] at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:124) at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1132) at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1115) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:865) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:764) at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:512) at com.zhubayi.emp.service.EmpService.getEmpInfo(EmpService.java:49) at com.zhubayi.emp.Test01.test(Test01.java:18) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
点进框住的部分,然后就会进入
extractData
方法
public T extractData(ClientHttpResponse response) throws IOException { IntrospectingClientHttpResponse responseWrapper = new IntrospectingClientHttpResponse(response); if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) { return null; //得到返回值的contentType MediaType contentType = getContentType(responseWrapper); try { //拿到messageConverters集合,然后进行遍历 for (HttpMessageConverter<?> messageConverter : this.messageConverters) { //如果是GenericHttpMessageConverter接口的实例,继承AbstractHttpMessageConverter会走这个if。 if (messageConverter instanceof GenericHttpMessageConverter genericMessageConverter) { //判断这个转换器能不能转换这个contentType类型 if (genericMessageConverter.canRead(this.responseType, null, contentType)) { if (logger.isDebugEnabled()) { ResolvableType resolvableType = ResolvableType.forType(this.responseType); logger.debug("Reading to [" + resolvableType + "]"); //走到这代表当前的HttpMessageConverter能进行转换,则调用read并返回 return (T) genericMessageConverter.read(this.responseType, null, responseWrapper); if (this.responseClass != null) { //还是判断这个转换器能不能进行contentType转换 if (messageConverter.canRead(this.responseClass, contentType)) { if (logger.isDebugEnabled()) { String className = this.responseClass.getName(); logger.debug("Reading to [" + className + "] as \"" + contentType + "\""); //走到这代表当前的HttpMessageConverter能进行转换,则调用read并返回 return (T) messageConverter.read((Class) this.responseClass, responseWrapper); catch (IOException | HttpMessageNotReadableException ex) { throw new RestClientException("Error while extracting response for type [" + this.responseType + "] and content type [" + contentType + "]", ex); 走到这抛出异常,所有的消息转换器都不能进行处理。 throw new UnknownContentTypeException(this.responseType, contentType, responseWrapper.getStatusCode(), responseWrapper.getStatusText(), responseWrapper.getHeaders(), getResponseBody(responseWrapper));
messageConverters
集合中就保存着
在RestTemplate
构造方法中添加的HttpMessageConverter
实现类
debug分析
进行
debug
,可以看到,收到response
的Header
里面的Content-Type
值为application/octet-stream
继续往下走,通用的
http
消息转换器匹配到自己的子类MappingJackson2HttpMessageConverter,但是可以看到的是supportedMediaTypes 只支持:application/json 的 MediaType。并不能转换text/html
未匹配到转换器,走到方法最后,就会抛出异常throw new UnknownContentTypeException(this.responseType, contentType, responseWrapper.getStatusCode(), responseWrapper.getStatusText(), responseWrapper.getHeaders(), getResponseBody(responseWrapper));
1. 修改
mappingJackson2HttpMessageConverter
配置重新设置
MappingJackson2HttpMessageConverter
能处理的MediaType
@Bean public RestTemplate restTemplate(){ RestTemplate restTemplate = new RestTemplate(); MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML)); restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter); return restTemplate;
MappingJackson2HttpMessageConverter
也是一个HttpMessageConverter
转换类,但是他不能处理text/html
的数据,原因是他的父类AbstractHttpMessageConverter
中的supportedMediaTypes
集合中没有text/html
类型,如果有的话就能处理了,通过setSupportedMediaTypes
可以给他指定一个新的MediaType
集合上面的写法会导致
MappingJackson2HttpMessageConverter
只能处理text/html
类型的数据
2. 继承
mappingJackson2HttpMessageConverter
使用
MappingJackson2HttpMessageConverter
,只需要给他能处理的MediaType
public class MyHttpMessageConverter extends MappingJackson2HttpMessageConverter { public MyHttpMessageConverter() { setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
然后将这个消息转换器追加到
RestTemplate
中的messageConverters
@Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(new MyHttpMessageConverter()); // 兼容 application/octet-stream return restTemplate;
3.实现
HttpMessageConverter
直接继承
HttpMessageConverter
(当然更推荐的是继承Abstract HttpMessageConverter
)来实现public interface HttpMessageConverter<T> { * 根据mediaType判断clazz是否可读 boolean canRead(Class<?> clazz, @Nullable MediaType mediaType); * 根据mediaType判断clazz是否可写 boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType); * 获取支持的mediaType List<MediaType> getSupportedMediaTypes(); * 将HttpInputMessage流中的数据绑定到clazz中 T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException; * 将t对象写入到HttpOutputMessage流中 void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
canWrite,write
方式是不需要处理的,只管 canRead和read 就行在
canRead
方法中判断了是不是application/octet-stream
类型,是的话就会返回true
,SpringBoot
就会调用read
,用来将字节流中的数据转换成具体实体,Class
就是我们最终想要得到的实例对象的Class
StreamUtils
这个工具类是SpringBoot自带的一个,用来读取InputStream
中的数据并返回String
字符串,SpringBoot
内部很多地方都用到了这个工具类
public class MyHttpMessageConverter2 implements HttpMessageConverter<Object> { * 根据mediaType判断clazz是否可读 @Override public boolean canRead(Class<?> aClass, MediaType mediaType) { if (mediaType != null) { return mediaType.isCompatibleWith(MediaType.APPLICATION_OCTET_STREAM); return false; * 根据mediaType判断clazz是否可写 @Override public boolean canWrite(Class<?> aClass, MediaType mediaType) { return false; * 获取支持的mediaType @Override public List<MediaType> getSupportedMediaTypes() { return Arrays.asList(MediaType.APPLICATION_OCTET_STREAM); * 将HttpInputMessage流中的数据绑定到clazz中 @Override public Object read(Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException { String json = StreamUtils.copyToString(httpInputMessage.getBody(), Charset.forName("UTF-8")); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return objectMapper.readValue(json, aClass); * 将t对象写入到HttpOutputMessage流中 @Override public void write(Object o, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
最后需要进行配置,
getMessageConverters()
会返回现有的HttpMessageConverter
集合,我们在这个基础上加入我们自定义的HttpMessageConverter
即可
@Bean public RestTemplate restTemplate(){ RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(new MyHttpMessageConverter2()); return restTemplate;
4.继承
AbstractHttpMessageConverter
public class MyHttpMessageConverter3 extends AbstractHttpMessageConverter<Object> { public MyHttpMessageConverter3() { super(MediaType.APPLICATION_OCTET_STREAM); @Override protected boolean supports(Class<?> aClass) { return true; @Override protected Object readInternal(Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException { String json = StreamUtils.copyToString(httpInputMessage.getBody(), Charset.forName("UTF-8")); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return objectMapper.readValue(json, aClass); @Override protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
然后同上面,加入我们自定义的
HttpMessageConverter
即可
@Bean public RestTemplate restTemplate(){ RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(new MyHttpMessageConverter3()); return restTemplate; # java # junit # spring # spring boot
所有评论(0)