Reputation: 27781
I need to post an object (e.g. not a MultiValueMap
) via a RestTemplate
with the content type application/x-www-form-urlencoded
. When I try to do so ...
HttpHeaders headers = new HttpHeaders();
HttpEntity request;
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED)
// data is some generic type
request = new HttpEntity<>(data, headers);
// clazz is the Class<T> being returned
restTemplate.exchange(url, method, request, clazz)
... I get the following error:
org.springframework.web.client.RestClientException: Could not write request: no suitable HttpMessageConverter found for request type [com.whatever.MyRequestPayload] and content type [application/x-www-form-urlencoded]
Here is what I see within restTemplate.getMessageConverters()
:
Why don't I want to provide a MultiValueMap
? Two reasons:
x-www-form-urlencoded
will only complicate thingsx-www-form-urlencoded
stringUpvotes: 33
Views: 46971
Reputation: 1220
Reason: there is no converter can convert your java object into request body in x-www-form-urlencoded
format.
Solution1: create this kind of converter, as what @Josh M. posts.
Solution2: convert your java object into MultiValueMap
, and there is already a converter named FormHttpMessageConverter
in spring boot which will convert MultiValueMap
into request body in x-www-form-urlencoded
format automatically.
So in solution2, all you need is to convert your java object into MultiValueMap
:
MultiValueMap<String, String> bodyPair = new LinkedMultiValueMap();
bodyPair.add(K1, V1);
bodyPair.add(K2, V2);
bodyPair.add(K3, V3);
...
K1
, V1
, K2
, V2
, ..., means the field names and corresponding values in your java object. All the fields you declared in your java class need to be added. If there are too many fields, consider using Java Reflection.
Upvotes: 23
Reputation: 76
You can use FormHttpMessageConverter for content-type "application/x-www-form-urlencoded" as below and send data in RestTemplate post method for body as MultiValueMap<String, String>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate" lazy-init="true">
<constructor-arg name="requestFactory" ref="bufferingClientHttpRequestFactory" />
<property name="errorHandler" ref="responeErrorHandler" />
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
Java Config Version
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
return restTemplate
}
Upvotes: -2
Reputation: 131
You can try use https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/FormHttpMessageConverter.html
RestTemplate template = new RestTemplate(...);
template.getMessageConverters().add(new org.springframework.http.converter.FormHttpMessageConverter.FormHttpMessageConverter());
Upvotes: 5
Reputation: 58
If you are using WebMvcConfigurerAdapter
then you might get such errors. Also WebMvcConfigurerAdapter is deprecated from Spring 5.x.x. Try using the WebMvcConfigurer
. Once done you will have to override:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
Upvotes: 0
Reputation: 27781
I ended up having to write a custom HTTP message converter which takes any object and writes it out as www-form-urlencoded content to the request body:
RestTemplate template = new RestTemplate(...);
template.getMessageConverters().add(new ObjectToUrlEncodedConverter(mapper));
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;
public class ObjectToUrlEncodedConverter implements HttpMessageConverter
{
private static final String Encoding = "UTF-8";
private final ObjectMapper mapper;
public ObjectToUrlEncodedConverter(ObjectMapper mapper)
{
this.mapper = mapper;
}
@Override
public boolean canRead(Class clazz, MediaType mediaType)
{
return false;
}
@Override
public boolean canWrite(Class clazz, MediaType mediaType)
{
return getSupportedMediaTypes().contains(mediaType);
}
@Override
public List<MediaType> getSupportedMediaTypes()
{
return Collections.singletonList(MediaType.APPLICATION_FORM_URLENCODED);
}
@Override
public Object read(Class clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException
{
throw new NotImplementedException();
}
@Override
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException
{
if (o != null)
{
String body = mapper
.convertValue(o, UrlEncodedWriter.class)
.toString();
try
{
outputMessage.getBody().write(body.getBytes(Encoding));
}
catch (IOException e)
{
// if UTF-8 is not supporter then I give up
}
}
}
private static class UrlEncodedWriter
{
private final StringBuilder out = new StringBuilder();
@JsonAnySetter
public void write(String name, Object property) throws UnsupportedEncodingException
{
if (out.length() > 0)
{
out.append("&");
}
out
.append(URLEncoder.encode(name, Encoding))
.append("=");
if (property != null)
{
out.append(URLEncoder.encode(property.toString(), Encoding));
}
}
@Override
public String toString()
{
return out.toString();
}
}
}
Upvotes: 28
Reputation: 903
Have you tried something like adding MappingJackson2HttpMessageConverter
to the RestTemplate
restTemplate.getMessageConverters().add(getMappingJackson2HttpMessageConverter());
public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_FORM_URLENCODED));
return mappingJackson2HttpMessageConverter;
}
Upvotes: 6