简体   繁体   English

Jersey / Jax-RS:如何过滤资源和子资源

[英]Jersey/Jax-RS: how to filter resource and sub-resources

In Jersey 2, how can I bind a filter to all the method of a Resource as well as to all the methods of its sub-resources? 在Jersey 2中,如何将过滤器绑定到Resource的所有方法以及其子资源的所有方法?

For example, if I have the following 2 resources: 例如,如果我有以下2个资源:

import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.server.model.Resource;

@Path("/myresource/{id: \\d+}")
@Produces(MediaType.APPLICATION_JSON)
@Singleton
class RootResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response get(@PathParam("id") Long id) {
        return Response.ok().build();
    }

    @Path("/sub")
    public Resource getSubResource() {
        return Resource.from(SubResource.class);
    }
}

@Produces(MediaType.APPLICATION_JSON)
@Singleton
class SubResource {
    @GET
    @Path("/{subid: \\d+}")
    public Response get(@PathParam("id") Long id, @PathParam("subid") Long subid) {
        return Response.ok().build();
    }
}

I would like to filter RootResource.get(Long) and SubResource.get(Long, Long) . 我想过滤RootResource.get(Long)SubResource.get(Long, Long) But if I have other resources, those should not be filtered. 但如果我有其他资源,则不应过滤这些资源。

Using the DynamicFeature , we only have info on the Class and Method. 使用DynamicFeature ,我们只有类和方法的信息。

import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;

public class MyFeature implements DynamicFeature {

    @Override
    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
        // Here how can I find out that SubResource is actually a sub-resource of RootResource
    }

}

The idea is that I want to be able to filter out all calls for a certain set of id's (the set of id's is dynamic), with something more or less like this: 我的想法是,我希望能够过滤出一组id的所有调用(id的集合是动态的),或多或少像这样:

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;


public class MyFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        for(Object resource:requestContext.getUriInfo().getMatchedResources()) {
            if(resource instanceof RootResource) {
                Long id = Long.valueOf(requestContext.getUriInfo().getPathParameters().getFirst("id"));
                // ...
            }
        }
    }

}

but I would like to avoid having to search for the matched resources. 但我想避免搜索匹配的资源。 Is this possible? 这可能吗?

I'm not 100% sure I understand the problem, but it seems that you want to want to limit which resources should go through the filter. 我不是100%确定我理解这个问题,但似乎你想要限制哪些资源应该通过过滤器。 For that you can simply use Name Binding . 为此,您可以简单地使用名称绑定

