Handling multiple actions for a POST method

When we go a bit ahead from the CRUD scope of REST, we often need to support several actions for a same resource. This is typically handled by a method POST and we need to implement a processing to route the request to the right method in our resource class to handle it. This can be since the provided payload for such requests and responses can be different.

In addition, we want to leverage the conversion support provided by REST frameworks to directly work on beans for the payloads.

Generally in this context, we use a dedicated header to specify the action to execute. In the following we will use a custom header named x-action.

In this post, we will describe how to implement such use cases with REST frameworks like Restlet and JAX-RS compliant ones.

Use case

In general, methods POST of list resources is used to create a corresponding element. We can imagine to need to have several actions to handle for a method POST:

  • an action to add a set of elements. In this case, the input content corresponds to an array.
  • an action against the list itself like reorder, clear, and so on

The use of method POST for actions can also be used for other kinds of resources.

With REST, a wrong approach consists in using the action names within the resource path itself. For example with previous samples, we could have: /elements/reorder or /elements/clear. A better approach is to use a specific header to specify which action must be executed.

Moreover, when implementing such approach with Java REST frameworks, we can generally work with low-level elements but also with beans describing structured request and response contents. So we need to find out a way to select the right methods to invoke the action processing and them the right parameters.

With Restlet

Restlet provides no support for this out of the box. At the moment, only query parameters can be declaratively used within an annotation Post to select a request. The following code describes how to use this feature:

@Post("?action=single")
public void handleSingleAdd(TestBean contact) {
    (...)
}

With Restlet, we need to implement an annotated method that will route the request to the right handling method. This routing method must works on low-level API of Restlet to have access to the custom header and directly use the converter service to create the right instances of objects for requests.

The following code describes how to implement such processing. We introduce a method getInputObject to convert the input content into beans and handle errors if the right content isnt provided.

private <T> T getInputObject(Representation representation, Class<T> clazz) {
    try {
        return getConverterService().toObject(representation, clazz, this);
    } catch (Exception ex) {
        throw new ResourceException(
              Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY);
    }
}

@SuppressWarnings("unchecked")
@Post
public Representation handleAction(Representation representation)
                                                             throws IOException {
    Series<Header> headers = (Series<Header>)
       getRequestAttributes().get("org.restlet.http.headers");

    String actionHeader = headers.getFirstValue("x-action", "single");
    if ("single".equals(actionHeader)) {
        TestBean bean = getInputObject(representation, TestBean.class);
        TestBean returnedBean = handleSingleAction(bean);
        return getConverterService().toRepresentation(returnedBean);
    } else if ("list".equals(actionHeader)) {
        List<TestBean> beans = getInputObject(representation, List.class);
        List<TestBean> returnedBeans = handleMultipleAction(beans);
        return getConverterService().toRepresentation(returnedBeans);
    } else {
        throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST);
    }
}

With JAX-RS

With JAX-RS, there are two possible ways to implement such feature. The first one is based on a filter and the other one directly implemented within the resource. Lets started with the first one.

Approach #1

JAX-RS allows to define pre-matching filters that are called before the resource call and even before the framework choose which resource class and method will be used to handle the request. With this approach, we are able to update the requested URI to add something to tell the resource which method to use.

Following code describes the implementation of such filter:

@PreMatching
@Provider
public class PreMatchingFilter implements ContainerRequestFilter {
    @Context
    private ResourceInfo resourceInfo;

    @Context
    private UriInfo uriInfo;

    @Override
    public void filter(ContainerRequestContext requestContext)
                                                                       throws IOException {
        String xActionValue = requestContext.getHeaderString("x-action");
        if ("list".equals(xActionValue)) {
            requestContext.setRequestUri(
                   URI.create(uriInfo.getRequestUri() + "/list"));
        } else {
            requestContext.setRequestUri(
                   URI.create(uriInfo.getRequestUri() + "/single"));
        }
    }
}

The corresponding resource implementation will provide for its methods a sub path to select which will be called for each case:

@Path("/beans")
public class BeansResource {
    @POST
    @Path("/single")
    public void testContent(TestBean content) {
        (...)
    }

    @POST
    @Path("/list")
    public void testContent(List<TestBean> content) {
        (...)
    }

The main drawback of this approach is that some sub paths are defined and they can potentially be called directly from the client.

Approach #2

The second approach handles the routing of the request handling directly within the resource class. This feature is part of the JAX-RS specication and is called . The latter gives us some control over the chosen resource to handle the request.

We need to define an abstract class that will be returned for the path by the JAX-RS annotatéd method for the path and the method that support several actions. According to the value of the header xx, we actually return a sub class that provides processing for such case.

Following code describes an implementation of such approach within a resource class:

@Path("/beans")
public class BeansResource {
    public static abstract class AbstractHeaderResource {
    }

    @Path("/")
    public AbstractHeaderResource doSomething(
                  @HeaderParam("X-Header") String xHeader) {
        if ("list".equals(xHeader)) {
            return new ListResource();
        } else {
            return new SingleResource();
        }
    }

    public static class SingleResource extends AbstractHeaderResource {
        @POST
        public Response doSometing(TestBean bean) {
            (...)
            return Response.ok("single action").build();
        }
    }

    public static class ListResource extends AbstractHeaderResource {
        @POST
        public Response doSometing(List<TestBean> beans) {
            (...)
            return Response.ok("list action").build();
        }
    }
}

We can notice that the class AbstractHeaderResource can define an abstract method is the actions manage all the same content format:

public static abstract class AbstractHeaderResource {
    @POST
    public abstract Response doSometing(TestBean bean);
}

This entry was posted in JAX-RS, REST, Restlet and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s