在Spring MVC 3中不使用请求内容类型的Http Post。

时间:2022-11-23 17:46:42

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 ().

收到请求后,我得到的是Http状态代码415:服务器拒绝此请求,因为请求实体的格式不受请求的方法()请求资源的支持。

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.

我将得到不允许的405方法。有趣的是,在允许的响应头中,它列出了GET和POST作为允许的方法。

I do have a class that does JOSN mapping:

我有一个类做JOSN映射:

@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 AnnotationMethodHandlerAdapter) {
        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.

复制从春天的例子。非常适合JSON内容类型。

A more general question is how to make spring mvc request handlers work with different request content-types. Any advice would be greatly appreciated.

一个更普遍的问题是如何使spring mvc请求处理程序处理不同的请求内容类型。如有任何建议,将不胜感激。

7 个解决方案

#1


53  

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).

不幸的是,FormHttpMessageConverter(在内容类型为应用程序/x-www-form-urlencode时用于@ requestbody -注释的参数)不能绑定目标类(@ModelAttribute可以)。

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:

因此,您需要@ModelAttribute而不是@RequestBody。如果您不需要将不同的内容类型传递给该方法,您可以简单地替换注释:

@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:

否则,我猜您可以创建一个单独的方法表单数据,并使用适当的header属性:

@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).

编辑:另一种可能的选择是,通过将FormHttpMessageConverter(将输入消息转换为参数映射)和WebDataBinder(将参数的映射转换为目标对象)来实现自己的HttpMessageConverter。

#2


22  

I was having HTTP response code of 415

我的HTTP响应码是415。

My problems were resolved when I added Content Type to request header

当我添加内容类型到请求头时,我的问题就解决了。

e.g

"Content-Type: application/json"

“application / json内容类型:

#3


3  

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.

在问题的核心,我们希望同时接受应用程序/json和应用程序/x-www-form-urlencode内容类型,并使用相同的请求处理程序。

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.

为了做到这一点,我使用@RequestBody,它已经为我(以及我找到的线程中的其他线程)工作了(一般来说,还有一些其他的工作,所以应用程序/x-www-form-urlencode可以与@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.

首先,创建一个新的HttpMessageConverter,它能够将请求输入更改为一个对象。我通过重用FormHttpMessageConverter来实现这一点,它已经能够将输入更改为MultiValueMap。然后,我将MultiValueMap更改为常规映射,并使用Jackson将映射转换为所需的对象。

Here is the code for the HttpMessageConverter:

下面是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:

有很多不同的方式,一个Spring应用程序可能会选择这个消息转换器。对我来说,它是在XML文件中完成的:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>

#4


2  

Using @ModelAttribute is indeed the preferred way to deal with form parameters.

使用@ModelAttribute确实是处理表单参数的首选方法。

#5


0  

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.

使用JSON也适用于我,我认为它使JSON解释器从正文获得数据。我试着用PUT,这有点难。你可以在这里看到我的帖子。

#6


0  

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);

#7


0  

I use this code for convert html form to json .

我使用此代码将html表单转换为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.

使用单引号是错误的。我改了“to”,问题就解决了。

#1


53  

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).

不幸的是,FormHttpMessageConverter(在内容类型为应用程序/x-www-form-urlencode时用于@ requestbody -注释的参数)不能绑定目标类(@ModelAttribute可以)。

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:

因此,您需要@ModelAttribute而不是@RequestBody。如果您不需要将不同的内容类型传递给该方法,您可以简单地替换注释:

@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:

否则,我猜您可以创建一个单独的方法表单数据,并使用适当的header属性:

@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).

编辑:另一种可能的选择是,通过将FormHttpMessageConverter(将输入消息转换为参数映射)和WebDataBinder(将参数的映射转换为目标对象)来实现自己的HttpMessageConverter。

#2


22  

I was having HTTP response code of 415

我的HTTP响应码是415。

My problems were resolved when I added Content Type to request header

当我添加内容类型到请求头时,我的问题就解决了。

e.g

"Content-Type: application/json"

“application / json内容类型:

#3


3  

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.

在问题的核心,我们希望同时接受应用程序/json和应用程序/x-www-form-urlencode内容类型,并使用相同的请求处理程序。

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.

为了做到这一点,我使用@RequestBody,它已经为我(以及我找到的线程中的其他线程)工作了(一般来说,还有一些其他的工作,所以应用程序/x-www-form-urlencode可以与@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.

首先,创建一个新的HttpMessageConverter,它能够将请求输入更改为一个对象。我通过重用FormHttpMessageConverter来实现这一点,它已经能够将输入更改为MultiValueMap。然后,我将MultiValueMap更改为常规映射,并使用Jackson将映射转换为所需的对象。

Here is the code for the HttpMessageConverter:

下面是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:

有很多不同的方式,一个Spring应用程序可能会选择这个消息转换器。对我来说,它是在XML文件中完成的:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.terminal.core.services.config.ObjectHttpMessageConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>

#4


2  

Using @ModelAttribute is indeed the preferred way to deal with form parameters.

使用@ModelAttribute确实是处理表单参数的首选方法。

#5


0  

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.

使用JSON也适用于我,我认为它使JSON解释器从正文获得数据。我试着用PUT,这有点难。你可以在这里看到我的帖子。

#6


0  

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);

#7


0  

I use this code for convert html form to json .

我使用此代码将html表单转换为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.

使用单引号是错误的。我改了“to”,问题就解决了。