在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"));
// ...
}
}
}
}
但我希望避免搜索匹配的资源。这可能吗?
答案 0 :(得分:6)
我不是100%确定我理解这个问题,但似乎您想要限制哪些资源应该通过过滤器。为此,您只需使用Name Binding。
基本步骤:
创建@NameBinding
注释
@NameBinding
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Filtered {
}
注释过滤器
@Filtered
@Provider
public class MyFilter implements ContainerRequestFilter {
注释您想要过滤的任何根资源,资源方法,子资源类
好的,所以经过一些游戏之后,我想出了几个解决方案......其中很好,但它完成了工作。
请注意,我们为每种资源(方法)调用了configure
中的DynamicFeature
。
获取正在检查的方法并获取其声明类(在子资源中的方法的情况下,声明类将是子资源类)
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);
}
}
}
}
为了实现这一点,我们将基于以下假设:定义子资源的是它是@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);
}
}
}
}
同样,这些解决方案都没有经过严格的测试,但是我尝试过的少数几个案例都有效。就个人而言,我只是选择名字绑定,但也许这是泽西队可以提出的问题。这(子资源的自动注册,当注册根资源时) 看起来像应该可以解决方案,或者至少可以配置。
答案 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);
}
}