20

I have a controller action I think should be an HTTP PUT, but Spring is complaining when I try and use @RequestParam in the controller action. Is request parameters not allowed for HTTP PUT methods, and is that why Spring is rejecting it?

@RequestMapping(value = "/{helpDocumentId}/vote", method = RequestMethod.PUT)
public void voteHelpfulness(@PathVariable long helpDocumentId, @RequestParam boolean isHelpful) {
    helpManager.voteOnHelpDocument(helpDocumentId, isHelpful);
}

When executed, it throws this error:

org.springframework.web.bind.MissingServletRequestParameterException: Required boolean parameter 'isHelpful' is not present

Of course, the isHelpful parameter IS present. I can make the above code work perfectly for HTTP POST, so I know this isn't the problem.

     $.ajax({
            url: "/help/" + helpDocumentId + "/vote.json",
            type: "PUT",
            data: {
                isHelpful: isHelpful
            },
            success: function(response) {
                // ....
            }
     });

Is PUT the correct http method? This action modifies the helpDocument, but it doesn't create one.

egervari
  • 22,372
  • 32
  • 121
  • 175
  • 5
    https://jira.springsource.org/browse/SPR-7030 – digitaljoel Nov 23 '11 at 23:25
  • Shucks, I hate bugs in Spring. Always gets me stuck! Thanks – egervari Nov 23 '11 at 23:27
  • Agreed. I'm still looking, hoping that's not the only solution... – digitaljoel Nov 23 '11 at 23:29
  • @digitaljoel, quite late, but since Spring 3.1 there's [HttpPutFormContentFilter](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/filter/HttpPutFormContentFilter.html). – Arjan Jan 28 '13 at 19:16
  • @Arjan Good to know. I simply PUT the data as JSON in the body and handle it from there using the RequestBody annotation. No need for any filters and it Just Works™ – digitaljoel Jan 28 '13 at 19:24
  • Yep, @digitaljoel, I also use `@RequestBody` in many places, and even my MockMvc tests were all fine. Until *one* controller actually was using `@RequestParam`, which still worked perfectly fine with MockMvc's `perform(put(...).param("name", "value"))`, but failed when actually deploying in a servlet container... :-) – Arjan Jan 28 '13 at 19:28
  • Well, @digitaljoel, it Just Works™ when using the correct type for the @RequestBody ;-) (Just saw an issue with trying to handle incoming JSON with `@RequestBody MultiValueMap`.) – Arjan Jan 29 '13 at 20:21
  • Oh, @digitaljoel, maybe Spring/Jackson can actually map JSON to a (nested) MultiValueMap. If you happen to know, then see the comments [here](http://stackoverflow.com/questions/14590640/415-unsupported-media-type-with-spring-3-2/14590793#comment20369434_14590793). Are you mapping to some custom type, plain String, or something generic? – Arjan Jan 29 '13 at 21:30

4 Answers4

15

Since Spring 3.1, HttpPutFormContentFilter can be used to handle application/x-www-form-urlencoded data:

Filter that makes form encoded data available through the ServletRequest.getParameter*() family of methods during HTTP PUT requests.

The Servlet spec requires form data to be available for HTTP POST but not for HTTP PUT requests. This filter intercepts HTTP PUT requests where content type is 'application/x-www-form-urlencoded', reads form encoded content from the body of the request, and wraps the ServletRequest in order to make the form data available as request parameters just like it is for HTTP POST requests.

For other incoming data, such as JSON, you'll need @RequestBody as explained in JQuery, Spring MVC @RequestBody and JSON - making it work together, to not run into a 415 Unsupported Media Type.

Community
  • 1
  • 1
Arjan
  • 22,808
  • 11
  • 61
  • 71
6

Spring controllers support GET/HEAD/POST/PUT/DELETE/OPTIONS/TRACE, but since your browser may not be able to send these request methods, it wont work for you.

The workaround is to use the "org.springframework.web.filter.HiddenHttpMethodFilter" provided by Spring. It requires you to pass a hidden parameter for the request method. The default parameter supported by this filter is "_method".

Check the javadoc of the filter for more info.

praveenj
  • 349
  • 1
  • 2
  • 15
3

This, as suggest above, seems to be a bug in spring/servlet API. In reality PUT requests are supposed to work on Request Body (or payload) and not on Request Parameters. In that sense, servlet API & spring's handling is correct.

Having said that, a better and much easier workaround is to pass no data element from your javascript/jQuery call and pass your parameters as part of the url itself. meaning, set parameters in the url field the way you would do in a GET call.

$.ajax({
            url: "/help/" + helpDocumentId + "/vote.json" + "?param1=param2Val&..",
            type: "PUT",
            data: "",
            success: function(response) {
                // ....
            }
     });

now this works for simple parameters, i guess, will not work for complex JSON types. Hope this helps.

Ravi Bhatt
  • 3,147
  • 19
  • 21
0

I followed the recommendation in the comments and changed @RequestParam to @RequestBody and it just worked (my parameter is a String).

I agree this is a bug in Spring because the exact same code that fails in my production environment (when using @RequestParam) works fine in localhost.

AntonioOtero
  • 1,759
  • 1
  • 14
  • 16