Basic Steps: 基本步骤:

  1. Create a @NameBinding annotation 创建@NameBinding批注

     @NameBinding @Target({METHOD, TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Filtered { } 
  2. Annotate the filter 注释过滤器

     @Filtered @Provider public class MyFilter implements ContainerRequestFilter { 
  3. Annotate whatever root resources, resource methods, sub resource classes you want to be filtered 注释要过滤的任何根资源,资源方法,子资源类


UPDATE UPDATE

OK so after some playing around, I came up with a couple solutions.. netiher of which are pretty, but it gets the job done. 好吧,经过一些游戏后,我想出了几个解决方案..其中很好,但它完成了工作。

Keep in mind that configure in the DynamicFeature is called for each resource(method) we have. 请记住,我们为每个资源(方法)调用DynamicFeature中的configure

Algorithm 1: 算法1:

  1. Get the method being checked and get its declaring class (in the case of a method in a sub resource, the declaring class will be the sub resource class) 获取正在检查的方法并获取其声明类(在子资源中的方法的情况下,声明类将是子资源类)

     Class<?> possibleSubResource = resourceInfo.getResourceMethod().getDeclaringClass(); 
  2. Build a temporary Resource from your root resource 建立一个临时的Resource从根资源

     Resource resource = Resource.from(SomeResource.class); 
  3. Iterate its child resources, checking if it's a resource locator 迭代其子资源,检查它是否是资源定位器

     for (Resource childResource : resource.getChildResources()) { if (childResource.getResourceLocator() != null) { 
  4. If is is resource locator get the return type. 如果是资源定位器获取返回类型。

     ResourceMethod sub = childResource.getResourceLocator(); Class responseClass = sub.getInvocable().getRawResponseType(); 
  5. Then check if the response type from step 4 == the declaring class from step 1. 然后检查步骤4中的响应类型是否= =步骤1中的声明类。

     if (responseClass == possibleSubResource) { context.register(SomeFilter.class); } 

For the above to work, you actually need to return the sub resource type from the locator method, instead of a Resource . 对于上述工作,你实际上需要从定位方法返回,而不是一个子资源类型, Resource (You can try and make it work with Resource , but I haven't been able to figure it out) (您可以尝试使其与Resource ,但我无法弄清楚)

@Path("{id}")
public SomeSubResource getSubResource() {
    return new SomeSubResource();
}

Here is the full code that works (not battle tested :-) 这是有效的完整代码(不是经过测试的:-)

@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
    Class<?> resourceClass = resourceInfo.getResourceClass();

    if (resourceClass == SomeResource.class) {
        context.register(SomeFilter.class);
    }

    Class<?> possibleSubResource = resourceInfo.getResourceMethod().getDeclaringClass();

    Resource resource = Resource.from(SomeResource.class);
    for (Resource childResource : resource.getChildResources()) {
        if (childResource.getResourceLocator() != null) {
            ResourceMethod sub = childResource.getResourceLocator();
            Class responseClass = sub.getInvocable().getRawResponseType();

            if (responseClass == possibleSubResource) {
                context.register(SomeFilter.class);
            }
        }
    }
}

Algorithm 2: 算法2:

For this to work, we are going based off the assumption that what defines a Sub Resource is that it is annotation with @Path and has no Http method annotation 为了实现这一点,我们将基于以下假设:定义子资源的是它是使用@Path注释并且没有Http方法注释

  1. Get the method being checked and get its declaring class (in the case of a method in a sub resource, the declaring class will be the sub resource class) 获取正在检查的方法并获取其声明类(在子资源中的方法的情况下,声明类将是子资源类)

     Class<?> possibleSubResource = resourceInfo.getResourceMethod().getDeclaringClass(); 
  2. Iterate through the Method s in the root resource class 迭代根资源类中的Method

     for (Method method : SomeResource.class.getDeclaredMethods()) { 
  3. Check if the method has an Http method annotation 检查方法是否具有Http方法注释

     boolean isHttpPresent = false; for (Class annot : Arrays.asList(GET.class, POST.class, PUT.class, DELETE.class)) { if (method.isAnnotationPresent(annot)) { isHttpPresent = true; break; } } 
  4. Check if the method has the @Path annotation. 检查方法是否具有@Path注释。 If it does, and it has not Http method annotations, then we register the filter 如果是,并且它没有Http方法注释,那么我们注册过滤器

     if (method.isAnnotationPresent(Path.class) && !isHttpPresent) { Class subResourceClass = method.getReturnType(); if (subResourceClass == possibleSubResource) { context.register(SomeFilter.class); } } 

Here is the full code 这是完整的代码

@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
    Class<?> resourceClass = resourceInfo.getResourceClass();

    if (resourceClass == SomeResource.class) {
        context.register(SomeFilter.class);
    }

    Class<?> possibleSubResource = resourceInfo.getResourceMethod().getDeclaringClass();

    for (Method method : SomeResource.class.getDeclaredMethods()) {
        boolean isHttpPresent = false;
        for(Class annot : Arrays.asList(GET.class,POST.class,PUT.class, DELETE.class)){
            if (method.isAnnotationPresent(annot)) {
                isHttpPresent = true;
                break;
            }
        }
        if(method.isAnnotationPresent(Path.class) && !isHttpPresent){
            Class subResourceClass = method.getReturnType();
            if (subResourceClass == possibleSubResource) {
                context.register(SomeFilter.class);
            }
        }
    }
}

Again, neither of these solutions are battled tested, but work for the few cases I have tried. 同样,这些解决方案都没有经过严格的测试,但是我尝试过的少数几个案例都可以使用。 Personally, I'd just go with the name binding, but maybe this is an issue you can raise with the Jersey team. 就个人而言,我只是选择名字绑定,但也许这是泽西队可以提出的问题。 This (automatic registration of sub resource, when the root resources are registered) does seem like something that should work out the box, or at least be able to be configured. 这(子资源,当根资源注册的自动登记) 似乎喜欢的事,应该开箱,或至少能够被配置。

I had a similar need: I wanted an annotation to specifically filter resource methods in order to achieve something like this: 我有类似的需求:我想要一个注释专门过滤资源方法,以实现这样的事情:

@Path("/api/sample")
@Produces(MediaType.APPLICATION_JSON)
public class SampleResource {

    @Path("/filtered")
    @GET
    @Sample(value = "a sample value")
    public Hello filtered() {
        return new Hello("filtered hello");
    }

    @Path("/nonfiltered")
    @GET
    public Hello raw() {
        return new Hello("raw hello");
    }
}

My annotation being: 我的注释是:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sample {

    String value() default "";
}

I ended up using a DynamicFeature to register a Filter on Resource 我最终使用DynamicFeature注册资源Filter

@Provider
public class SampleFeature implements DynamicFeature {

    private SampleFilter sampleFilter;

    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
        if (resourceInfo.getResourceMethod().getAnnotation(Sample.class) != null) {
            if (sampleFilter == null) {
                this.sampleFilter = new SampleFilter();
            }
            context.register(sampleFilter);
        }
    }
}

the tricky thing was to find out how I could fetch the annotation value within my filter, hence to find out about ExtendedUriInfo , see below: 棘手的是找出如何在我的过滤器中获取注释值,从而找到有关ExtendedUriInfo ,见下文:

public class SampleFilter implements ContainerRequestFilter {

    public SampleFilter() {
    }

    public void filter(ContainerRequestContext containerRequestContext) throws IOException {
        String sampleValue = this.getAnnotation(containerRequestContext).value();
        // do some filtering based on the Sample Value
        }

    private Sample getAnnotation(ContainerRequestContext requestContext) {
        ResourceMethod method = ((ExtendedUriInfo) (requestContext.getUriInfo()))
                .getMatchedResourceMethod();
        Method invokedMethod = method.getInvocable().getHandlingMethod();
        return invokedMethod.getAnnotation(Sample.class);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM