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

Java Jersey/Jax-RS:如何过滤资源和子资源,java,rest,jersey,jax-rs,jersey-2.0,Java,Rest,Jersey,Jax Rs,Jersey 2.0,在Jersey 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

在Jersey 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"));
                // ...
            }
        }
    }

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

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

我不能100%肯定我理解这个问题,但您似乎想限制哪些资源应该通过过滤器。为此,您可以简单地使用

基本步骤:

  • 创建
    @NameBinding
    注释

    @NameBinding 
    @Target({METHOD, TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Filtered {    
    }
    
    boolean isHttpPresent = false;
    for (Class annot : Arrays.asList(GET.class, POST.class, PUT.class, DELETE.class)) {
        if (method.isAnnotationPresent(annot)) {
            isHttpPresent = true;
            break;
        }
    }
    
  • 为过滤器添加注释

    @Filtered
    @Provider
    public class MyFilter implements ContainerRequestFilter {
    
    if (method.isAnnotationPresent(Path.class) && !isHttpPresent) {
        Class subResourceClass = method.getReturnType();
        if (subResourceClass == possibleSubResource) {
            context.register(SomeFilter.class);
        }
    }
    
  • 注释要筛选的任何根资源、资源方法、子资源类


  • 更新 好的,在玩了一些游戏之后,我想出了一些解决方案。。其中很多都很漂亮,但它完成了任务

    请记住,
    DynamicFeature
    中的
    configure
    是为我们拥有的每个资源(方法)调用的

    算法1:
  • 获取要检查的方法并获取其声明类(对于子资源中的方法,声明类将是子资源类)

  • 迭代它的子资源,检查它是否是资源定位器

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

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

    if (responseClass == possibleSubResource) {
        context.register(SomeFilter.class);
    }
    
  • 要使上述方法起作用,实际上需要从locator方法返回子资源类型,而不是
    资源
    。(您可以尝试使用
    资源
    ,但我还没有弄明白)

    下面是可以工作的完整代码(未经过战斗测试:-)

  • 在根资源类中迭代
    方法
    s

    Class<?> possibleSubResource =
             resourceInfo.getResourceMethod().getDeclaringClass();
    
    Class<?> possibleSubResource =
             resourceInfo.getResourceMethod().getDeclaringClass();
    
    for (Method method : SomeResource.class.getDeclaredMethods()) {
    
  • 检查该方法是否具有Http方法批注

    @NameBinding 
    @Target({METHOD, TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Filtered {    
    }
    
    boolean isHttpPresent = false;
    for (Class annot : Arrays.asList(GET.class, POST.class, PUT.class, DELETE.class)) {
        if (method.isAnnotationPresent(annot)) {
            isHttpPresent = true;
            break;
        }
    }
    
  • 检查该方法是否具有
    @Path
    注释。如果有,并且没有Http方法注释,那么我们注册过滤器

    @Filtered
    @Provider
    public class MyFilter implements ContainerRequestFilter {
    
    if (method.isAnnotationPresent(Path.class) && !isHttpPresent) {
        Class subResourceClass = method.getReturnType();
        if (subResourceClass == possibleSubResource) {
            context.register(SomeFilter.class);
        }
    }
    
  • 这是完整的代码

    @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);
                }
            }
        }
    }
    
    @覆盖
    公共void配置(ResourceInfo ResourceInfo、FeatureContext上下文){
    Class resourceClass=resourceInfo.getResourceClass();
    if(resourceClass==SomeResource.class){
    register(SomeFilter.class);
    }
    类possibleSubResource=resourceInfo.getResourceMethod().getDeclaringClass();
    for(方法:SomeResource.class.getDeclaredMethods()){
    布尔值isHttpPresent=false;
    for(类annot:Arrays.asList(GET.Class、POST.Class、PUT.Class、DELETE.Class)){
    如果(方法isAnnotationPresent(annot)){
    isHttpPresent=true;
    打破
    }
    }
    if(方法.isAnnotationPresent(Path.class)&&&!ishtppresent){
    Class subResourceClass=method.getReturnType();
    if(subResourceClass==possibleSubResource){
    register(SomeFilter.class);
    }
    }
    }
    }
    
    同样,这两种解决方案都没有经过测试,但都适用于我尝试过的少数案例。就我个人而言,我会选择名字绑定,但也许这是一个你可以向泽西队提出的问题。这(当根资源被注册时,子资源的自动注册)看起来确实像是应该解决的问题,或者至少可以进行配置。

    我也有类似的需求: 我想要一个注释来专门过滤资源方法,以实现 大概是这样的:

    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"));
                    // ...
                }
            }
        }
    
    }
    
    @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
    在资源上注册了
    过滤器

    @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);
            }
        }
    }
    
    棘手的问题是如何在过滤器中获取注释值,因此要了解
    extendederariinfo
    ,请参见以下内容:

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

    我也这么想,但这是我想避免的。如果明天我添加一个新方法和/或新的子资源,我不想知道我还需要添加那个注释。我希望这是尽可能通用,以避免任何未来的错误。但是谢谢你的回答。请看我的更新。我不喜欢,但它可能会给你一些想法。