【问题标题】:Http Post with request content type form not working in Spring MVC 3具有请求内容类型表单的 Http Post 在 Spring MVC 3 中不起作用
【发布时间】:2011-05-19 08:39:24
【问题描述】:

代码sn-p:

@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");
}

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

如果我把代码改成这样:

代码sn-p:

@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");
}

我会得到 405 Method not allowed。有趣的是在响应的允许标头中,它将 GET 和 POST 列为允许的方法。

我确实有一个做 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;
}

}

复制自 Spring 示例。非常适合 JSON 内容类型。

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

【问题讨论】:

    标签: spring-mvc content-type


    【解决方案1】:

    不幸的是,FormHttpMessageConverter(当内容类型为application/x-www-form-urlencoded 时用于@RequestBody 注释的参数)无法绑定目标类(@ModelAttribute 可以)。

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

    @RequestMapping(method = RequestMethod.POST)
    public ModelAndView create(@ModelAttribute UserAccountBean account) { ... }
    

    否则我猜你可以使用适当的headers 属性创建一个单独的方法表单处理表单数据:

    @RequestMapping(method = RequestMethod.POST, 
        headers = "content-type=application/x-www-form-urlencoded") 
    public ModelAndView createFromForm(@ModelAttribute UserAccountBean account) { ... }
    

    编辑:另一个可能的选择是通过结合FormHttpMessageConverter(将输入消息转换为参数映射)和WebDataBinder(将参数映射转换为目标对象)。

    【讨论】:

    • 顺便说一句,你从哪里知道的?来自任何参考文档(如果是,你能分享一下吗?)或尝试错误?
    • @Bobo:FormHttpMessageConverter 产生 MultiValueMap 的事实可以在其 javadoc 中找到:static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…
    • 糟糕。没有检查javadoc。我不太明白的另一件事是为什么 spring 框架不会检测到它并在部署时向我抛出错误消息。
    【解决方案2】:

    我的 HTTP 响应代码为 415

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

    例如

    “内容类型:应用程序/json”

    【讨论】:

      【解决方案3】:

      问题的核心是,我们希望通过相同的请求处理程序同时接受 application/json 和 application/x-www-form-urlencoded Content-types。

      为此,我使用了 @RequestBody,它已经为我的 application/json 工作(通常是我发现的线程中的其他人,但还有额外的工作,所以 application/x-www-form-urlencoded可以与@RequestBody 一起使用。

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

      这里是 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("");
          }
      }
      

      Spring 应用程序可以通过多种不同的方式获取该消息转换器。对我来说,它是在一个 XML 文件中完成的:

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

      【讨论】:

      • 谢谢。 4 年后,这是我发现一般转换为 @RequestBody 对象的唯一方法。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        使用 JSON 也对我有用,我想它使 JSON 解释器从正文中获取数据。 不过,我试图使用 PUT,这有点困难。 你可以阅读我的帖子here

        【讨论】:

          【解决方案6】:

          以下对我有用

          在服务器端:

           @RequestMapping(value = "test", method = RequestMethod.POST, consumes = {"application/xml", "application/json"})
                  @ResponseStatus(HttpStatus.OK)
                  public @ResponseBody
                  String methodName(@RequestBody EntityClassName entity) {
          

          在客户端:

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

            我使用此代码将 html 表单转换为 json 。

            function ConvertFormToJSON(form) {
                                    var array = $(form).serializeArray();
                                    var json = {};
            
                                    $.each(array, function() {
                                        json[this.name] = this.value || '';
                                    });
            
                                    return json;
                                }
            

            并且使用单引号是错误的。我把 ' ' 改成 " " 问题就解决了。

            【讨论】:

              猜你喜欢
              • 2016-04-19
              • 2019-05-04
              • 2018-08-06
              • 1970-01-01
              • 2015-02-18
              • 2018-09-03
              • 1970-01-01
              • 2015-09-30
              • 2018-03-03
              相关资源
              最近更新 更多