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
stringTo use it, we need to select the x-www-form-urlencoded tab in the body of their request. We need to enter the key-value pairs for sending the request body to the server, and Postman will encode the desired data before sending it. Postman encodes both the key and the value.
setRequestMethod("POST"); connection. setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection. setRequestProperty("Content-Length", "" + Integer. toString(urlParameters.
Now notice, we have used @ModelAttribute above to map the incoming application/x-www-form-urlencoded or multipart/form-data directly to the Student model class. The @ModelAttribute is an annotation that binds a method parameter or method return value to a named model attribute and then exposes it to a web view.
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();
}
}
}
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(K1, V2);
bodyPair.add(K2, V2);
...
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With