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

时间:2015-04-02 07:58:26

标签: java rest jersey jax-rs jersey-2.0

在Jersey 2中,如何将过滤器绑定到资源的所有方法以及其子资源的所有方法?

例如,如果我有以下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();
    }
}

我想过滤RootResource.get(Long)SubResource.get(Long, Long)。但如果我有其他资源,则不应过滤这些资源。

使用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
    }

}

我的想法是,我希望能够过滤掉一组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"));
                // ...
            }
        }
    }

}

但我希望避免搜索匹配的资源。这可能吗?

2 个答案:

答案 0 :(得分:6)

我不是100%确定我理解这个问题,但似乎您想要限制哪些资源应该通过过滤器。为此,您只需使用Name Binding

基本步骤:

  1. 创建@NameBinding注释

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

    @Filtered
    @Provider
    public class MyFilter implements ContainerRequestFilter {
    
  3. 注释您想要过滤的任何根资源,资源方法,子资源类


  4. 更新

    好的,所以经过一些游戏之后,我想出了几个解决方案......其中很好,但它完成了工作。

    请注意,我们为每种资源(方法)调用了configure中的DynamicFeature

    算法1:

    1. 获取正在检查的方法并获取其声明类(在子资源中的方法的情况下,声明类将是子资源类)

      Class<?> possibleSubResource =
               resourceInfo.getResourceMethod().getDeclaringClass();
      
    2. 从您的根资源

      构建临时Resource
      Resource resource = Resource.from(SomeResource.class);
      
    3. 迭代其子资源,检查它是否是资源定位器

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

      ResourceMethod sub = childResource.getResourceLocator();
      Class responseClass = sub.getInvocable().getRawResponseType();
      
    5. 然后检查步骤4中的响应类型是否= =步骤1中的声明类。

      if (responseClass == possibleSubResource) {
          context.register(SomeFilter.class);
      }
      
    6. 要使上述工作正常,您实际上需要从定位器方法返回子资源类型,而不是Resource。 (您可以尝试使其与Resource一起使用,但我无法弄明白)

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

      以下是有效的完整代码(未经过战斗测试: - )

      @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);
                  }
              }
          }
      }
      

      算法2:

      为了实现这一点,我们将基于以下假设:定义子资源的是它是@Path的注释并且没有Http方法注释

      1. 获取正在检查的方法并获取其声明类(在子资源中的方法的情况下,声明类将是子资源类)

        Class<?> possibleSubResource =
                 resourceInfo.getResourceMethod().getDeclaringClass();
        
      2. 迭代根资源类中的Method

        for (Method method : SomeResource.class.getDeclaredMethods()) {
        
      3. 检查方法是否具有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. 检查方法是否具有@Path注释。如果是,并且它没有Http方法注释,那么我们注册过滤器

        if (method.isAnnotationPresent(Path.class) && !isHttpPresent) {
            Class subResourceClass = method.getReturnType();
            if (subResourceClass == possibleSubResource) {
                context.register(SomeFilter.class);
            }
        }
        
      5. 这是完整的代码

        @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);
                    }
                }
            }
        }
        

        同样,这些解决方案都没有经过严格的测试,但是我尝试过的少数几个案例都有效。就个人而言,我只是选择名字绑定,但也许这是泽西队可以提出的问题。这(子资源的自动注册,当注册根资源时) 看起来像应该可以解决方案,或者至少可以配置。

答案 1 :(得分:1)

我有类似的需求: 我想要一个注释来专门过滤资源方法以实现 像这样的东西:

@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");
    }
}

我的注释是:

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

    String value() default "";
}

我最终使用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);
        }
    }
}

棘手的是找出如何在我的过滤器中获取注释值,从而找出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);
    }
}