I'm afraid what you trying to accomplish is actually not possible in a nice way using the current version without changing Jersey itself.
Anyway I'm also not really sure if using @Provider
for request specific filters is the right way according to specification specification. But who am I to speak I actually do it myself. Of course one could also register the filter in a ResourceConfig
.
In general I'd suggest to take a look at @NameBinding
, but for this case name-binding Jersey-style is not enough. With @NameBinding
you do not have to check for the annotation yourself, because Jersey already does that for you.
Unfortunately again with using @NameBinding
, which was introduced just for such cases there is the problem of the "auto-generated" OPTIONS-handler. I did quite some digging (some of the most relevant classes/methods are OptionsMethodProcessor
, WadlModelProcessor
, ResourceModelConfigurator#init
and ServerRuntime ApplicationHandler#initialize
) but did not find a way to hook into the process adequately. Here's what should suffice for handling CORS:
@NameBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CrossOrigin {
}
@CrossOrigin
public class CrossOriginResponseFilter implements ContainerResponseFilter {
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
// do Cross Origin stuff
}
}
@Path("ress")
public class MyResource {
@CrossOrigin
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response save(DetailsDTO details) {
// do something with the details
}
}
But while this works well for any direct request to the resource this does also not work for CORS-preflight-requests, because Jersey does not apply the name-binding-annotation @CrossOrigin
to the predefined / auto-generated OPTIONS-handler.
You can see that when looking at the runtime-representation of the Resource in the request context (don't let all the text irritate you, the important thing are the nameBindings
-properties at the end of each ResourceMethod
):
[ResourceMethod{
httpMethod=POST, consumedTypes=[application/json],
producedTypes=[application/json], suspended=false, suspendTimeout=0,
suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class de.example.MyResource,
handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@2c253414]}, definitionMethod=public javax.ws.rs.core.Response de.example.MyResource.save(de.example.DetailsDTO),
parameters=[Parameter [type=class de.example.DetailsDTO, source=null, defaultValue=null]],
responseType=class javax.ws.rs.core.Response},
nameBindings=[interface de.example.CrossOrigin]},
ResourceMethod{
httpMethod=OPTIONS, consumedTypes=[*/*],
producedTypes=[application/vnd.sun.wadl+xml], suspended=false,
suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS,
invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class org.glassfish.jersey.server.wadl.processor.WadlModelProcessor$OptionsHandler,
handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@949030f]},
definitionMethod=public abstract java.lang.Object org.glassfish.jersey.process.Inflector.apply(java.lang.Object),
parameters=[Parameter [type=interface javax.ws.rs.container.ContainerRequestContext, source=null, defaultValue=null]], responseType=class javax.ws.rs.core.Response},
nameBindings=[]},
ResourceMethod{
httpMethod=OPTIONS, consumedTypes=[*/*], producedTypes=[text/plain],
suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS,
invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor$PlainTextOptionsInflector,
handlerConstructors=[]}, definitionMethod=public abstract java.lang.Object org.glassfish.jersey.process.Inflector.apply(java.lang.Object),
parameters=[Parameter [type=interface javax.ws.rs.container.ContainerRequestContext, source=null, defaultValue=null]],
responseType=class javax.ws.rs.core.Response}, nameBindings=[]},
ResourceMethod{
httpMethod=OPTIONS, consumedTypes=[*/*], producedTypes=[*/*],
suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS,
invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor$GenericOptionsInflector,
handlerConstructors=[]}, definitionMethod=public abstract java.lang.Object org.glassfish.jersey.process.Inflector.apply(java.lang.Object),
parameters=[Parameter [type=interface javax.ws.rs.container.ContainerRequestContext, source=null, defaultValue=null]], responseType=class javax.ws.rs.core.Response},
nameBindings=[]}]
But now you can use the name-binding information to handle the preflight requests yourself by creating another filter:
@Provider
@Priority(1)
public class CrossOriginResponseFilter implements ContainerRequestFilter {
Resource res = ((ContainerRequest)requestContext)
.getUriInfo().getMatchedResourceMethod().getParent();
if (res.getResourceMethods().get(0).getNameBindings().contains(CrossOrigin.class)) {
// handlePreflightRequest and abort: requestContext.abortWith(builder.build());
}
}
Funny thing is that the extracted Resource res
will only contain the relevant resource method that matches the actual request URI and method and the auto-generated OPTIONS-handlers as you can see above in the run-time representation of the resource methods. The example resource actually has further methods, POSTs and GETs. So you can access the needed information by using .get(0)
here.
BUT BEWARE! I did not check if that is true in any case or just when you for example annotate your resource methods with separate paths. So maybe there is more matching work to do than in my simple version here.
I myself find that solution to be quite ugly and ended up with a filter that is not method-specific but simply handles all requests to any resource (similar to the solution of the guys here). But it should be an answer to the question how you can "find out the actual (GET/POST) resource method when handling an OPTIONS request".