Can I use @Requestparam annotation for a Post request?
Asked Answered
R

5

39

I have this controller method:

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

However when I send a post request to this endpoint I get the following

"{"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"}"

When I remove the params argument from the @PostMapping annotation I get a more general error, it will say that it cannot find the first required parameter (start), while it really is being send together with the parameters end, hours and username.

how to get param in method post spring mvc?

I've read in this post that @RequestParam can only be used for get methods, but if I remove @RequestParam and stick with the params argument of the @PostMapping annotation it still doesn't work. I know I can use @RequestBody but I do not want to make a class just for those 4 parameters. Can anyone tell me how I can make this work?

Thank you

EDIT: I'm reading here https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params-- that the argument params isn't exactly what I thought it was. It seems to be used as a condition. If a set of parameters match a value then the endpoint controller method will be activated.

Ravenravening answered 23/11, 2017 at 14:6 Comment(8)
Judging from the error message and the code you posted the code doesn't match the exception.Papa
Your understanding is wrong. You can use @RequestParam with any type of request. @RequestBody is if you post a body (in JSON, XML etc) and you want it marshaled to an object. If you only want to use parameters then @RequestBody is pretty much useless. However binding to an object should be preferred over separate parameters and for this you should use @ModelAttribute.Papa
the others say @RequestParam cannot be used with post requestsRavenravening
They are wrong... (Having written 3 books on the subject I'm fairly sure I know what I'm talking about :) ). Parameters are parameters regardless the method used to get them to the server. They can even be used to obtain the form parameters when posting a form. Now if you would be sending JSON that would be a different story as that is a body and should be parsed using @RequestBody.Papa
then how come I get an error when I use @RequestParam in the above example? What should I change for it to work? Please enlighten me.Ravenravening
The error isn't about the @RequestParam but the conditions (the params in the @RequestMapping) and as stated I think the code posted here isn't the actual code...Papa
A lot of explanation, but no answer how to get parameters from URL in post method using @RequestParam annotation?!.Ledaledah
@Patzu You could also just use @PathVariable annotation instead.Ravenravening
B
33

What you are asking for is fundamentally wrong. POST requests sends data in a body payload, which is mapped via @RequestBody. @RequestParam is used to map data through the URL parameters such as /url?start=foo. What you are trying to do is use @RequestParam to do the job of @RequestBody.

Alternative solutions for REST controllers

  • Introduce a DTO class. It is the most preferred and clean method.
  • If you really want to avoid creating a class, you can use @RequestBody Map<String, String> payload. Be sure to include 'Content-Type': 'application/json' in your request header.
  • If you really want to use @RequestParam, use a GET request instead and send your data via URL parameters.

Alternative solutions for MVC controllers

  • Introduce a DTO class and use it with annotation @ModelAttribute.
  • If you transform the form data into JSON, you can use @RequestBody Map<String, String> payload. To do this, please see this answer.

It is not possible to map form data encoded data directly to a Map<String, String>.

Biophysics answered 23/11, 2017 at 14:18 Comment(9)
sweet, i'll go for the second option. Thanks allot!Ravenravening
@RequestBody is only useful is you actually send a payload other then form encoding (like JSON or XML) for a simply form post @RequestBody will do nothing then you have to use either @ModelAttribute or use @RequestParam to obtain the form parameters. As stated @RequestParam isn't limited to GET methods.Papa
@Synch When I use your second option I get a Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of java.lang.String out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappin gException: Can not deserialize instance of java.lang.String out of START_OBJECT token error. Any idea how I can prevent this error from happening?Ravenravening
kan ik @ModelAttribute ook gebruiken wanneer ik geen Spring Mvc applicatie heb?Ravenravening
@ModelAttribute werkt ook niet, parameterveld is leegRavenravening
ModelAttribute is a Spring annotation, so you can't use it outside Spring application.Biophysics
I am using spring just not spring mvc, anyway.. I found out what the culprit was. I should include 'Content-Type': 'application/json' in the headers when I make the post. That way the RequestBody map gets filled with parameters properly.Ravenravening
This answer is misleading. RequestParam can be used for form-encoded POST bodies. See docs.spring.io/spring-framework/docs/current/javadoc-api/org/….Prescriptible
using Map to send the data instead of using a separate DTO class is really a better way and it is way easier. And, i found that map works not only for Map<String,String> in REST controllers, but also other types as well.. such as Map<String, Integer> also works.Bemuse
D
41

