4

I wonder if it's possible with JAX-RS to route a request using an header in addition to the HTTP method. In fact, I can't find out the way to do that.

I thought about something like that where x-header is an header:

@Path("/contacts/")
public class MyResource {
    @POST
    @Header("x-header:content1")
    public void method1(MyContent1 content) {
        (...)
    }

    @POST
    @Header("x-header:content2")
    public void method2(MyContent2 content) {
        (...)
    }
}

This question follows this answer: How to Update a REST Resource Collection.

Thanks very much for your help! Thierry

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360

2 Answers2

4

If you need to affect the request matching/routing process, you have to use JAX-RS filters - PreMatching filters to be specific (@PreMatching) [this will work in JAX-RS 2.0 onwards] If the use header info in the resource methods, it wont make sense because JAX-RS would have already matched the method

Here is the overall flow in filter implementation

  1. Use the ContainerRequestContext to fetch header info
  2. Apply your business criteria depending on the header value
  3. Now the trick is to be able to route to the desired resource method - one option you have is to use the setRequestUri method of ContainerRequestContext and have different resource methods set on different URIs (using @Path)

Jersey docs might help -- https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9538

Abhishek
  • 1,175
  • 1
  • 11
  • 21
  • (+1), but I'm wondering if it makes sense to have different paths to implement this. You need to think about implications. This means that the different methods can be accessed with out the header, simply by using the corresponding path. If you don't mind this, then I guess it would be fine. One option (if you don't want this behavior), is to check the header inside the method. If it doesn't exist, then throw an exception. – Paul Samsotha Mar 11 '15 at 02:42
2

Just throw in another option. You can also use Sub-resource locators, which give us some control over the chosen resource (and is part of the JAX-RS spec). For example

@Path("contacts")
public class ContactsResource {
    
    @Path("/")
    public AbstractHeaderResource doSomething(@HeaderParam("X-Header") String xHeader) {
        if ("RequiredValue".equals(xHeader)) {
            return new WithHeaderResource();
        }
        return new WithOutHeaderResource();
    }
    
    public static abstract class AbstractHeaderResource {
        @POST
        @Consumes(MediaType.APPLICATION_JSON)
        public abstract Response doSometing(Contact contact);
    }
    
    public static class WithHeaderResource extends AbstractHeaderResource {
        @Override
        public Response doSometing(Contact contact) {
            return Response.ok("*** With Header ***").build();
        }
    }
    
    public static class WithOutHeaderResource extends AbstractHeaderResource {
        @Override
        public Response doSometing(Contact contact) {
            return Response.ok("*** WithOut Header ***").build();
        }
    }
}

Test

C:\>curl -v -X POST  
         -d "{\"name\":\"Peeskillet\"}" 
         -H "X-Header:RequiredValue" 
         -H "Content-Type:application/json"
         http://localhost:8080/contacts 

*** With Header ****

C:\>curl -v -X POST  
         -d "{\"name\":\"Peeskillet\"}"  
         -H "Content-Type:application/json"
         http://localhost:8080/contacts 

*** WithOut Header ****

The resource methods don't have to accept the same parameter type. I did it just for brevity. You can have the sub resource classes extend an empty abstract class or interface (just for typing), and create the methods however you want

UPDATE

If you need some objects injected into the sub resource, you can also return the sub resource class from the locator method or you can inject ResourceContext into the main resource class and use that to create the sub resource instance and return that instance from the locator method. Both ways will support container injection as the container will create the instance instead of you just instantiating it.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • 1
    Thanks for your response! It's really an interesting approach. I just tested it and it works perfectly ;-) – Thierry Templier Mar 11 '15 at 10:49
  • @peeskillet is there a way to route to another resources based on the deserialised object (Jackson can perform polymorphic deserialization), without writing the code the dispatching ourselve it the _receiving_ java method. (see https://stackoverflow.com/questions/46369176/is-it-possible-to-possible-to-achieve-with-jersey-a-polymorphic-deserialisation/46369336#46369336) – bric3 Sep 22 '17 at 16:36