在 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 .
基本步骤:
创建一个
@NameBinding
注解@NameBinding @Target({METHOD, TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Filtered { }
注释过滤器
@Filtered @Provider public class MyFilter implements ContainerRequestFilter {
注释任何要过滤的根资源、资源方法、子资源类
更新
好吧,经过一番尝试后,我想出了几个解决方案..其中一个都很漂亮,但它完成了工作。
请记住,我们拥有的每个资源(方法)都会调用 DynamicFeature
中的 configure
。
算法1:
获取被检查的方法并获取其声明类(如果是子资源中的方法,声明类将是子资源类)
Class<?> possibleSubResource = resourceInfo.getResourceMethod().getDeclaringClass();
从您的根资源构建一个临时的
Resource
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
。 (你可以尝试让它与 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();
遍历根资源类中的
Method
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);
}
}
}
}
同样,这些解决方案都没有经过实战测试,但适用于我尝试过的少数情况。就我个人而言,我只会使用名称绑定(bind),但也许这是您可以向 Jersey 团队提出的问题。这(子资源的自动注册,当根资源被注册时)确实看起来应该是开箱即用的东西,或者至少能够被配置。
关于java - Jersey /Jax-RS : how to filter resource and sub-resources,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29407837/