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

后端 未结 4 1242
感动是毒
感动是毒 2020-12-08 15:35

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 U

4条回答
  •  粉色の甜心
    2020-12-08 16:23

    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 create(AbstractMethod abstractMethod) {
    
            //keep track of required query param names
            final ImmutableSet.Builder requiredQueryParamsBuilder =
                    ImmutableSet.builder();
    
            //get the list of params from the resource method
            final ImmutableList 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.singletonList(
                    new ValidationFilter(requiredQueryParamsBuilder.build())
            );
        }
    
        private static final class ValidationFilter implements ResourceFilter {
    
            final ImmutableSet requiredQueryParams;
    
            private ValidationFilter(ImmutableSet requiredQueryParams) {
                this.requiredQueryParams = requiredQueryParams;
            }
    
            @Override
            public ContainerRequestFilter getRequestFilter() {
                return new ContainerRequestFilter() {
                    @Override
                    public ContainerRequest filter(ContainerRequest request) {
    
                        final Collection 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:

    
        com.sun.jersey.spi.container.ResourceFilters
        my.package.name.ValidationFilterFactory
    
    

    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

提交回复
热议问题