从Java代码中获取HttpServletRequest(请求)对象

13

我需要在Java代码中获取请求对象(request object)。由于某些原因,我不能将该对象传递给我的代码。是否有一种方法可以像这样说:getCurrentHTTPServletRequest

我可以安全地假设我处于Servlet上下文中。


请详细说明您需要这个的地方、原因和时间,这样我们可以给出更好的建议,以正确的方式完成任务。同时,提供半伪代码/SSCCE也会对我们理解您想要实现的内容有很大帮助。 - BalusC
我需要扩展Solr QueryComponent以接受SolrRequest。不幸的是,SolrRequest并不是一个围绕servlet请求的包装器。 SolrDispatchFilter(用于处理请求,而不仅仅是过滤器)执行HttpServletRequest到SolrRequest的转换。因此,将额外信息从请求传递到我的自定义QueryComponent的唯一方法是修改SolrDispatchFilter,这是我不想做的。非常感谢您的帮助。ThreadLocal变量应该能够胜任我的工作。 - aseem
5个回答

17

如果需要,你应该将它传递下去。其他的做法基本上都会很丑陋。

你可以使用ThreadLocal变量——在获取请求时为该特定线程设置上下文,并在以后获取它。只要你只需要在线程中访问请求,并且不进行任何奇怪的异步请求处理,那么这种方法就可行。但是,由于刚才提到的原因,这种方法比较脆弱。

然而,我强烈建议你明确你的依赖关系。要么将Servlet请求传递下去,要么只传递需要的部分。


4
+1: 对于“应该传递下去”的支持。比起丑陋的ThreadLocal技巧,这更值得推荐,因为请求线程可以被Web容器池化,否则你必须确保在完成后将其从线程中移除。 这需要太多工作并会产生不良副作用。 - BalusC
5
并不总是有可能将对象向下传递。你可能需要跨越一些你无法控制的API。即使你可以改变任何API,也会很傻和不方便在所有方法中声明请求参数,并且每次调用任何方法时都要传递请求对象。只要知道自己在做什么,ThreadLocal就没问题。 - irreputable
1
那么,要么API不适用于特定目的,要么您正在错误的方向上寻找解决方案。例如,从另一侧访问它怎么样?我已经做了太长时间的JSP / Servlet,可以确认有足够好的方法,而且您不一定需要ThreadLocal来实现此目的。 - BalusC
2
@BalusC,您在那些理智的人提供和选择框架的情况下是完全正确的。如果被迫使用某种“天才”设计,则应选择“较小的恶”。当然,我们无法知道这里的情况是什么 - 只知道存在“某些原因” :) - Bozho

10

假设您无法将请求对象传递到调用堆栈下面,则需要一些共享机制,这并不理想,但有时是必要的。

Spring为此提供了RequestContextFilter。它使用了 ThreadLocal,允许代码通过RequestContextHolder获取当前请求。请注意,此过滤器不要求您使用 Spring 的任何其他部分:

Servlet 2.3 Filter,通过 LocaleContextHolder 和 RequestContextHolder 将请求暴露给当前线程。在 web.xml 中注册为过滤器。

此过滤器主要用于与第三方 Servlet(例如 JSF FacesServlet)一起使用。在 Spring 自己的 Web 支持中,DispatcherServlet 的处理已经足够完善。

如果您要使用 ThreadLocal,那么最好使用现有的、可工作的解决方案,而不是冒险出现 ThreadLocal 代码容易出现的错误。


4

Jon Skeet已经讲解了几乎所有内容,但是需要对他的建议“只需所需部分”进行澄清 - 如果您需要传递请求参数,但不需要依赖于HttpServletRequest,请传递request.getParameterMap()

另外,在使用ThreadLocal选项时可以扩展一下 - 您可以有一个Filter来处理所有传入的请求,并在其中设置请求。

public final static ThreadLocal<HttpServletRequest> httpServletRequestTL =
      new ThreadLocal<HttpServletRequest>();

因为您在每个请求中都设置了它(注意过滤器映射),所以您不必担心servlet容器线程池 - 您将始终拥有当前请求。

P.S.这是skaffman提出的spring实用程序背后的逻辑 - 我与他一起推荐稳定的组件,而不是自己制作。


4

没有Servlet API可以做到这一点。然而,Tomcat提供了一个API调用来实现这个功能。

HttpServletRequest request = (HttpServletRequest)org.apache.catalina.core.ApplicationFilterChain.getLastServicedRequest(); 

此方法将获取当前线程传递给Servlet进行服务的最后一个请求。

要使此方法正常工作,Tomcat必须处于“严格的Servlet合规性”模式。如果没有,您需要通过添加以下JVM参数来启用它:

org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true

这是我所面临的情况的最接近答案。如果您感兴趣,我已经为它创建了一个赏金。基本上,我有一个过滤器,从servlet上下文中获取Spring服务工厂对象,该工厂在过滤器调用其getService方法时分配一个bean实例,但我需要传递请求的会话或实际的httpServletRequest对象。但我不应该这样做,因为它会破坏拥有过滤器的远程服务桥接的抽象。您的答案看起来是正确的,但问题是webApp是多用户的,我可能会得到错误的请求,对吗? - jigzat
看起来像是我需要的,但是当我调用时出现了“java.lang.IllegalAccessError: org/apache/catalina/core/ApplicationFilterChain”的错误。 - Laloutre

0

假设由于某些疯狂的业务相关原因,顶级servlet确实是禁忌,仍然有定义ServletFilter的选项来预览请求并将其放入ThreadLocal中。假设web.xml也不是神圣不可侵犯的。

但我同意Jon Skeet的看法,这会非常丑陋。我会编写代码,然后尝试找到其他工作。 :)

实际上,考虑到过滤器可以完全从接收servlet中夺取控制权,您可以使用此技术将代码转移到自己的servlet中,执行任何您想要的操作,然后运行其他“官方”servlet...或者沿着这些线路做任何其他事情。其中一些解决方案甚至允许您正确而强大地处理请求数据。


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