1

I am working on creating a RESTful API using Jersey. I want to implement versioning via the Accept-Header. My resources, say MyResourceV1, MyResourceV2 and SubresourceV1, SubresourceV2, are separated into different classes. Now I am using the content-type application/vnd.myapp.resource.v1+json, which works great for regular resources since Jersey recognizes the custom media type.

@GET
@Path("/list")
@Produces("application/vnd.myapp.resource.v1+json")
public List<MyResourceV1> getProjectList() {
//returns JSON
}

Now MyResource implements the sub resource SubResource

@Path("/{resourceId}/subresource/")
    @Produces("application/vnd.myapp.subresource.v1+json")
    public SubResourceV1 getSubResource() {
        return new SubResourceV1();
    }

However, Jersey seems to ignore the @Produces annotation which results in Jersey complaining about multiple resources registered on the same path:

[...] and resource Resource{"/{resourceId}/subresource/", 0 child resources, 0 resource methods, 1 sub-resource locator, 1 method handler classes, 0 method handler instances}, contains sub resource locators on the same path /{resourceId}/subresource/.

Because MyResourceV2 includes the same path:

@Path("/{resourceId}/subresource/")
@Produces("application/vnd.myapp.subresource.v2+json")
public SubResourceV2 getSubResource() {
    return new SubResourceV2();
}

The only workaround I see is to include the different versions in the same file via different methods for the respective version. This bloats the code unnecessarily. Is there any way to have my versions seperated into files and still keep my sub resource locators?

Phil
  • 25
  • 1
  • 4
  • How about returning a 'Response' wrapped with entity depending on what is Accept header. Something like Response.ok..... – Optional Oct 23 '17 at 15:00

1 Answers1

0

I think there is no alternative to what you think you must do (one sub resource with all methods)

The jax rs specification says that sub resource locators are basically just responsible for providing the jaxrs container with a suitable implementation of said subresource.

There's nothing in there AFAICT regarding Accept matching and sub resource locators.

  • So basically JAX-RS is at least not emphasizing versioning per content-negotiation, which is considered to be the (more) restful way, on the implementation side? That is disappointing. Thanks for the clarification. – Phil Oct 24 '17 at 07:08
  • I don't know if we can say it like that. Sub resources strictly speaking are just deeper paths, and sub resource locators are doing this quite well :) Plus, if you'd like to create two resources with two different @Accepts (not sub resources) then you might have the same problem. IIRC, Jaxrs will use only one of two classes with the same `@Path`. Making the `@Accept` check on the method seems logical to me because when you version this way, more often that not, only some endpoints are actually served with 2 versions. –  Oct 24 '17 at 07:32
  • Fair point though I still feel that in the long run this will bloat the containing classes. – Phil Oct 24 '17 at 08:00
  • Sure, it might. In our API, we do this, and use another layer to actually do the job. That means that our resources are just dispatching to this other layer. Doing so would allow us to separate code for the two versions if we wanted to. However, we try to depreciate our old versions as soon as we can to ensure that we don't have a bloated API :) –  Oct 24 '17 at 08:03
  • You could create a separate class just to handle the sub-resource locator. The class could have the same `@Path` at the original class. The in the locator have the return type as `Object` and do a conditional return based on the `@HeaderParam("Accept")`. That's one way I can see. – Paul Samsotha Oct 24 '17 at 15:30