3

I'm trying to build a very basic REST API using Spring.

My URL endpoints are:

GET    /notes
GET    /notes/{noteId}
POST   /notes
PUT    /notes/{noteId}
DELETE /notes/{noteId}

All these endpoints work perfectly fine as expected except the PUT request which I want to run to update an item.

The problem is that data is not being received via PUT, where as it works fine for POST.

Here's my controller; I've tested it by adding a method identical to the update method but using POST and that works fine. I don't know why?

package notes;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/notes")
public class NotesController {

    ...

    @RequestMapping(value="/{noteId}", method=RequestMethod.PUT)
    public Response update(@PathVariable Integer noteId, Note note) {

        return new Response("Note Updated", note);

    }

    @RequestMapping(value="/{noteId}", method=RequestMethod.POST)
    public Response updateWithPost(@PathVariable Integer noteId, Note note) {

        return new Response("Note Updated", note);

    }

    ...

}

Using postman, I've tested POST http://127.0.0.1:8080/notes/5 and the response was:

{
    "message": "Note Updated",
    "note": {
        "id": null,
        "title": "Hello World detail content",
        "text": "Hello World",
        "createdAt": "72, Mar 2015",
        "updatedAt": "72, Mar 2015"
    }
}

But for PUT http://127.0.0.1:8080/notes/5 with exactly same data the response was:

{
    "message": "Note Updated",
    "note": {
        "id": null,
        "title": "",
        "text": "",
        "createdAt": "72, Mar 2015",
        "updatedAt": "72, Mar 2015"
    }
}

Update Request For both PUT & POST request I'm sending the same test data:

title: Hello World
text: Hello World detail content

Response Class

package notes;

public class Response {

    private String message;
    private Note note;

    public Response(String text) {
        setMessage(text);
    }

    public Response(String text, Note note) {
        setMessage(text);
        setNote(note);
    }

    //...getters/setters
}

Note Class

package notes;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Note {

    private Integer id = null;
    private String title = "";
    private String text = "";
    private Date createdAt = new Date();
    private Date updatedAt = new Date();

    public Note() {}

    public Note(String title, String text) {
        this.setTitle(title);
        this.setText(text);
    }


    //...getters/setters
}

ApplicationConfig

package notes;

import org.springframework.context.annotation.*;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@EnableWebMvc
@Configuration
public class ApplicationConfig {

}

I don't know why this is not working?

kabirbaidhya
  • 3,264
  • 3
  • 34
  • 59

2 Answers2

4

This is a limitation of the Servlet Spec and the inner workings of Spring for populating model attributes.

First, the spec says

3.1.1 When Parameters Are Available

The following are the conditions that must be met before post form data will be populated to the parameter set:

  1. The request is an HTTP or HTTPS request.
  2. The HTTP method is POST.
  3. The content type is application/x-www-form-urlencoded.
  4. The servlet has made an initial call of any of the getParameter family of methods on the request object. If the conditions are not met and the post form data is not included in the parameter set, the post data must still be available to the servlet via the request object's input stream. If the conditions are met, post form data will no longer be available for reading directly from the request object's input stream.

Second, your handler methods' second parameter, the one of type Note, is actually considered a model attribute, as if it was implicitly annotated with @ModelAttribute. As such, it is processed by Spring's ModelAttributeMethodProcessor. This HandlerMethodArgumentResolver uses the getParameter (and its family of methods) for populating the created instance's fields.

Since this is a PUT request, the parameters are not retrievable through getParameter. However, they are still accessible through the request body, but Spring doesn't go there for model attributes.

You can do the conversion yourself. But I suggest you change your request content to JSON or XML for PUT and use @RequestBody annotated parameters.


There is a long standing request to add parameters for PUT requests.

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 1
    Thank you so much. Sending the request payload in json & `@RequestBody` annotation did the job and finally it worked. Plus in my CORS filter I had to add `PUT` method in `Access-Control-Allow-Methods` header to allow it. – kabirbaidhya Mar 13 '15 at 09:31
0

Don't know if it's the case here (or if it is even relevant nowadays, haven't worked with REST-stuff for a while), but I've hit a problem in the past where Jetty and Tomcat handled PUT-requests differently (the application itself was using Spring MVC similarly as your code), the other expected the parameters to be in the URL and the other wanted them in the body, like with POST.

Apparently the HTTP-spec isn't very precise on this, so different containers can handle it differently. More information here.

esaj
  • 15,875
  • 5
  • 38
  • 52