java - Jersey /Jax-RS : how to filter resource and sub-resources

标签 java rest jersey jax-rs jersey-2.0

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

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

}

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

最佳答案

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

基本步骤:

  1. 创建一个@NameBinding注解

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

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


更新

好吧,经过一番尝试后,我想出了几个解决方案..其中一个都很漂亮,但它完成了工作。

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

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

要使上述工作正常,您实际上需要从定位器方法返回子资源类型,而不是 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);
        }
    }
    

完整代码如下

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

同样,这些解决方案都没有经过实战测试,但适用于我尝试过的少数情况。就我个人而言,我只会使用名称绑定(bind),但也许这是您可以向 Jersey 团队提出的问题。这(子资源的自动注册,当根资源被注册时)确实看起来应该是开箱即用的东西,或者至少能够被配置。

关于java - Jersey /Jax-RS : how to filter resource and sub-resources,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29407837/

相关文章:

rest - 从网站调用不安全的端点在 HTTPS 下运行 - nginx

java - 通过扭转 @XmlElement 重命名的影响来解码 json?

java - REST Web 服务返回 415 - 不支持的媒体类型

java - 使用 REST API 时出现内部服务器错误

java - 为什么我无法通过 App Engine 将大于 100KByte 的文件上传到 Google Cloud Storage?

java - 比较字符串和整数,不起作用?

java - 当测试类作为 'TestNG Test' 运行时,无法从 testng.xml 检索参数值

枚举和 ConcurrentHashMap 上的 Java 单例。线程安全问题?

java - 在 Java Controller 中检索 play.api.mvc.Request

javascript - app android - 外部资源实现