如何将自定义上下文绑定到Jersey请求

3
我有一个Jersey REST应用程序,并使用基于令牌的用户身份验证。当请求进入时,会创建一个自定义的RestContext对象,并将其添加到ContainerRequestContext属性中(通过在接收到请求后运行的筛选器)。此上下文管理用户授权(通过角色)和访问其他业务逻辑。它可用于资源中执行业务逻辑。处理请求时,在管道的最后执行第二个过滤器来清理RestContext
尽管需要两个过滤器,但这很好用。我一直在阅读关于使用HK2和InjectionResolver的文章,我想知道是否可以使用注入将此RestContext注入到我的资源和其他过滤器中(例如,我有一个过滤器从RestContext创建SecurityContext)。但我找不到答案。通常,如何为每个请求注入一个依赖于请求上下文的对象?这是可能的吗?是否有更简单的方法,例如使用@Context
编辑:正如指出的那样,我基本上想在我的资源中注入一个定制类,类似于文档。但是,我似乎无法正确注册我的AbstractBinder以绑定注入我的类。我得到以下信息:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=RestContext,parent=RestContextFilter,qualifiers={}),position=0,optional=false,self=false,unqualified=null,1435496015)

编辑2: 我设法取得了一些进展。我按以下方式创建了我的配置文件:
new ResourceConfig(allResources())
  .packages(packagesToScan())
  .registerInstances(new RestContextBinder());

由于文档明确说明绑定剂的注入不是通过类而是通过实例不支持,然而我现在遇到了这个问题:
A MultiException has 3 exceptions.  They are:
1. java.lang.IllegalStateException: Not inside a request scope.
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of my.package.RestContextFilter errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on my.package.RestContextFilter

RestContext被@Inject注入到请求/响应过滤器中。然后用它来创建一个SecurityContext并将其设置在ContainerRequestContext中,在响应过滤器中清理它。难道响应过滤器不是请求作用域的吗?为什么我会收到错误信息?

我对在Jersey中正确的处理方式不太确定。话虽如此,我认为如果你能找到Context的Jersey实现,你可以编写自己的InjectionResolver来注入Jersey的实现。如果你将你的Context的优先级设置得比Jersey的注入解析器高,它将被选择为Jersey之前的解析器。然后,你可以随意增强Jersey的解析器,当你不需要时,只需调用Jersey的上下文方法作为备选方法即可。 - jwells131313
哇,听起来有点糙!我想我会坚持这种方法,直到找到更主流的东西。这真的不值得努力。 - Giovanni Botta
我可能误解了你的问题,但我认为你想做的是像这样的东西:在你的配置中使用bind(RestContext.class).in(RequestScoped.class)。这将允许你使用@Inject RestContext ctx;,并确保每个请求只有一个实例存在。你可以将它注入到你的过滤器中,并设置必要的信息,然后在其他地方根据需要注入它。你不需要在ContainerRequestContext上设置任何东西,只需注入你的RestContext即可。这符合你的要求吗?如果是的话,我可以将其作为答案添加进去。 - Alden
@Alden 这听起来正是我需要的。这个能通过注解而不是配置文件来实现吗?还有,它是如何进行清理的?我的 RestContext 中有一个 close() 方法,需要在 REST 方法执行后调用。它是如何绑定的呢?最后,这些内容在文档中的哪里可以找到?我找不到任何相关信息!谢谢 - Giovanni Botta
@GiovanniBotta 我刚刚发布了一个回答,概述了我的想法。抱歉,我不知道这些东西在文档中的位置!我曾经花了很长时间来解决像这样的问题。 - Alden
1个回答

7

更新:现在 Jersey 2.7 已经发布,解决方案更加简单。

看起来您需要一个 RequestScoped 绑定。以下是使用 Jersey 2.7 设置的示例:

您需要一个请求过滤器,并且由于 RestContext 将被视为 RequestScoped,因此您可以将提供程序注入到您的过滤器中,设置一些属性,并确保它们在整个请求期间都可用。

