Search This Blog

Tuesday 28 May 2013

Rest Example with Spring - 2

Continuing from the previous post, I decided to take up the next method
@RequestMapping(value = "{id}", method = RequestMethod.GET)
public @ResponseBodyUser getUser(@PathVariable final int id) {
   logger.info("getUser with id " + id);
   return this.allUsers.get(id);
}
The method will return an User instance when the id is passed to it. So the URL for the same would be
http://localhost:8080/SampleRest/api/user/1
The documentation for the PathVariable annotation
Annotation which indicates that a method parameter should be bound to a URI template variable.
When the controller receives a request with the above URL and the GET method, it sets the value of id variable with the value following /user/<id>
On executing the call, the server logs indicate how the method was detected:
2013-05-05 20:18:33 DEBUG DispatcherServlet:819 - DispatcherServlet with name 'd
ispatcher' processing GET request for [/SampleRest/api/user/1]
...
2013-05-05 20:18:33 DEBUG RequestMappingHandlerMapping:209 - Looking up handler 
method for path /user/1
...
2013-05-05 20:18:33 DEBUG RequestMappingHandlerMapping:253 - Found 1 matching ma
pping(s) for [/user/1] : [{[/user/{id}],methods=[GET],params=[],headers=[],consu
mes=[],produces=[],custom=[]}]
...
2013-05-05 20:18:33 DEBUG ServletInvocableHandlerMethod:123 - Invoking [getUser]
 method with arguments [1]
The next is the create method:
@RequestMapping(method = RequestMethod.POST)
   public @ResponseBody
   User addUser(@RequestBody final User user) {
      final int newId = this.keyIndex++;
      user.setId(newId);
      this.allUsers.put(newId, user);
      logger.info("addUser : new User added with id " + newId);
      return user;
   }
The method is a POST method to the same resource. I tried to execute the same with the RestClient plugin and got the below exception:
2013-05-05 20:24:30 DEBUG RequestMappingHandlerMapping:216 - Returning handler m
ethod [public com.test.controller.User com.test.controller.RestServiceController
.addUser(com.test.controller.User)]
...
2013-05-05 20:24:30 DEBUG ServletInvocableHandlerMethod:160 - Error resolving ar
gument [0] [type=com.test.controller.User]
HandlerMethod details: 
Controller [com.test.controller.RestServiceController]
Method [public com.test.controller.User com.test.controller.RestServiceControlle
r.addUser(com.test.controller.User)]
org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'text/p
lain;charset=UTF-8' not supported
A little web hunting indicated that the root cause was Spring not having any clear idea on how to deal with my request body. So I added appropriate headers and made the call again:
 As seen the response returned the record with id set to a new value. The logs indicate the method being called after the request body has been processed.
2013-05-05 20:30:04 DEBUG RequestResponseBodyMethodProcessor:117 - Reading [com.
test.controller.User] as "application/json;charset=UTF-8" using [org.springframe
work.http.converter.json.MappingJacksonHttpMessageConverter@3449a8]
2013-05-05 20:30:04 DEBUG ServletInvocableHandlerMethod:123 - Invoking [addUser]
 method with arguments [com.test.controller.User@1ff66bd]
2013-05-05 20:30:04 INFO  RestServiceController:72 - addUser : new User added wi
th id 4
The next method is for Update.
@RequestMapping(value = "{id}", method = RequestMethod.PUT)
   public @ResponseBody
   User updateUser(@PathVariable final int id, @RequestBody final User user) {
      if ((user.getId() == User.INVALID_ID) || (id == User.INVALID_ID)) {
         throw new RuntimeException("Invalid Id receieved - ");
      }
      this.allUsers.put(id, user);
      logger.info("updateUser : user with id " + user.getId()
            + " has been updated");
      return user;
   }
Here a PUT method was used for the same resource.Similarly we can use the DELETE method to delete a resource from the server.
@RequestMapping(value = "{id}", method = RequestMethod.DELETE)
   @ResponseStatus(value = HttpStatus.OK)
   public void deleteUser(@PathVariable final int id) {
      this.allUsers.remove(id);
      logger.info("deleteUser : user with id " + id + " has been deleted");
      return;
   }
Both these methods use the id extracted from the URL itself. I tested both of the above using RestClient Plugin and they worked fine. The Delete method is a bit different in that it has a void return type. If the method succeeds, than the Servlet will  return a 200 value or OK.
This completes basic CRUD operations.
The last method is not exactly a resource method in that it uses a slightly different URL.
@RequestMapping(value = "search/{name}", method = RequestMethod.GET)
   public @ResponseBody
   User findUserByName(@PathVariable final String name) {
      logger.info("findUserByName : for name " + name);
      User target = null;
      for (final User user : this.allUsers.values()) {
         if (name.equals(user.getName())) {
            target = user;
            break;
         }
      }
      return target;
   }
The method is actually a search method. The URL for the same would be
http://localhost:8080/SampleRest/api/user/search/William
The value after "/search" goes into the method variable marked by PathVariable annotation. This method returns a user with the specified name.
This completes my REST API for user resource. With Spring implementing the service was easier than expected.

2 comments: