Dropwizard + Jersey:“不在请求范围内";创建自定义注释时 [英] Dropwizard + Jersey : "Not inside a request scope" when creating custom annotation

查看:27
本文介绍了Dropwizard + Jersey:“不在请求范围内";创建自定义注释时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的 Dropwizard 0.8.1 REST 服务,它包含 Jersey 2.17.在 REST/Jetty 服务的上游,我有一些身份验证服务,它向传递给我的 Dropwizard 应用程序的 HTTP 标头添加了一些很好的授权信息.

I have a simple Dropwizard 0.8.1 REST service that pulls in Jersey 2.17. Upstream of the REST/Jetty service I have some authentication service that adds some nice authorization information to the HTTP Header that gets passed to my Dropwizard app.

喜欢能够在我的资源中创建一个自定义注释来隐藏所有杂乱的标头解析到 POJO 垃圾.像这样:

I would love to be able to create a custom annotation in my Resource that hides all the messy header-parsing-to-POJO garbage. Something like this:

 @Path("/v1/task")
 @Produces(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
 public class TaskResource {

      @UserContext                               // <-- custom/magic annotation
      private UserContextData userContextData;   // <-- holds all authorization info

      @GET
      public Collection<Task> fetch() {
           // use the userContextData to differentiate what data to return
      }

我在最后一天浏览了 stackoverflow,发现其他几个人也有同样的问题并出现 (?) 以获得一些满意,但我似乎无法避免得到不在请求范围内"尝试执行此操作时的堆栈跟踪.

I've spent the last day looking around stackoverflow and found several other people who had the same issue and appeared (?) to get some satisfaction, but I can't seem to avoid getting a "Not inside a request scope" stack trace when I try to do this.

因此,我隐藏了所有更改,并尝试直接实现 Jersey 文档第 22.1 和 22.2 节中提供的示例:https://jersey.java.net/documentation/2.17/ioc.html

So I stashed all my changes and tried to implement the example provided in sections 22.1 and 22.2 by the Jersey documentation directly: https://jersey.java.net/documentation/2.17/ioc.html

按照他们的示例(但在我的 Dropwizard 应用程序中),我试图在我的资源中获得一个@SessionInject"注释,但它每次也会因不在请求范围内"堆栈跟踪而爆炸.我在这里做错了什么?

Following along with their example (but in my Dropwizard app), I'm trying to get a "@SessionInject" annotation in my Resource, but it also blows up with "Not inside a request scope" stack trace each time. What am I doing wrong here?

资源:

  @Path("/v1/task")
  @Produces(MediaType.APPLICATION_JSON)
  @Consumes(MediaType.APPLICATION_JSON)
  public class TaskResource {

       private final TaskDAO taskDAO;

       @Context
       private HttpServletRequest httpRequest;

       @SessionInject
       private HttpSession httpSession;

       public TaskResource(TaskDAO taskDAO) {
           this.taskDAO = taskDAO;
       }

       @GET
       public Collection<Task> fetch(@SessionInject HttpSession httpSession) {              
           if (httpSession != null) {
                logger.info("TOM TOM TOM httpSession isn't null: {}", httpSession);
           }
           else {
                logger.error("TOM TOM TOM httpSession is null");
           }
           return taskDAO.findAllTasks();
       }

SessionInjectResolver:

The SessionInjectResolver:

package com.foo.admiral.integration.jersey;

import com.foo.admiral.integration.core.SessionInject;
import javax.inject.Inject;
import javax.inject.Named;

import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Injectee;

import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SessionInjectResolver implements InjectionResolver<SessionInject> {

    private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);

    @Inject
    @Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
    InjectionResolver<Inject> systemInjectionResolver;

    @Override
    public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
        if (HttpSession.class == injectee.getRequiredType()) {
            return systemInjectionResolver.resolve(injectee, handle);
        }

        return null;
    }

    @Override
    public boolean isConstructorParameterIndicator() {
        return false;
    }

    @Override
    public boolean isMethodParameterIndicator() {
        return false;
    }
}

HttpSessionFactory:

The HttpSessionFactory:

package com.foo.admiral.integration.jersey;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class HttpSessionFactory implements Factory<HttpSession> {

    private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);
    private final HttpServletRequest request;

    @Inject
    public HttpSessionFactory(HttpServletRequest request) {
        logger.info("Creating new HttpSessionFactory with request");
        this.request = request;
    }

    @Override
    public HttpSession provide() {
        logger.info("Providing a new session if one does not exist");
        return request.getSession(true);
    }

    @Override
    public void dispose(HttpSession t) {
    }
}

注解:

package com.foo.admiral.integration.core;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface SessionInject {
}

最后,Dropwizard Application 类中的绑定:

And, finally, the binding in the Dropwizard Application class:

@Override
public void run(TodoConfiguration configuration, Environment environment) throws Exception {
    ...

    environment.jersey().register(new AbstractBinder() {
        @Override
        protected void configure() {
            bindFactory(HttpSessionFactory.class).to(HttpSession.class);

            bind(SessionInjectResolver.class)
                    .to(new TypeLiteral<InjectionResolver<SessionInject>>() { })
                    .in(Singleton.class);
        }
    });

旧的堆栈跟踪:

Caused by: java.lang.IllegalStateException: Not inside a request scope.
at jersey.repackaged.com.google.common.base.Preconditions.checkState(Preconditions.java:149)
at org.glassfish.jersey.process.internal.RequestScope.current(RequestScope.java:233)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
at org.jvnet.hk2.internal.MethodInterceptorImpl.invoke(MethodInterceptorImpl.java:74)
at org.jvnet.hk2.internal.MethodInterceptorInvocationHandler.invoke(MethodInterceptorInvocationHandler.java:62)
at com.sun.proxy.$Proxy72.getSession(Unknown Source)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:29)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:14)

一些可能有用的线索:

1) 我注意到我的 HttpSessionFactory 中的日志记录语句永远不会被触发,所以我认为工厂没有正确识别到 DropWizard.

1) I'm noticing is that the logging statements in my HttpSessionFactory are never getting fired, so I don't think the Factory is correctly identified to DropWizard.

2) 如果我将注释更改为参数而不是字段并将注释的使用移动到 fetch() 方法签名中,它不会抛出堆栈跟踪(但 httpSession 仍然为空,大概是因为工厂没有开火......)

2) If I change the annotation to be a Parameter instead of a Field and move the use of the annotation into the fetch( ) method signature like this, it doesn't throw the stack trace (but the httpSession is still null, presumably because the Factory isn't firing...)

 public Collection<Task> fetch(@SessionInject HttpSession httpSession) {

3) 如果我使用 environment.jersey().register() 或 environment.jersey().getResourceConfig().register()注册"活页夹似乎并不重要...同样的事情.

3) It doesn't appear to matter if I "register" the binder with environment.jersey().register() or environment.jersey().getResourceConfig().register()... they appear to do the same thing.

你看到任何明显的问题吗?提前致谢!

Do you see any obvious problems? Thanks in advance!

推荐答案

这是奇怪的行为.但看起来正在发生的是以下

This is weird behavior. But what looks like is going on is the following

  1. 您已将 TaskResource 注册为实例而不是 .class.这我很确定(虽然你没有提到).

  1. You have registered TaskResource as an instance and not as a .class. This I'm pretty sure of (though you have not mentioned).

register(new TaskResource()); 
/* instead of */ 
register(TaskResource.class);

做前者,它将资源设置在单例范围内.后者在请求范围内(除非另有注释 - 见下文)

Doing the former, it set the resource in a singleton scope. The latter in a request scope (unless annotated otherwise - see below)

加载资源模型时,它看到 TaskResource 是一个单例,并且 HttpServletRequest 在请求范围内.那个或那个工厂在每个请求范围内.我猜是两者之一.

When the resource model is loading it sees the TaskResource is a singleton, and that the HttpServletRequest is in a request scope. Either that or that the factory is in a per request scope. I'm guessing one of the two.

我认为它实际上可能一个范围问题,如错误消息中所述,但我非常确定的是,在运行时,它将使用线程本地代理进行处理,因为范围较小.

I thought that it might actually be a scope issue, as mentioned in the error message, but what I'm pretty sure of is that at runtime, it will get handled with a thread local proxy, because of the lesser scope.

您可以通过将 TaskResource 注册为一个类,然后使用 @Singleton 注释 TaskResource 来查看它是否已修复.这是如果您确实确实希望资源类成为单例.如果没有,那么就不要使用 @Singleton.

You can see it fixed by registering the TaskResource as a class, and then annotating the TaskResource with @Singleton. This is if you actually do want the resource class to be a singleton. If not, then just leave off the @Singleton.

对我来说奇怪的是,当资源在启动时显式实例化时,它在启动时失败,但在第一个请求加载框架时有效(这就是将它注册为类时发生的情况).它们都仍处于单例范围内.

The odd thing to me is that it the fact that it fails on startup when the resource is explicitly instantiated on startup, but works when the framework loads on the first request (which is what happens when you register it as a class). They are both still in a singleton scope.

您可能需要考虑的一件事是您是否真的希望资源成为单例.您确实必须担心单例的线程安全问题,并且还有一些其他限制.就个人而言,我更喜欢将它们保留在请求范围内.您必须进行一些性能测试,以查看是否会对您的应用程序产生很大影响.

One thing you might want to take into consideration is whether you actually want the resource to be a singleton or not. You do have to worry about thread safety issues with singletons, and there are are some other limitations. Personally, I prefer to keep them in a request scope. You would have to do some performance testing to see if there is much of an impact for your application.

对于参数注入,您可能需要查看这篇文章

For parameter injection you may want to take a look at this post

另见

  • jersey 2 context injection based upon HttpRequest without singleton. My answer should shed some more light.

这篇关于Dropwizard + Jersey:“不在请求范围内";创建自定义注释时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