jax-rshttp-method

How do I implement a default functionality for HTTP Method in JAX-RS?


I am using JAX-RS and it has default implementations for @OPTIONS and @HEAD methods, but I would like to provide a different functionality.

What I have done currently is this:

@GET
@Path("path/to/resource")
@Produces(MediaType.APPLICATION_JSON)
public Response resourceCall(){
    // Normal method implementation
}

@OPTIONS
@Path("path/to/resource")
@Produces(MediaType.APPLICATION_JSON)
public Response resourceCall(){
    Response.status(Response.Status.METHOD_NOT_ALLOWED).build();
}

So, basically I create a new call to each one of my resources. But, I would like to have a catch-all method here that would treat all my calls to @OPTION or to @HEAD.

How do I implement such functionality?

EDIT:

Just for clarity. I know how to do this using a Servlet Filter but, I am wondering if JAX-RS has a similar feature built-in for this specific case.


Solution

  • Since you are using Jersey, Jersey has a feature that allows you to programmatically add and modify resources. So you could add OPTIONS and HEAD methods to all your resources, without having to touch your resource classes. Below is an example that does nothing but send a 405 with a No <Method> message. Probably not what you want, but you should be able to figure out what you need to modify to get what you want.

    import javax.ws.rs.container.ContainerRequestContext;
    import javax.ws.rs.core.Configuration;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import javax.ws.rs.ext.Provider;
    import org.glassfish.jersey.process.Inflector;
    import org.glassfish.jersey.server.model.ModelProcessor;
    import org.glassfish.jersey.server.model.Resource;
    import org.glassfish.jersey.server.model.ResourceModel;
    
    @Provider
    public class HeadAndOptionsModelProcessor implements ModelProcessor {
    
        @Override
        public ResourceModel processResourceModel(ResourceModel resourceModel,
                Configuration configuration) {
            ResourceModel.Builder resourceModelBuilder = new ResourceModel.Builder(false);
            for (Resource rootResource: resourceModel.getResources()) {
                final Resource.Builder rootResourceBuilder = Resource.builder(rootResource);
                addOptions(rootResourceBuilder);
                addHead(rootResourceBuilder);
    
                for (Resource childResource: rootResource.getChildResources()) {
                    final Resource.Builder childResourceBuilder = Resource.builder(childResource);
                    addOptions(childResourceBuilder);
                    addHead(childResourceBuilder);
                    rootResourceBuilder.addChildResource(childResourceBuilder.build());
                }
                resourceModelBuilder.addResource(rootResourceBuilder.build());
            }
            return resourceModelBuilder.build();
        }
    
        @Override
        public ResourceModel processSubResource(ResourceModel subResourceModel,
                Configuration configuration) {
            return subResourceModel;
        }
    
        private void addOptions(Resource.Builder resourceBuilder) {
            resourceBuilder.addMethod("OPTIONS")
                    .handledBy(new Inflector<ContainerRequestContext, Response>() {
                        @Override
                        public Response apply(ContainerRequestContext context) {
                            return getOptionsResponse(context);
                        }
                    }).produces(MediaType.TEXT_PLAIN).extended(true);
        }
    
        private void addHead(Resource.Builder resourceBuilder) {
            resourceBuilder.addMethod("HEAD")
                    .handledBy(new Inflector<ContainerRequestContext, Response>() {
                        @Override
                        public Response apply(ContainerRequestContext context) {
                            return getHeadResponse(context);
                        }
                    }).produces(MediaType.TEXT_PLAIN).extended(true);
        }
    
        private static Response getOptionsResponse(ContainerRequestContext context) {
            return Response.status(Response.Status.METHOD_NOT_ALLOWED).entity("No Options").build();
        }
    
        private static Response getHeadResponse(ContainerRequestContext context) {
            return Response.status(Response.Status.METHOD_NOT_ALLOWED).entity("No Head").build();
        }
    }