java - Jersey 中过滤器的动态绑定(bind)不适用于子资源

标签 java rest jax-rs jersey-2.0

我创建了一个 Jersey 过滤器,我需要将其分配给某些资源(不是全部)。因此,我使用动态绑定(bind)来实现这一点。

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

因此,我希望将此过滤器应用于那些路径中包含特定字符串的资源中的所有方法。其中一些资源使用子资源定位符来定义子资源。例如,

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

尽管 Jersey 文档声称

The configure method will be executed once for each resource method that is defined in the application.

上面显示的 MyDynamicFeature 中的 configure 方法根本不会被 MyResource 类的 getSubResource 方法调用。不过,它确实会被 MyResource 类中的所有其余方法调用(我在示例中省略了)。

有没有办法让这个工作适用于子资源?我还需要将过滤器应用于 MySubResource

我们使用 Jersey 2.21。

最佳答案

查看this issue 。我不确定目前是否可能。如果您在功能中添加一些日志记录来记录方法和类,您将看到子资源方法永远不会被遍历。正如 Marek 在该问题中所解释的那样,这是因为为了处理这个问题,需要调用子资源定位器方法,但事实并非如此。

唯一的解决方法是使用 Name Binding反而。我已经对此进行了测试并且它有效(见下文)。这个想法是制作一个自定义注释,并注释您想要过滤的过滤器、资源类和子资源类。例如

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

上面将绑定(bind) V2Resource 以及 V2SubResource 中的所有资源方法。

下面是使用 Jersey Test Framework 的完整示例。像任何其他 JUnit 测试一样运行它

更新:请注意,在以下测试中,对于当前 (2.26) 版本的 Jersey,版本 2 资源的测试会因过滤器中返回 1000 状态代码而挂起。我猜泽西不喜欢这样。要解决此问题,只需将过滤器中的状态代码更改为 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"));
    }
}

关于java - Jersey 中过滤器的动态绑定(bind)不适用于子资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36878817/

相关文章:

java - 我如何找到循环中的第n项? java

java - 运行时事件调度线程上的 Swing 验证代码

java - 如何在jFrame中添加交互式动画?

java - 在 Java 中,在发送 JSON POST 时使用 HttpPost(apache commons HttpClient)——我应该对正文进行 URL 编码吗?

android - 如何在改造 API 方法中传递 POST 参数?

javascript - Pusher:如何获取 Pusher 中的私有(private) channel 订阅数?

java - 在 Java REST API 中注入(inject) ContainerRequestFilter

java - IntelliJ 想法 : build and run Java apps in docker containers

api - 在 Firebase 的 POST 请求中丢弃空数组

jax-rs - Jaxrs 多部分