In order to deal with different versions of a content-type i am trying to use the accept-parameters of the "Accept*" headers (RFC 2616).
Accept: application/vnd.mycompany.mytype;version=2 , application/vnd.mycompany.mytype;version=1;q=0.1
The problem is that Jax-RS annotations do not support Accept-parameters...
@GET
@Produces("application/vnd.test;version=1")
public Response test1() {
return Response.ok("Version 1", "application/vnd.test").build();
}
@GET
@Produces("application/vnd.test;version=2")
public Response test2() {
return Response.ok("Version 2", "application/vnd.test").build();
}
Results in a media type conflict exception:
Producing media type conflict. The resource methods public javax.ws.rs.core.Response test.resources.TestResource.test2() and public javax.ws.rs.core.Response test.resources.TestResource.test1() can produce the same media type
Maybe, this exception only related to my JAX-RS framework (Jersey), but i'm afraid this is due to the JSR311 which is not explicit about accept-parameters.
By now, i'm using content-types which holds the version within their names, but i found this solution pretty uggly.
@GET
@Produces("application/vnd.test-v1")
public Response test() {
return Response.ok("Version 1", "application/vnd.test-v1").build();
}
Do you have any ideas about how to deal with accept-parameters ?
EDIT
I think i wasn't clear enough. I want to automatically route the request to specific methods. These methods are versioned and correspond to a specific version of the returned content-type. JAX-RS current implementation prevents me to use accept-parameters to route the request (to the corresponding method).
greenkode suggest that i manage the version
accept-parameter within a dispatching method (using @HeaderParam("Accept")
).
This solution would end-up in re-writing the content negociation logic which is embeded in the framework (and described in JSR 311).
What can i do to use both accept-parameter and content-negociation logic from JAX-RS ?
Maybe a solution is to use another framework (I only worked with Jersey by Now). But i don't know which one.
Unless I'm missing something. JAX-RS does support Accept parameters. look at the
@Consumes("*/*")
annotation. Also, The exception you're getting with media type conflict occurs because you have twoGET
methods at the same url. annotate the test2() method with@Path("test2")
, then send yourGET
request to url/test2 instead. that should get rid of that error.EDIT
You can inject the Value of the
Accept
header using@HeaderParams
. Here's a sample of what I did.passing the request
this will print
You can then handle it manually. Is this what you're looking for?
The JAX-RS specification does not explicitly state anything about ignoring Accept header parameters. But the only parameter for which handling is definitely defined is quality (q). This is a possible area for improvement as it seems to have lead to ambiguity (or outright bugginess) in the Jersey implementation. The current version of Jersey (1.17) does not take Accept header parameters into consideration when matching incoming requests to resource methods, which is why you are getting the error:
For the resource:
It would appear that Jersey performs a 'uniqueness' check based on the Accept header 'type/subtype', totally omitting any parameters. This can be confirmed by testing with various pairs of headers on the 'matching' resource methods:
All fail with the same error. If the assumption were made that only the quality parameter is ever sent, then it would make sense to only match on 'type/subtype', because this kind of request is nonsensical:
Aka, quality parameters only make sense when you are dealing with a mix of possible content types. However, this sort of limited matching fails when non-quality parameters or additional parameters are being sent:
So what are the possible solutions?
With RESTEasy, and resource:
A successful match is made with the following Accept header:
And this:
You can download and test with this sample project. Git, Maven and JBoss 7.x required
With Jersey framework, the Accept header of the HTTP request declared what is most acceptable. If a resource class is capable of producing more that one MIME media type then the resource method chosen will correspond to the most acceptable media type as declared by the client. In your case, if the accept header is
then the method test1() will be invoked.
If it is
the latter one will be called.
More than one media type may be declared in the same @Produces declaration, for example:
the test(9 method will be called if either of the 2 mediatypes is acceptable. if both are acceptable, the first will be invoked.
Hope it helps!