简体   繁体   English

Jersey中过滤器的动态绑定不适用于子资源

[英]Dynamic binding of a filter in Jersey doesn't work for sub-resources

I created a Jersey filter and I need it assigned to some resources (not all). 我创建了一个泽西过滤器,我需要将它分配给一些资源(不是全部)。 Therefore, I'm using dynamic binding to achieve that. 因此,我正在使用动态绑定来实现这一点。

public class MyDynamicFeature implements DynamicFeature {

    @Override
    public void configure(ResourceInfo resourceInfo, FeatureContext featureContext) {
        Path resourcePath = resourceInfo.getResourceClass().getAnnotation(Path.class);
        if (resourcePath != null && resourcePath.value().contains("/v2/"))
        {
            featureContext.register(MyFilter.class);
        }
    }
}

So I want this filter to be applied to all methods in those resources that contain a certain string in their paths. 所以我希望将此过滤器应用于那些在其路径中包含特定字符串的资源中的所有方法。 Some of those resources use sub-resource locators to define sub-resources. 其中一些资源使用子资源定位符来定义子资源。 Eg, 例如,

@Path("/v2/resource_path")
@Consumes({ ... })
@Produces({ ... })
class MyResource 
{
    @Path("/subresource_path")
    public MySubResource getSubResource(@Context ResourceContext rc)
    {
        return rc.getResource(MySubResource.class);
    }   
}

Even though Jersey documentation claims 即使泽西文件声称

The configure method will be executed once for each resource method that is defined in the application. configure方法将针对应用程序中定义的每个资源方法执行一次。

the configure method in MyDynamicFeature shown above doesn't get called for getSubResource method of MyResource class at all. 上面显示的MyDynamicFeature中的configure方法根本没有调用MyResource类的getSubResource方法。 It does get called for all the rest of the methods in MyResource class though (which I omitted in the example). 它确实被调用MyResource类中的所有其余方法(我在示例中省略了)。

Is there a way to make this work for sub-resources? 有没有办法让这项工作适用于子资源? I need my filter to be applied to MySubResource as well. 我也需要将我的过滤器应用于MySubResource

We use Jersey 2.21. 我们使用Jersey 2.21。

Check out this issue . 看看这个问题 I'm not sure that it is currently possible. 我不确定它目前是否可行。 If you add some logging in your feature to log the method and class, you will see that subresource methods are never traversed. 如果在功能中添加一些日志记录以记录方法和类,您将看到永远不会遍历子资源方法。 As explained by Marek in the issue, it's because in order to handle this, the sub-resource locator method would need to be invoked, which it never is. 正如Marek在该问题中所解释的那样,这是因为为了处理这个问题,需要调用子资源定位器方法,这种方法永远不会被调用。

The only workaround is to use Name Binding instead. 唯一的解决方法是使用Name Binding I've tested this and it works (see below). 我已经测试了这个并且它有效(见下文)。 The idea is to make a custom annotation, and annotate the filter, the resource class, and sub-resource class you want filtered. 我们的想法是创建自定义注释,并注释要过滤的过滤器,资源类和子资源类。 For example 例如

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

@SomeAnno
public class Filter implements ContainerRequestFilter {}

@SomeAnno
@Path("v2")
public class V2Resource {

    @Path("sub")
    public V2SubResource get()  {
        return new V2SubResource();
    }

    @SomeAnno
    public static class V2SubResource {
        @GET
        public String get() { return "get"; }
    }
}

The above would bind all the resource methods in V2Resource as well as V2SubResource . 上面将绑定V2Resource所有资源方法以及V2SubResource

Below is a complete example using Jersey Test Framework . 以下是使用Jersey Test Framework的完整示例。 Run it like any other JUnit test 像任何其他JUnit测试一样运行它

UPDATE : Note that with the below tests, with the current (2.26) version of Jersey, the tests for the version 2 resource hangs because of the 1000 status code being returned in the filter. 更新 :请注意,使用以下测试,使用Jersey的当前(2.26)版本,版本2资源的测试将挂起,因为过滤器中返回了1000状态代码。 I guess Jersey doesn't like this. 我猜泽西岛不喜欢这个。 To fix this just change the status code in the filter to 500 and fix the test assertions accordingly to test for a 500 status code. 要解决此问题,只需将过滤器中的状态代码更改为500并相应地修复测试断言以测试500状态代码。

import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.NameBinding;
import javax.ws.rs.Path;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

/**
 * Stack Overflow question http://stackoverflow.com/q/36878817/2587435
 * 
 * Run this like any other JUnit test. Only one required test dependency
 * 
 *  <dependency>
 *      <groupId>org.glassfish.jersey.test-framework.providers</groupId>
 *      <artifactId>jersey-test-framework-provider-inmemory</artifactId>
 *      <version>${jersey2.version}</version>
 *  </dependency>
 *
 * @author Paul Samsotha
 */
public class DynamicSubresourceTest extends JerseyTest {

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

    @Provider
    @Status1000
    public static class Status1000Filter implements ContainerRequestFilter {
        @Override
        public void filter(ContainerRequestContext context) throws IOException {
            context.abortWith(Response.status(500).build());
        }
    }

    @Path("v1")
    public static class V1Resource {

        @GET
        public String get() {
            return "v1";
        }

        @Path("sub")
        public V1SubResource getSub() {
            return new V1SubResource();
        }

        public static class V1SubResource {
            @GET
            public String get() {
                return "v1subresource";
            }
        }
    }

    @Path("v2")
    @Status1000
    public static class V2Resource {

        @GET
        public String get() {
            return "v2";
        }

        @Path("sub")
        public V2SubResource getSub() {
            return new V2SubResource();
        }

        @Status1000
        public static class V2SubResource {
            @GET
            public String get() {
                return "v2subresource";
            }
        }
    }

    @Override
    public ResourceConfig configure() {
        return new ResourceConfig(V1Resource.class, V2Resource.class)
                .property(ServerProperties.WADL_FEATURE_DISABLE, true)
                .register(Status1000Filter.class)
                .register(new LoggingFilter(Logger.getAnonymousLogger(), true));
    }

    @Test
    public void should_return_1000_for_v2_resource_method() {
        final Response response = target("v2").request().get();
        assertThat(response.getStatus(), is(500));
    }

    @Test
    public void should_return_1000_for_v2_subresource_locator() {
        final Response response = target("v2/sub").request().get();
        assertThat(response.getStatus(), is(500));
    }

    @Test
    public void should_return_data_for_v1_resource_method() {
        final Response response = target("v1").request().get();
        assertThat(response.getStatus(), is(200));
        assertThat(response.readEntity(String.class), is("v1"));
    }

    @Test
    public void should_return_data_for_v1_subresource_locator() {
        final Response response = target("v1/sub").request().get();
        assertThat(response.getStatus(), is(200));
        assertThat(response.readEntity(String.class), is("v1subresource"));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM