问题
没有找到对应的HttpMessageConverter
org.springframework.web.client.RestClientException: No HttpMessageConverter for java.util.Map and content type "application/x-www-form-urlencoded"
at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:1000)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:774)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:711)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602)
报错位置
// RestTemplate
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
super.doWithRequest(httpRequest);
Object requestBody = this.requestEntity.getBody();
if (requestBody == null) {
HttpHeaders httpHeaders = httpRequest.getHeaders();
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
}
if (httpHeaders.getContentLength() < 0) {
httpHeaders.setContentLength(0L);
}
}
else {
Class<?> requestBodyClass = requestBody.getClass();
Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
HttpHeaders httpHeaders = httpRequest.getHeaders();
HttpHeaders requestHeaders = this.requestEntity.getHeaders();
MediaType requestContentType = requestHeaders.getContentType();
for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<Object> genericConverter =
(GenericHttpMessageConverter<Object>) messageConverter;
if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
}
logBody(requestBody, requestContentType, genericConverter);
genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);
return;
}
}
else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
if (!requestHeaders.isEmpty()) {
requestHeaders.forEach((key, values) -> httpHeaders.put(key, new ArrayList<>(values)));
}
logBody(requestBody, requestContentType, messageConverter);
((HttpMessageConverter<Object>) messageConverter).write(
requestBody, requestContentType, httpRequest);
return;
}
}
// 此处报错,说明没有找到对应的转换器
String message = "No HttpMessageConverter for " + requestBodyClass.getName();
if (requestContentType != null) {
message += " and content type \"" + requestContentType + "\"";
}
throw new RestClientException(message);
}
}
寻错
- 查看RestTemplate配置, 使用
RestTemplateBuilder进行创建的, 查看build方法,其中执行了new RestTemplate()
RestTemplateBuilder builder = new RestTemplateBuilder();
StringHttpMessageConverter m = new StringHttpMessageConverter();
RestTemplate restTemplate = builder
.additionalMessageConverters(m)
.build();
// 其中build方法中
public RestTemplate build() {
return configure(new RestTemplate());
}
- RestTemplate的转换器在
new RestTemplate()时就添加了AllEncompassingFormHttpMessageConverter转换器,按理说可以处理content type为"application/x-www-form-urlencoded"
// RestTemplate()构造函数中
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
content type为
"application/x-www-form-urlencoded"对应的转换器都继承自FormHttpMessageConverter, 其子类有AllEncompassingFormHttpMessageConverter
- 查看configure方法, 此处替换了原来的
MessageConverters
if (!CollectionUtils.isEmpty(this.messageConverters)) {
restTemplate.setMessageConverters(new ArrayList<>(this.messageConverters));
}
-
因为builder中只添加了一个转换器,所以RestTemplate中也只有一个,而这个转换器并不能处理content type为
"application/x-www-form-urlencoded" -
修改后还是报类似的错, 查看
FormHttpMessageConverter中的canWrite方法原来body必须要MultiValueMap类型的
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
// 此处
if (!MultiValueMap.class.isAssignableFrom(clazz)) {
return false;
}
if (mediaType == null || MediaType.ALL.equals(mediaType)) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) {
return true;
}
}
return false;
}
原因
- 使用
RestTemplateBuilder创建RestTemplate时,移除了所有默认的转换器,只保留了自定义配置的转换器(前提: 添加了转换器到builder中) - content type为
"application/x-www-form-urlencoded"时body的类型必须是MultiValueMap类型
解决
RestTempalte配置转换器
// 直接new, 不使用builder
RestTemplate restTemplate = new RestTemplate();
body的类型由Map变成MultiValueMap
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(datam, reqHeaders);
ResponseEntity<JsonNode> responseEntity = restTemplate.exchange(initUrl, HttpMethod.POST, requestEntity, JsonNode.class);