请求体中的内容类型'application/x-www-form-urlencoded;charset=UTF-8'不支持@RequestBody MultiValueMap。

166

基于解决 Spring @Controller 中 x-www-form-urlencoded 的问题的答案

我编写了以下 @Controller 的方法:

@RequestMapping(value = "/{email}/authenticate", method = RequestMethod.POST
            , produces = {"application/json", "application/xml"}
            ,  consumes = {"application/x-www-form-urlencoded"}
    )
     public
        @ResponseBody
        Representation authenticate(@PathVariable("email") String anEmailAddress,
                                    @RequestBody MultiValueMap paramMap)
                throws Exception {


            if(paramMap == null || paramMap.get("password") == null) {
                throw new IllegalArgumentException("Password not provided");
            }
    }

请求失败,错误信息如下:

{
  "timestamp": 1447911866786,
  "status": 415,
  "error": "Unsupported Media Type",
  "exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
  "message": "Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported",
  "path": "/users/usermail%40gmail.com/authenticate"
}

[PS:泽西(Jersey)更加友好,但由于实际限制,现在无法使用它]


1
你在 @RequestBody 中添加了 consumes = {"application/x-www-form-urlencoded"} 吗? - shiladitya
1
你是如何执行请求的?请添加你使用的代码(js,jquery,curl或其他)。 - Nikolay Rusev
我有同样的问题。在我的情况下,我使用jQuery AJAX来发送数据,而这些数据是 JSON.stringify({"ordersToDownload":"00417002"}) - Arashsoft
这是我使用的代码:$.ajax({url:"/myurl", type:"POST", data: JSON.stringify({"someAttribute":"someData"}) }) - Arashsoft
请查看我的答案 链接描述 - Eshiett Oto-obong
12个回答

211
问题在于当我们使用 application/x-www-form-urlencoded 时,Spring 没有将其理解为 RequestBody。因此,如果我们想使用它,我们必须删除 @RequestBody 注释。 然后尝试以下内容:
@RequestMapping(
  path = "/{email}/authenticate", 
  method = RequestMethod.POST,
  consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, 
  produces = {
    MediaType.APPLICATION_ATOM_XML_VALUE, 
    MediaType.APPLICATION_JSON_VALUE
  })
public @ResponseBody Representation authenticate(
  @PathVariable("email") String anEmailAddress, 
  MultiValueMap paramMap) throws Exception {

  if (paramMap == null && 
      paramMap.get("password") == null) {
     throw new IllegalArgumentException("Password not provided");
  }
  return null;
}

请注意,已删除注释@RequestBody答案: Spring中使用application/x-www-form-urlencoded内容类型进行Http Post请求无法正常工作

1
无需@kholofeloMaloma - Douglas Ribeiro
5
如果有人想知道为什么这个功能可以在没有任何注释的情况下工作,似乎 Spring 会将任何非注释参数处理为如果它们有 @ModelAttribute,即使这种行为(可悲地)没有记录在文档中。而 @ModelAttribute 确实理解 x-www-form-urlencoded。 - bluemind
2
公共 ResponseEntity<?> getToken(MultiValueMap paramMap) 非法参数异常:参数类型不匹配 - withoutOne
我认为应该使用OR而不是AND(如果paraMap为空,则get将产生NPE)。if(paramMap == null || paramMap.get("password") == null) { throw new IllegalArgumentException("未提供密码"); } - MarkAddison
给我一个错误:接口org.apache.commons.collections4.MultiValuedMap没有找到主要或唯一的构造函数。 - Shabbir Essaji
显示剩余2条评论

100

现在似乎只需要用@RequestParam标记方法参数,它就可以为您完成工作。

@PostMapping( "some/request/path" )
public void someControllerMethod( @RequestParam Map<String, String> body ) {
  //work with Map
}

2
这在Spring Boot 2.4.2上是有效的。 - mending3

23

在你的请求中添加一个头部,将内容类型设置为application/json。

curl -H 'Content-Type: application/json' -s -XPOST http://your.domain.com/ -d YOUR_JSON_BODY

这样Spring就知道如何解析内容。


1
你可能需要在命令中添加一个Accept头部:'curl -vk -H "Accept: application/json" -H "Content-Type: application/json" '等等。 - razvanone
1
请问您能否解释一下如何将此设置添加到我的HTML表单中? - Osama Al-Banna

19

在Spring 5中

@PostMapping( "some/request/path" )
public void someControllerMethod( @RequestParam MultiValueMap body ) {

    // import org.springframework.util.MultiValueMap;

    String datax = (String) body .getFirst("datax");
}

