2010-02-10

REST with Spring 3.0, Spring MVC and Dojo. Part 4 - PUT (updating objects)

In the fourth part of the REST/Spring 3.0/MVC/Dojo series we will take a look at the update case, which is done with PUT in REST. Please check out the table below:

Operation REST method Sample URI
Return item(s) GET /books (all books)
/books/ (all books)
/books/12 (one book with ID = 12)
Create a new item POST (you pass the ID in the object itself)
/books
/books/
Update an item PUT (you pass the ID in the object itself)
/books
/books/
Delete an item DELETE /books/12 (book with ID = 12)

PUT for update is very similar to POST for create. There are two differences and one of them will make this part interesting: one is that we pass the actual ID of the object to update (not really interesting), the other is the case when the object to update does not exist in the system (interesting as we will send 404 back to the user).

What happens if the object does not exist? We want to throw an exception, but a regular exception thrown and not caught while handling gives a 500 (Internal server error) on the client side. But that's not what we want. What do we want? Well, since the object was not found, we want to give the client a 404. But first, let's look at the controller.

Handling PUT with Spring MVC


@RequestMapping(value = "/books", method = RequestMethod.PUT)
    public BookTO handleBookUpdate(HttpServletResponse response,
            @RequestBody @Valid BookTO bookTO) {
        BooksBean booksBean = Factory.getBooksBean();
        return booksBean.updateBook(bookTO);
    }

So, this is a sample action. As you can see, the RequestMapping value is identical to the one from the POST (create) action. Not a problem - the method is now PUT. We also have the RequestBody and Valid annotations known from the previous part.

OK, let's look at the Dojo part!

Handling PUT from the client perspective - Dojo


function updateBook(bookId, bookName) {
    var bookToUpdate = {
            "id":bookId,
            "name":bookName
    };
    var actualContent = dojo.toJson(bookToUpdate);
    
    //Save.
    var deferred = this._request("rawXhrPut", {
        url: "http://localhost:8080/restsample-0.0.1-SNAPSHOT/rest/books.json",
        handleAs: "json",
        putData: actualContent,
        headers: { "Content-Type": "application/json"}
    });
    deferred.addCallback(this, function(value) {
        alert("Updated! " + value.bookTO.id + ", " + value.bookTO.name);
    });
    deferred.addErrback(this, function(value) {                
          alert("Error: "  + value);
    });    
}

Nothing very cool here - except that we use Dojo rawXhrPut instead of xhrPut and putData instead of postData.

Throwing an exception that ends up as 404


Finally, let's look at the exception handling so that we get a 404.

@RequestMapping(value = "/books", method = RequestMethod.PUT)
    public BookTO handleBookUpdate(HttpServletResponse response,
            @RequestBody @Valid BookTO bookTO) throws IOException {
        BooksBean booksBean = Factory.getBooksBean();
        try {
            return booksBean.updateBook(bookTO);
        } catch (BookNotFoundException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return new BookTO();
        }
    }

This code catches a BookNotFoundException. If the problem occurs, it writes a 404 to response and returns an empty object.

This is by no means perfect. An ideal way would be to be able to throw the exception without having to worry about writing a 404. This is something that perhaps Spring MVC could handle. I am not aware of such possibility.

Also, we return an empty object. This is definitely better than null, but it might be better to return an empty object more explicitly (a static EMPTY_BOOK field of an Empty final class - just an idea). It does not matter much in our case as client's processing will have to handle a 404 error anyway and will not even get to handle this empty object.

Why not just throw the exception and let the exception itself write a 404? I tried it and it does not work on JBoss (and it works perfectly on WebSphere), it turns out.


In the next and last part we will look at deleting.

Links


Download source code for this article

  1. REST with Spring 3.0, Spring MVC and Dojo. Part 1 - GET
  2. REST with Spring 3.0, Spring MVC and Dojo. Part 2 - GET from Dojo perspective
  3. REST with Spring 3.0, Spring MVC and Dojo. Part 3 - POST and JSR-303 validation
  4. REST with Spring 3.0, Spring MVC and Dojo. Part 4 - PUT (updating objects)
  5. REST with Spring 3.0, Spring MVC and Dojo. Part 5 - DELETE

2 comments: