我正在使用POST
将代码编写到RestTemplate
数据到第三方API。该API使用内容类型text;charset=UTF-8
进行响应,而Spring会抛出InvalidMediaTypeException
,因为该内容类型不包含/
。是否有可能向Spring表明内容类型text
应该被视为text/plain
的内容类型?如果是这样,我该如何做到这一点?
这是导致问题的代码。我无法展示URL
,但我认为这并不重要。
// Make the body of the request.
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
body.add("Customer Street", "a test street!");
// Make the headers of the request.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(Arrays.asList(MediaType.TEXT_PLAIN));
// Create an HTTP entity.
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<MultiValueMap<String, String>>(body, headers);
// Get a rest template
RestTemplate rest = new RestTemplate();
// Post the data.
String resp = null;
try {
resp = rest.postForObject(URL, entity, String.class);
} catch (InvalidMediaTypeException e) {
e.printStackTrace();
return;
}
This问题几乎完全描述了我遇到的问题。这个问题的公认答案基本上是“看到这个问题”,我做了;它描述如下。
在this问题中(在上面的答案中链接),istibekesi询问使用自定义内容类型myXml
,并提供无效的示例配置。 Brian Clozel's回答帮助我理解了之前我不理解的内容类型的一些事情,但我仍然对这些问题感到困惑:
Brian说,给定的配置应将myXml
注册为路径扩展/参数,以便与application/xml
进行协商。我最初的理解是,Brian的意思是,“将接受标头设置为myXml
的请求应与接受标头设置为application/xml
的请求相同。”但是,现在我很确定Brian的意思是“以.myXml
结尾或使用查询参数format=myXml
的请求应被视为application/xml
。”我的第二种解释是否正确?如果是,为什么不给定的配置强制Spring将接受标头设置为myXml
作为application/xml
处理请求?
Brian说,istibekesi应该做的是注册HttpMessageConverter
然后注册application/xml
和myXml
。我想我理解Brian的意思是“注册一个HttpMessageConverter
”,但我无法弄清楚如何注册自定义媒体类型(例如myXml
)来使用HttpMessageConverter
}。
不幸的是,Brian建议“使用类似application/vnd.foobar.v.1.0+xml
的媒体类型”对我没有帮助,因为我无法控制我发送的响应的内容类型。我已尝试将请求的接受标头设置为text/plain
,但它没有更改响应。
Spring抛出的异常的堆栈跟踪是
org.springframework.http.InvalidMediaTypeException: Invalid mime type "text;charset=UTF-8": does not contain '/'
at org.springframework.http.MediaType.parseMediaType(MediaType.java:452)
at org.springframework.http.HttpHeaders.getContentType(HttpHeaders.java:745)
at org.springframework.web.client.HttpMessageConverterExtractor.getContentType(HttpMessageConverterExtractor.java:114)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:85)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:380)
at com.agileBTS.hellosignTest.App.main(App.java:47)
Caused by: org.springframework.util.InvalidMimeTypeException: Invalid mime type "text;charset=UTF-8": does not contain '/'
at org.springframework.util.MimeTypeUtils.parseMimeType(MimeTypeUtils.java:256)
at org.springframework.http.MediaType.parseMediaType(MediaType.java:449)
我浏览了堆栈跟踪中每个函数的源代码,看看我是否能弄清楚发生了什么,我可以清楚地看到parseMimeType
方法(抛出异常的第一种方法)非常简单:如果mime-type不包含/
,则抛出异常。我不明白任何代码将如何解决这个问题,除非我继承MimeTypeUtils
并强制Spring使用我的子类。这是必要的吗?这似乎很难。
在他们的回答中,Sean Carroll建议我使用行注册"text"
mime类型
c.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_PLAIN, MediaType.parseMediaType("text")));
但是,如果您在第487行查看#parseMediaType
方法here的源代码,您会看到#parseMediaType
将大部分工作交给{{1} }} 方法。从第176行开始查看 源代码here,很明显MimeTypeUtils#parseMimeType
会在第193行抛出#parseMimeType
,因为IllegalMimeTypeException
没有包含"text"
(实际上,这是在我的应用程序中抛出/
的确切代码行)。我需要的是解决这个问题。
经过测试,我已经确定配置内容协商管理器在我的情况下也不起作用。基于this教程,我认为XML配置很明显:
IllegalMimeTypeException
等同于此Java配置:
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
</bean>
看一下@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.mediaType("xml", MediaType.APPLICATION_XML)
.mediaType("json", MediaType.APPLICATION_JSON);
}
方法文档here,我看到了一行“从密钥添加映射,从路径扩展或查询参数中提取 ......“(强调我的);我想从该引文中排除“接受标题”是故意的。
答案 0 :(得分:1)
这就是我解释Brian的答案的方式。我假设你正在使用java配置。要注册HttpMessageConverter,您可以执行以下操作
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
StringHttpMessageConverter stringMessageConverter = new StringHttpMessageConverter();
// add support for "text" media type. This may require Charset of UTF-8
c.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_PLAIN, MediaType.parseMediaType("text")));
converters.add(stringMessageConverter);
}
}
击> <击> 撞击> 编辑:如何使用内容协商管理器将文本映射到text / plain?来自javadocs
对于路径扩展和参数策略,您可以显式添加MediaType映射。这将用于将路径扩展或参数值(如“json”)解析为媒体类型,例如“application / json”。
文档明确提到扩展和参数,我不完全确定,但它也可以在接受标题上工作(我需要更深入地研究代码)。如果不是,您可能需要查看自定义ContentNegotiationStrategy
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes" >
<value>
text=plain/text
</value>
</property>
</bean>
答案 1 :(得分:1)
可以使用ClientHttpRequestInterceptor
注册RestTemplate
,允许在Spring对它们做任何事情之前编辑客户端响应。为了解决我的问题,我创建了一个实现ClientHttpRequestInterceptor
的类,并覆盖了intercept
方法,用text;
标头中的application/json;
替换Content-Type
的所有实例。然后我使用我的ClientHttpRequestInterceptor
bean定义注册了我的RestTemplate
,这样每当我自动加载RestTemplate
时,它就会有这个拦截器。
我ClientHttpRequestInterceptor
的代码:
public class ContentTypeTextToTextJson implements ClientHttpRequestInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(ContentTypeTextToTextJson.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
LOG.debug("intercepting execution");
// Get the response as normal.
ClientHttpResponse response = execution.execute(request, body);
LOG.debug("intercepted response: " + response);
// Get the headers.
HttpHeaders headers = response.getHeaders();
LOG.debug("response had headers: " + headers);
// Grab all the content types.
List<String> contentTypes = headers.get("Content-Type");
LOG.debug("response had content-types: " + contentTypes);
// Loop over the content-types.
for(int i = 0; i < contentTypes.size(); i++) {
String contentType = contentTypes.get(i);
LOG.debug("processing content type: " + contentType);
// I'm not sure if it's possible for a content-type to be null, but I guess it's
// better safe then sorry?
if(null == contentType) {
continue;
}
// If it starts with "text;", replace "text" with "text/json" and replace the old content type.
if(contentType.startsWith("text;")) {
contentType = contentType.replaceFirst("text", "application/json");
LOG.debug("replacing content type " + contentTypes.get(i) + " with content type " + contentType);
contentTypes.set(i, contentType);
}
}
// Return the response.
return response;
}
}
RestTemplate bean定义:
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="interceptors">
<list>
<bean class="restinterceptors.ContentTypeTextToTextJson" />
</list>
</property>
</bean>