1
是的,在映射中包含consumer=MediaType.APPLICATION_FORM_URLENCODED_VALUE,您应该得到更多的积分!谢谢!现在似乎需要使用@RequestParam来从请求中获取MultiValueMap。 - Toza

14

在这里移除@RequestBody注解

@RequestBody MultiValueMap paramMap

@RequestMapping(value = "/signin",method = RequestMethod.POST)
public String createAccount(@RequestBody LogingData user){
    logingService.save(user);
    return "login";
}

@RequestMapping(value = "/signin",method = RequestMethod.POST)
public String createAccount( LogingData user){
    logingService.save(user);
    return "login";
} 

就像这样


7
虽然这段代码可能解决了问题,但是包括一份解释来阐明它是如何解决问题的以及为什么能够解决问题,将有助于提高你的回答质量,可能会得到更多的赞同。请记住,你正在回答未来的读者,而不仅仅是现在提问的人。请[编辑]你的回答添加解释,并给出适用的限制和假设指示。 - Yunnosch

8

只需删除 @RequestBody 注释即可解决问题(在Spring Boot 2上测试通过):

@RestController
public class MyController {

    @PostMapping
    public void method(@Valid RequestDto dto) {
       // method body ...
    }
}

4

在Spring MVC中,当我们想要处理简单的HTML表单提交时(不使用thymeleaf或Spring的form标签),我遇到了同样的问题。

Douglas Ribeiro的答案非常有效。但是为了防止万一,对于像我一样真正想在Spring MVC中使用"@RequestBody"的任何人,以下是该问题的原因

  • Spring需要①识别"Content-Type"和②将内容转换为我们在方法签名中声明的参数类型。
  • "application/x-www-form-urlencoded"不受支持,因为默认情况下,Spring找不到适当的HttpMessageConverter来执行转换工作,即步骤②。

解决方案:

  • 适当的HttpMessageConverter手动添加到我们应用程序的Spring配置中。

步骤:

  1. 选择我们要使用的HttpMessageConverter类。对于“application/x-www-form-urlencoded”,我们可以选择“org.springframework.http.converter.FormHttpMessageConverter”。
  2. 通过调用我们应用程序中"WebMvcConfigurer"实现类的"public void configureMessageConverters(List<HttpMessageConverter<?>> converters)"方法,将FormHttpMessageConverter对象添加到Spring的配置中。在该方法内部,我们可以根据需要使用“converters.add()”添加任何HttpMessageConverter对象。

顺便说一下,我们可以通过"@RequestParam"访问值的原因是:

根据Servlet规范(第3.1.1节):

在将POST表单数据填充到参数集之前,必须满足以下条件:请求是HTTP或HTTPS请求。 2. HTTP方法为POST。 3.内容类型为application/x-www-form-urlencoded。 4. servlet已对请求对象上的任何getParameter系列方法之一进行了初始调用。

所以,请求体中的值将被填充到参数中。但是在Spring中,即使您可以在同一方法的签名中使用@RequestBody和@RequestParam,您仍然可以访问RequestBody。例如:
@RequestMapping(method = RequestMethod.POST, consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
public String processForm(@RequestParam Map<String, String> inputValue,  @RequestBody MultiValueMap<String, List<String>> formInfo) {
    ......
    ......
}
和包含相同的数据,除了“@RequestParam”的类型为Map,而“@RequestBody”的类型为MultiValueMap


4
@PostMapping(path = "/my/endpoint", consumes = { MediaType.APPLICATION_FORM_URLENCODED_VALUE })
public ResponseEntity<Void> handleBrowserSubmissions(MyDTO dto) throws Exception {
    ...
}

那种方法对我很有效。

这是最简单的答案;在Spring Boot 2.6.0上的@RestController对我有效。 - Hermann Steidel

2

我在这个StackOverflow答案中介绍了一种替代方案。

其中,我通过代码逐步解释了步骤。简单来说,分为以下几步:

第一步:编写一个对象

第二步:创建一个转换器,将模型映射到扩展AbstractHttpMessageConverter的类上

第三步:实现WebMvcConfigurer.class并覆盖configureMessageConverters方法,告诉Spring使用此转换器

第四步和最后一步:在控制器内设置consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,并在对象前面加上@RequestBody,使用此实现进行映射。

我正在使用Spring Boot 2。


0

你可以尝试在Spring的转换器中打开支持

@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // add converter suport Content-Type: 'application/x-www-form-urlencoded'
        converters.stream()
                .filter(AllEncompassingFormHttpMessageConverter.class::isInstance)
                .map(AllEncompassingFormHttpMessageConverter.class::cast)
                .findFirst()
                .ifPresent(converter -> converter.addSupportedMediaTypes(MediaType.APPLICATION_FORM_URLENCODED_VALUE));
    }

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接