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.
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:
SEVERE: Producing media type conflict. The resource methods ...
For the resource:
@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();
}
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:
Resource 1 Resource 2 ---------------------------------------- text/html;q=0.4 text/html;q=0.8 text/html text/html;q=0.2 text/html text/html;qs=1.4 text/html;qs=1.4 text/html;qs=1.8 text/html;level=1 text/html;level=2 text/html;foo=bleh text/html;bar=23
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:
Accept: text/html;q=0.8, text/html;q=0.4, text/html
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:
Accept: text/html;version=4.0;q=0.8, text/html;version=3.2;q=0.4
So what are the possible solutions?
With RESTEasy, and resource:
@Path("/content/version")
public class ContentVersionResource {
@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();
}
}
A successful match is made with the following Accept header:
Accept: application/vnd.test;version=1;q=0.3, application/vnd.test;version=2;q=0.5
Response: Version 2
And this:
Accept: application/vnd.test;version=1;q=0.5, application/vnd.test;version=2;q=0.3
Response: Version 1
You can download and test with this sample project. Git, Maven and JBoss 7.x required
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With