Well, the answer by @Sync is fundamentally wrong and not the question being asked.

  1. First of all, I use @RequestParam in a lot of scenarios expecting either GET or POST HTTP messages and I'd like to say, that it works perfectly fine;
  2. POST Message's payload data (body) is actually the text data, which can perfectly legally be paramname = paramvalue key-value mapping(s) alike (see POST Message Body types here);
  3. docs.spring.io, an official source for Spring Documentation, clearly states, that:

In Spring MVC, "request parameters" map to query parameters, form data, and parts in multipart requests.

So, the answer is YES, you can use @RequestParam annotation with @Controller class's method's parameter, as long as that method is a handler method (is request-mapped by @RequestMapping) and you don't expect Object, this is perfectly legal and there's nothing wrong with it.

Downpour answered 12/4, 2019 at 13:51 Comment(5)
https://mcmap.net/q/270180/-what-is-the-difference-b-w-requestparam-and-queryparam-anotation here I explain this annotation in a bit deeper way.Downpour
i think what @Sync has said in his answer is that, if you used request parameters in POST, they are sent in the URL and is prone to get exposed. Therefore he suggests to send the data in the request body using the ways he has suggested because, when sending a POST request, the request body is encrypted.Bemuse
Spring docs linked in this answer state: ".. the Servlet API combines query parameters and form data into a single map called "parameters", and that includes automatic parsing of the request body." So POST RequestParameters can be in the body, not exposed in the URL. Sync's discussion of alternatives is useful, but the statement that you have to use RequestBody with POST isn't right. (Possibly it used to be true?)Skinny
(But note also: "In Spring WebFlux, 'request parameters' map to query parameters only")Skinny
@DiliniPeiris: if using ssl / tls all data including query param are encrypted but can find its way in server logs after tls terminationParget
B
33

What you are asking for is fundamentally wrong. POST requests sends data in a body payload, which is mapped via @RequestBody. @RequestParam is used to map data through the URL parameters such as /url?start=foo. What you are trying to do is use @RequestParam to do the job of @RequestBody.

Alternative solutions for REST controllers

  • Introduce a DTO class. It is the most preferred and clean method.
  • If you really want to avoid creating a class, you can use @RequestBody Map<String, String> payload. Be sure to include 'Content-Type': 'application/json' in your request header.
  • If you really want to use @RequestParam, use a GET request instead and send your data via URL parameters.

Alternative solutions for MVC controllers

  • Introduce a DTO class and use it with annotation @ModelAttribute.
  • If you transform the form data into JSON, you can use @RequestBody Map<String, String> payload. To do this, please see this answer.

It is not possible to map form data encoded data directly to a Map<String, String>.

Biophysics answered 23/11, 2017 at 14:18 Comment(9)
sweet, i'll go for the second option. Thanks allot!Ravenravening
@RequestBody is only useful is you actually send a payload other then form encoding (like JSON or XML) for a simply form post @RequestBody will do nothing then you have to use either @ModelAttribute or use @RequestParam to obtain the form parameters. As stated @RequestParam isn't limited to GET methods.Papa
@Synch When I use your second option I get a Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of java.lang.String out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappin gException: Can not deserialize instance of java.lang.String out of START_OBJECT token error. Any idea how I can prevent this error from happening?Ravenravening
kan ik @ModelAttribute ook gebruiken wanneer ik geen Spring Mvc applicatie heb?Ravenravening
@ModelAttribute werkt ook niet, parameterveld is leegRavenravening
ModelAttribute is a Spring annotation, so you can't use it outside Spring application.Biophysics
I am using spring just not spring mvc, anyway.. I found out what the culprit was. I should include 'Content-Type': 'application/json' in the headers when I make the post. That way the RequestBody map gets filled with parameters properly.Ravenravening
This answer is misleading. RequestParam can be used for form-encoded POST bodies. See docs.spring.io/spring-framework/docs/current/javadoc-api/org/….Prescriptible
using Map to send the data instead of using a separate DTO class is really a better way and it is way easier. And, i found that map works not only for Map<String,String> in REST controllers, but also other types as well.. such as Map<String, Integer> also works.Bemuse
L
2

You should use @RequestBody instead of using @RequestParam And you should provide whole object as a body of request @RequestParam is to get data from URL

you can do something like public saveUser(@RequestBody User user) { do something with user }

and it will be mapped as User object for example

Labrador answered 23/11, 2017 at 14:21 Comment(0)
S
0
public void createLeave(@RequestParam Map<String, String> requestParams)

Above code did not work.

Correct syntax is:

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

Which will map the request to a map

Scrabble answered 18/9, 2020 at 11:39 Comment(0)
F
-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);
}

This is for multipart/form-data enctype post request.

Fire answered 17/10, 2018 at 17:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.