Http Post with request content type form not working in Spring MVC 3
code snippet:
@RequestMapping(method = RequestMethod.POST)//, headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView create(@RequestBody UserAccountBean account) {
try{
accounts.put(account.assignId(), account);
}catch(RuntimeException ex)
{
return new ModelAndView("account/registerError");
}
return new ModelAndView("account/userVerification");
}
After receiving request, What I got is Http Status code 415: The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().
If I change the code to this:
code snippet:
@RequestMapping(method = RequestMethod.POST,headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView create(@RequestBody UserAccountBean account) {
try{
accounts.put(account.assignId(), account);
}catch(RuntimeException ex)
{
return new ModelAndView("account/registerError");
}
return new ModelAndView("account/userVerification");
}
I will get 405 Method not allowed. Funny thing is in the allow header of response, it lists GET and POST as allowed methods.
I do have a class that does JOSN mapping:
@Component
public class JacksonConversionServiceConfigurer implements BeanPostProcessor {
private final ConversionService conversionService;
@Autowired
public JacksonConversionServiceConfigurer(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AnnotationMethodH开发者_如何转开发andlerAdapter) {
AnnotationMethodHandlerAdapter adapter = (AnnotationMethodHandlerAdapter) bean;
HttpMessageConverter<?>[] converters = adapter.getMessageConverters();
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJacksonHttpMessageConverter) {
MappingJacksonHttpMessageConverter jsonConverter = (MappingJacksonHttpMessageConverter) converter;
jsonConverter.setObjectMapper(new ConversionServiceAwareObjectMapper(this.conversionService));
}
}
}
return bean;
}
}
Copied from Spring examples. works great with JSON content-type.
A more general question is how to make spring mvc request handlers work with different request content-types. Any advice would be greatly appreciated.
Unfortunately FormHttpMessageConverter
(which is used for @RequestBody
-annotated parameters when content type is application/x-www-form-urlencoded
) cannot bind target classes (as @ModelAttribute
can).
Therefore you need @ModelAttribute
instead of @RequestBody
. If you don't need to pass different content types to that method you can simply replace the annotation:
@RequestMapping(method = RequestMethod.POST)
public ModelAndView create(@ModelAttribute UserAccountBean account) { ... }
Otherwise I guess you can create a separate method form processing form data with the appropriate headers
attribute:
@RequestMapping(method = RequestMethod.POST,
headers = "content-type=application/x-www-form-urlencoded")
public ModelAndView createFromForm(@ModelAttribute UserAccountBean account) { ... }
EDIT: Another possible option is to implement your own HttpMessageConverter
by combining FormHttpMessageConverter
(to convert input message to the map of parameters) and WebDataBinder
(to convert map of parameters to the target object).
I was having HTTP response code of 415
My problems were resolved when I added Content Type to request header
e.g
"Content-Type: application/json"
At the heart of the problem, we wish to accept both application/json and application/x-www-form-urlencoded Content-types with the same request handler.
To do this, I use the @RequestBody, which was already working for application/json for me (and generally others from the threads I've found, but there is extra work so application/x-www-form-urlencoded can be used with @RequestBody.
First, create a new HttpMessageConverter capable of changing the request input to an object. I do this by reusing the FormHttpMessageConverter, which is already capable of changing the input to a MultiValueMap. I then change the MultiValueMap to a regular Map, and use Jackson to turn the Map to the desired object.
Here is the code for the HttpMessageConverter:
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.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* <p>Converts HTTP requests with bodies that are application/x-www-form-urlencoded or multipart/form-data to an Object
* annotated with {@link org.springframework.web.bind.annotation.RequestBody} in the the handler method.
*
* @author Jesse Swidler
*/
public class ObjectHttpMessageConverter implements HttpMessageConverter<Object> {
private final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
private final ObjectMapper objectMapper = new ObjectMapper();
private static final LinkedMultiValueMap<String, String> LINKED_MULTI_VALUE_MAP = new LinkedMultiValueMap<>();
private static final Class<? extends MultiValueMap<String, ?>> LINKED_MULTI_VALUE_MAP_CLASS
= (Class<? extends MultiValueMap<String, ?>>) LINKED_MULTI_VALUE_MAP.getClass();
@Override
public boolean canRead(Class clazz, MediaType mediaType) {
return objectMapper.canSerialize(clazz) && formHttpMessageConverter.canRead(MultiValueMap.class, mediaType);
}
@Override
public boolean canWrite(Class clazz, MediaType mediaType) {
return false;
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return formHttpMessageConverter.getSupportedMediaTypes();
}
@Override
public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
Map<String, String> input = formHttpMessageConverter.read(LINKED_MULTI_VALUE_MAP_CLASS, inputMessage).toSingleValueMap();
return objectMapper.convertValue(input, clazz);
}
@Override
public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws UnsupportedOperationException {
throw new UnsupportedOperationException("");
}
}
There are many different ways a Spring app might pick up that message converter. For me, it was accomplished in an XML file:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
Using @ModelAttribute is indeed the preferred way to deal with form parameters.
Using JSON worked for me as well, I suppose it makes the JSON interpreter get the data from the body. I was trying to use PUT though, which is a bit harder. You can read my post about it here.
Below worked for me
On server side:
@RequestMapping(value = "test", method = RequestMethod.POST, consumes = {"application/xml", "application/json"})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody
String methodName(@RequestBody EntityClassName entity) {
On client side:
String json = new JSONStringer().object()
.key("key").value("value")
.endObject()
.toString();
StringEntity se = new StringEntity(json);
se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
request.setEntity(se);
HttpResponse response = client.execute(request);
I use this code for convert html form to json .
function ConvertFormToJSON(form) {
var array = $(form).serializeArray();
var json = {};
$.each(array, function() {
json[this.name] = this.value || '';
});
return json;
}
and use single quotations was wrong . I changed ' ' to " " and problem solved.
精彩评论