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

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

例如,如果我有以下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.

基本步骤:

>创建@NameBinding批注

@NameBinding 
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Filtered {    
}

>注释过滤器

@Filtered
@Provider
public class MyFilter implements ContainerRequestFilter {

>注释要过滤的任何根资源,资源方法,子资源类

UPDATE

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

请记住,我们为每个资源(方法)调用DynamicFeature中的配置.

算法1:

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

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

>然后检查步骤4中的响应类型是否=步骤1中的声明类.

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

为了使上述工作,您实际上需要从定位器方法而不是资源返回子资源类型. (您可以尝试使其与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方法注释

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

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

>迭代根资源类中的方法

for (Method method : SomeResource.class.getDeclaredMethods()) {

>检查方法是否具有Http方法注释

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方法注释,那么我们注册过滤器

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

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

转载注明原文:java – Jersey / Jax-RS:如何过滤资源和子资源 - 代码日志