The following article outlines some common use cases one may encounter while developing RESTful web services using Dropwizard. It is entirely based upon the previous post on this matter, so it's advised that you go through it first and check out included source code package.

The source code for this article is here on GitHub, along with Guice/JPA stuff added for the next part.

Handling method parameters

Let's see how we can get parameters for resource method. In the following example I have added a method to HelloResource that takes plenty of parameters from different sources.

@POST
@Path("/{id}")
public String parameterDemoMethod(String body, 
                                   @PathParam("id") long id, 
                                   @QueryParam("foo") String foo, 
                                   @HeaderParam("X-Auth-Token") String token, 
                                   @Context HttpServletRequest request) {
    String response;
    response  = "id = " + id + "\n";
    response += "body = " + body + "\n";
    response += "foo = " + foo + "\n";
    response += "token = " + token + "\n";
    response += "ip = " + request.getRemoteAddr() + "\n";
    return response;
}

What appears here is @Path annotation to the method, that defines additional URL part needed to call this method. It's relative to class-level @Path value, which in case of HelloResource was /hello. So, in order to call parameterDemoMethod(), the URL should look like /hello/123. The {id} is a path parameter that maps to method parameter id by @PathParam annotation.

There can be optional parameters that may appear in URL, i.e. /hello/123?foo=value. The value is mapped to foo parameter by @QueryParam annotation.

Another source for parameters can be request headers. Here, by using @HeaderParam annotation, we're mapping value of header X-Auth-Token to token parameter.

@Context annotation is used to inject some objects that give access to JAX-RS context. Among objects that can be injected are: HttpHeaders, UriInfo, SecurityContext, Request, Providers. Here we injected Request, that provides access to getRemoteAddr() method that returns IP address of client calling our method.

The request body is mapped to body parameter. Note that no annotations are present. Jersey will try hard to map request body based on Content-type headers to the annotation-less parameter. In case of String it should be quite easy, but the parameter doesn't need to be of type String at all as it can be of any type, although only handful of types will be supported out of the box.

Working with JSON

You can both accept and return JSON objects that will be mapped to and from POJO. So let's write simple representation object:

public class User {

    private long id;
    private String name;
    private String login;
    private String password;

    // getters and setters omitted for readability
}

Magic happens in the UserResource class for our User object, which has methods that either take it as parameter or return it as needed.

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

@Path("/user")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {

    private UserDao userDao;

    public UserResource(UserDao userDao) {
        this.userDao = userDao;
    }

    @GET
    @Path("/{id}")
    public User getUser(@PathParam("id") long id) {
        User user = userDao.getById(id);
        return user;
    }

    @POST
    public User addUser(User user) {
        User createdUser = userDao.save(user);
        return createdUser;
    }

}

Worth noting are @Produces and @Consumes JAX-RS annotations on the class level that tell Jersey to expect and return request body in JSON format with appropriate Content-Type headers. Those annotations can also appear on method-level, overwriting any class-level annotations for method they apply to.

UserDao is a class that handles communication with a data source and provides methods such as add(), getById(), update(), delete(), etc. The reason for this is that it's important not to add too many responsibilities to classes. Resource class should only handle taking parameters and returning response for client requests, delegating actual work somewhere else. I don't give you an implementation for it, as it depends on what you're going to use to access the data.

As Dropwizard/Jersey uses Jackson for doing JSON mapping there are also plenty of annotations one can use in User object to customize mapping. Refer to Jackson annotation guide for more information.

Returning custom response status code

Let's enchance our methods in UserResource so that adding new User object returns status code 201 CREATED, and trying to get non-existent User returns 404 NOT FOUND.

@GET
@PathParam("/{id}")
public Response getUser(@PathParam("id") long id) {
    try {
        User user = userDao.getById(id);
        return Response.ok(user).build();
    } catch (UserDoesNotExistException e) {
        return Response.status(Response.Status.NOT_FOUND).build();
    }
}

@POST
public Response addUser(User user) {
    User createdUser = userDao.add(user);
    return Response.status(Response.Status.CREATED).entity(createdUser).build();
}

The return type changed to Response, which permits us to have more influence over what is actually being returned. Response class has bunch of static methods that provide ResponseBuilder object with another bunch of methods to customize response. There is status() method that permits to build a response with any custom status code, entity() method that puts any object into response body, and the chain ends by build() method that returns actual Response object.

Note that is not a good idea to manipulate data access objects directly from Resource classes unless the logic involved is very, very simple like here.

Validation of received object

We can add Java Bean Validation 1.0 annotations to our User object, to force some properties to be required while calling our addUser() method.

import javax.validation.constraints.*;

public class User {

    private long id;
    @NotNull
    @Size(min = 2, max = 64)
    private String name;
    @NotNull
    @Size(min = 2, max = 16)
    private String login;
    @NotNull
    @Size(min = 8, max = 16)
    private String password;

    // getters and setters omitted for readability
}

So, whenever someone posts incomplete User object to addUser() method he will receive error response and there will be no need to validate the object inside method. You can read through this article for a list of available annotations in Java Bean Validation 1.0 API.

Summary

Presented were most common use cases that I have occurred while developing RESTful web services using Dropwizard. Coming up will be a tutorial concerning integrating Guice for dependency injection with guice-persist module for database access via Java Persistence API.

This site uses cookies. By continuing to browse the site, you are agreeing to our use of cookies. Find out more in Privacy and Cookies Policy.