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.
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; } }
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
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