Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get resource method matched to URI before Jersey invokes it?

Tags:

I'm trying to implement a ContainerRequestFilter that does custom validation of a request's parameters. I need to look up the resource method that will be matched to the URI so that I can scrape custom annotations from the method's parameters.

Based on this answer I should be able to inject ExtendedUriInfo and then use it to match the method:

public final class MyRequestFilter implements ContainerRequestFilter {      @Context private ExtendedUriInfo uriInfo;      @Override     public ContainerRequest filter(ContainerRequest containerRequest) {          System.out.println(uriInfo.getMatchedMethod());          return containerRequest;     } } 

But getMatchedMethod apparently returns null, all the way up until the method is actually invoked (at which point it's too late for me to do validation).

How can I retrieve the Method that will be matched to a given URI, before the resource method is invoked?


For those interested, I'm trying to roll my own required parameter validation, as described in JERSEY-351.

like image 327
Paul Bellora Avatar asked Apr 30 '13 15:04

Paul Bellora


2 Answers

Actually, you should try to inject ResourceInfo into your custom request filter. I have tried it with RESTEasy and it works there. The advantage is that you code against the JSR interfaces and not the Jersey implementation.

public class MyFilter implements ContainerRequestFilter {     @Context     private ResourceInfo resourceInfo;      @Override     public void filter(ContainerRequestContext requestContext)             throws IOException     {         Method theMethod = resourceInfo.getResourceMethod();         return;     } } 
like image 162
Christian Gürtler Avatar answered Sep 26 '22 03:09

Christian Gürtler


I figured out how to solve my problem using only Jersey. There's apparently no way to match a request's URI to the method that will be matched before that method is invoked, at least in Jersey 1.x. However, I was able to use a ResourceFilterFactory to create a ResourceFilter for each individual resource method - that way these filters can know about the destination method ahead of time.

Here's my solution, including the validation for required query params (uses Guava and JSR 305):

public final class ValidationFilterFactory implements ResourceFilterFactory {      @Override     public List<ResourceFilter> create(AbstractMethod abstractMethod) {          //keep track of required query param names         final ImmutableSet.Builder<String> requiredQueryParamsBuilder =                 ImmutableSet.builder();          //get the list of params from the resource method         final ImmutableList<Parameter> params =                 Invokable.from(abstractMethod.getMethod()).getParameters();          for (Parameter param : params) {             //if the param isn't marked as @Nullable,             if (!param.isAnnotationPresent(Nullable.class)) {                 //try getting the @QueryParam value                 @Nullable final QueryParam queryParam =                         param.getAnnotation(QueryParam.class);                 //if it's present, add its value to the set                 if (queryParam != null) {                     requiredQueryParamsBuilder.add(queryParam.value());                 }             }         }          //return the new validation filter for this resource method         return Collections.<ResourceFilter>singletonList(                 new ValidationFilter(requiredQueryParamsBuilder.build())         );     }      private static final class ValidationFilter implements ResourceFilter {          final ImmutableSet<String> requiredQueryParams;          private ValidationFilter(ImmutableSet<String> requiredQueryParams) {             this.requiredQueryParams = requiredQueryParams;         }          @Override         public ContainerRequestFilter getRequestFilter() {             return new ContainerRequestFilter() {                 @Override                 public ContainerRequest filter(ContainerRequest request) {                      final Collection<String> missingRequiredParams =                             Sets.difference(                                     requiredQueryParams,                                     request.getQueryParameters().keySet()                             );                      if (!missingRequiredParams.isEmpty()) {                          final String message =                                 "Required query params missing: " +                                 Joiner.on(", ").join(missingRequiredParams);                          final Response response = Response                                 .status(Status.BAD_REQUEST)                                 .entity(message)                                 .build();                          throw new WebApplicationException(response);                     }                      return request;                 }             };         }          @Override         public ContainerResponseFilter getResponseFilter() {             return null;         }     } } 

And the ResourceFilterFactory is registered with Jersey as an init param of the servlet in web.xml:

<init-param>     <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>     <param-value>my.package.name.ValidationFilterFactory</param-value> </init-param> 

At startup, ValidationFilterFactory.create gets called for each resource method detected by Jersey.

Credit goes to this post for getting me on the right track: How can I get resource annotations in a Jersey ContainerResponseFilter

like image 39
Paul Bellora Avatar answered Sep 25 '22 03:09

Paul Bellora