@Priority(Priorities.AUTHORIZATION) // filter deals with roles, comes after AUTHENTICATION
public class RestContextFilter implements ContainerRequestFilter
{
    // you need to inject a provider, rather than the class directly
    // because this filter is instantiated before the request scope is ready
    private Provider<RestContext> rcProvider;

    @Inject
    public RestContextFilter(Provider<RestContext> rcProvider)
    {
        this.rcProvider = rcProvider;
    }

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException
    {
        // now you're in a request scope and can get your context
        RestContext rc = rcProvider.get();

        // set some properties on rc here (current user or roles or whatever)
    }
}

您需要在ResourceConfig中使用HK2绑定注册过滤器并绑定RestContext

public class MyResourceConfig extends ResourceConfig
{
    public MyResourceConfig()
    {
        register(RestContextFilter.class);

        register(new AbstractBinder()
        {
            @Override
            protected void configure()
            {
                bindFactory(new Factory<RestContext>()
                {
                    @Override
                    public RestContext provide()
                    {
                        return new RestContext();
                    }

                    // this will get called at the end of the request
                    // allowing you to close your request scoped object
                    @Override
                    public void dispose(RestContext instance)
                    {
                        instance.close();
                    }
                }, RequestScoped.class).to(RestContext.class).in(RequestScoped.class);
            }
        });
    }
}

然后,您可以在资源中注入RestContext,它将具有由过滤器设置的所有信息。例如:

@Path("/my/path")
public class MyResource
{
    private RestContext rc;

    @Inject
    public MyResource(RestContext rc)
    {
        this.rc = rc;
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public void upload(MyPostObj data)
    {
        // do stuff here, and rc is all set up
    }
}

我倾向于使用构造函数注入而不是字段注入,因为我认为这可以让其他开发人员更清楚地了解你代码中的依赖关系。如果我的大括号风格让你感到烦恼,我来自.NET的美好世界:)。


如жһњж€‘жњ‰дёЂдёҒжһ„йЂ е‡Ңж•°RestContext(ContainerRequestContext)пәЊж€‘еЏҮд»ӨеЏҒз”Ё@Injectиү›иҰЊжіЁи§ӘпәЊи®©JerseyењЁе€›е»ғж—¶жіЁе…ӨиҮ·ж±‚дёЉдё‹ж–‡еђ—пәџ - Giovanni Botta
实际上我得到了以下错误信息:org.glassfish.hk2.api.UnsatisfiedDependencyException: 在Injectee(requiredType=RestContext,parent=RestContextFilter,qualifiers={}),position=0,optional=false,self=false,unqualified=null,1983762179)处没有可用的对象进行注入。我的绑定有问题吗?我在我的ResourceConfig对象的构造函数中将RestContextBinder.class添加到了类列表中,这样行不通吗? - Giovanni Botta
RestContextBinder是什么?它是你的过滤器还是扩展了AbstractBinder并配置了RestContext绑定?如果是后者,那应该可以正常工作。如果RestContext需要访问ContainerRequestContext,你可以通过在filter方法中设置属性(而不是构造函数)来实现,或者我认为你可以像这样进行构造函数注入:@Inject public RestContext(@Context ContainerRequestContext)。不过我对此不太确定。我需要看到你的代码才能更好地帮助你。也许可以发布一个gist吗? - Alden
是的,它是一个AbstractBinder,但是它不起作用。我尝试给RestContext添加了一个无参数的构造函数,但是仍然得到相同的异常。 - Giovanni Botta
啊哈,我想我知道问题所在了!将它更改为:bind(RestContext.class).to(RestContext.class).in(RequestScoped.class);。我使用的是Guice而不是HK2,在Guice中绑定一个类到其自身时,不能包含"to"部分,否则会出错。看起来HK2是需要的。我刚刚更新了我的答案。 - Alden
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接