我可以为Post请求使用@Requestparam注解吗?

38

我有这个控制器方法:

@PostMapping(
        value = "/createleave",
        params = {"start","end","hours","username"})
public void createLeave(@RequestParam(value = "start") String start,
                        @RequestParam(value = "end") String end,
                        @RequestParam(value = "hours") String hours,
                        @RequestParam(value = "username") String username){
    System.out.println("Entering createLeave " + start + " " + end + " " + hours + " " + username);
    LeaveQuery newLeaveQuery = new LeaveQuery();
    Account account = accountRepository.findByUsername(username);
    newLeaveQuery.setAccount(account);
    newLeaveQuery.setStartDate(new Date(Long.parseLong(start)));
    newLeaveQuery.setEndDate(new Date(Long.parseLong(end)));
    newLeaveQuery.setTotalHours(Integer.parseInt(hours));
    leaveQueryRepository.save(newLeaveQuery);
}

然而,当我向这个端点发送POST请求时,我会收到以下响应:

"{"timestamp":1511444885321,"status":400,"error":"Bad Request","exception":"org.springframework.web.bind.UnsatisfiedServletRequestParameterException","message":"Parameter conditions \"start, end, hours, username\" not met for actual request parameters: ","path":"/api/createleave"}"
当我从@PostMapping注解中删除params参数时,我会收到一个更普遍的错误,它会说找不到第一个必需的参数(开始),而实际上它与参数end、hours和username一起被发送。如何在方法post spring mvc中获取参数?我在这篇文章中读到@RequestParam只能用于get方法,但如果我删除@RequestParam并坚持使用@PostMapping注释的params参数,它仍然无法正常工作。我知道我可以使用@RequestBody,但我不想为那4个参数创建一个类。有人可以告诉我如何使这个工作吗?谢谢编辑:我在这里阅读 https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params-- ,发现参数params并不是我想象的那样,它似乎被用作条件。如果一组参数匹配一个值,则激活端点控制器方法。

从错误信息和您发布的代码来看,代码与异常不匹配。 - M. Deinum
2
你的理解是错误的。你可以在任何类型的请求中使用@RequestParam。如果你要发布一个主体(以JSON、XML等形式),并且你想将其编组为一个对象,那么你应该使用@RequestBody。如果你只想使用参数,那么@RequestBody基本上是无用的。然而,绑定到一个对象应该优先于单独的参数,为此你应该使用@ModelAttribute - M. Deinum
其他人说@RequestParam不能与POST请求一起使用。 - Maurice
很多解释,但没有回答如何在使用@RequestParam注释的POST方法中从URL获取参数?! - Davoud Badamchi
@Patzu,你也可以使用@PathVariable注释。 - Maurice
显示剩余3条评论
5个回答

38

嗯,@Sync的答案基本上是错误的,并不是提出的问题。

  1. 首先,我在许多场景中使用@RequestParam,期望获得GET或POST HTTP消息,我想说,它完美地工作;
  2. POST消息的有效负载数据(body)实际上是文本数据,可以完全合法地像paramname = paramvalue键值映射一样(请参见POST Message Body types);
  3. docs.spring.io是Spring文档的官方来源,明确说明:

在Spring MVC中,“请求参数”映射到查询参数、表单数据和多部分请求中的部件。

所以,答案是肯定的,您可以在@Controller类的方法参数中使用@RequestParam注释,只要该方法是一个处理程序方法(由@RequestMapping映射请求),并且您不希望返回对象,这是完全合法的,没有任何问题。

在这里,我会更深入地解释一下这个注释。 - Giorgi Tsiklauri
1
我认为@Sync在他的回答中所说的是,如果您在POST请求中使用了请求参数,它们将被发送到URL中并容易暴露。因此,他建议使用他提出的方法将数据发送到请求正文中,因为在发送POST请求时,请求正文是加密的。 - Dilini Peiris
1
此答案中链接的Spring文档指出:“... Servlet API将查询参数和表单数据合并为一个名为“parameters”的映射,并包括请求正文的自动解析。”因此,POST请求参数可以在请求正文中,而不是暴露在URL中。Sync对替代方案的讨论很有用,但声称必须使用RequestBody与POST不正确。(可能以前是这样?) - Bampfer
在Spring WebFlux中,“请求参数”仅映射到查询参数。 - Bampfer
1
如果使用 SSL/TLS,则包括查询参数在内的所有数据都将被加密,但在 TLS 终止后可能会在服务器日志中找到。 - Khanna111

32
你所请求的方式基本上是错误的。POST请求会将数据发送到消息体中,而这些数据会通过@RequestBody进行映射。而@RequestParam则用于通过URL参数(如/url?start=foo)来映射数据。你所尝试做的是使用@RequestParam来完成@RequestBody的工作。

REST控制器的替代解决方案

  • 引入DTO类。这是最受欢迎和简洁的方法。
  • 如果你真的想避免创建一个类,可以使用@RequestBody Map<String, String> payload。请确保在请求头中包含'Content-Type': 'application/json'
  • 如果你真的想使用@RequestParam,则使用GET请求,并通过URL参数发送数据。

MVC控制器的替代解决方案

  • 引入DTO类,并将其与注解@ModelAttribute一起使用。
  • 如果你将表单数据转换为JSON,则可以使用@RequestBody Map<String, String> payload。要做到这一点,请参见此答案

无法直接将表单数据编码数据映射到Map<String, String>中。


1
甜的,我会选择第二个选项。非常感谢! - Maurice
1
@RequestBody 只有在发送负载(如 JSON 或 XML)而不是表单编码时才有用,对于简单的表单提交,@RequestBody 将不起作用,然后您必须使用 @ModelAttribute 或使用 @RequestParam 来获取表单参数。正如所述,@RequestParam 不仅限于 GET 方法。 - M. Deinum
@Synch 当我使用你的第二个选项时,我遇到了一个“无法读取HTTP消息:org.springframework.http.converter.HttpMessageNotReadableException: JSON解析错误:无法将java.lang.String的实例反序列化为START_OBJECT令牌;嵌套异常是com.fasterxml.jackson.databind.JsonMappingException:无法将java.lang.String的实例反序列化为START_OBJECT令牌”错误。有什么办法可以防止这种错误发生吗? - Maurice
1
我正在使用Spring,只是不是Spring MVC。无论如何,我找到了罪魁祸首。当我进行POST请求时,应该在headers中包含'Content-Type': 'application/json'。这样RequestBody映射就会正确地填充参数。 - Maurice
1
这个答案是误导性的。RequestParam可以用于表单编码的POST请求体。请参阅https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html。 - jchamberlain
显示剩余4条评论

3
您应该使用@RequestBody而不是@RequestParam,并将整个对象作为请求体提供。 @RequestParam用于从URL获取数据。
您可以像下面这样做: public saveUser(@RequestBody User user) { do something with user } 然后它将被映射为User对象。

0
public void createLeave(@RequestParam Map<String, String> requestParams)

以上代码无法工作。

正确的语法是:

public void createLeave(@RequestBody Map<String, String> requestParams)

这将把请求映射到一个映射表中


-2
@PostMapping("/createleave")
public void createLeave(@RequestParam Map<String, String> requestParams){

    String start = requestParams.get("start");
    String end= requestParams.get("end");
    String hours= requestParams.get("hours");
    String username = requestParams.get("username");

    System.out.println("Entering createLeave " + start + " " + end + " " + hours + " " + username);
}

这是用于multipart/form-data编码的POST请求。


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