【问题标题】:Tomcat: using servlet and websocket (jsr356) in same web appTomcat:在同一个 Web 应用程序中使用 servlet 和 websocket (jsr356)
【发布时间】:2017-10-28 05:54:53
【问题描述】:

我在 tomcat 中使用 Guice-servlets 和 websocket 创建了一个示例 webapp,现在一旦使用 guice 过滤器,websocket 就会停止工作

基本信息:

在我的 web.xml 中,我使用 GuiceBasedListener 初始化了 Guiceservlet

<web-app>
     <listener>
        <listener-class>test.GuiceBasedListener</listener-class>
    </listener>          
    <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

GuieBasedListener 将所有请求 /* 绑定到 MyDispatcher 的代码

public class GuiceBasedListener extends GuiceServletContextListener {
    protected Injector getInjector() {
        return Guice.createInjector( new ServletModule() {
            @Override
            protected void configureServlets() {
                bind(MyDispatcher.class).asEagerSingleton();
                serve("/*").with(MyDispatcher.class);//////IMPORTANT LINE//
            }
        });}}

MyDispatcher 只用字符串响应的代码

public class MyDispatcher extends HttpServlet {    
    @Inject private Injector injector;
    public MyDispatcher() {}    
    public void service(ServletRequest req, ServletResponse resp) throws IOException, ServletException {
        resp.getOutputStream().print("SUCCESS:" + req);
    }
}

我还有一个用于 Websocket 的 @ServerEndPoint

@ServerEndpoint(value = "/websocket/chat2")
public class WebSocket{
....
    @OnOpen
    public void start(Session session) {        
        System.out.println("Staring:"+this);
   }
....
}

观察:

  1. 现在,如果我运行应用程序并点击http://app:8080/test,它会返回SUCCESS
  2. 但如果我尝试使用 ws://app:8080/websocket/chat2 连接到 websocket,它会失败
  3. 现在,如果我评论 serve("/*").with(MyDispatcher.class);,基本上如果我们关闭 guice 路由,websocket 就会开始工作

  4. 如果我关闭 guice-servlet 但在 web.xml 中添加一个 servlet 映射,如下 websocket 仍然有效

    &lt; servlet-mapping &gt; &lt; servlet-name &gt; HelloWorld&lt; / servlet-name &gt; &lt; url-pattern &gt; /* &lt; / url-pattern &gt; &lt; / servlet-mapping &gt;

我错过了什么或做错了什么?


编辑:

观察连续性:

  1. 我所做的是定义了一个简单的过滤器,它只响应FILTER

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { response.getOutputStream().print("FILTER"); }

并将我的 web.xml 更改为

<web-app>           
    <filter>
        <filter-name>myFilter</filter-name>
        <filter-class>test.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>       
</web-app>

现在按预期点击http://localhost:8080/app/x 返回FILTER。 但是尝试与 websocket 连接失败,因为请求显示了这样的内容。 我还注意到,当我更改字符串 MyFilter 时,返回响应更改的内容长度,这意味着请求在 tomcat 为 websocket 处理之前到达MyFilter

  1. 我将 web.xml 更改为下面,guice 和 websocket 现在工作正常.. 所以我认为 Guice 不尊重在 GuiceFilter 之后注册的 WsFilter

    <filter>
    <filter-name>myFilter</filter-name>
    <filter-class>org.apache.tomcat.websocket.server.WsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 
    
     <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

TOMCAT 8.0、Window 7、Java 1.7、Guice 4.0、Guice-servlet-4.0

【问题讨论】:

  • “我怎样才能告诉 tomcat 在匹配过滤器 url 模式之前首先考虑 serverEndPoint url 来路由请求?” 坦率地说,Tomcat 已经这样做了。您只是误解了实际问题的原因。我建议重写问题以详细说明观察到的实际问题的症状,而不是询问如何解决错误假设的可能原因。我推测GuiceFilter 只是一个错误,需要一个简单的错误修复,但只要您不详细说明实际观察到的症状,就不清楚究竟是什么。
  • @BalusC 你是对的......我已经重写了这个问题
  • “但是如果我尝试使用 ws://app:8080/websocket/chat2 连接到 websocket,它会失败” 它到底是怎么失败的?究竟发生了什么,究竟发生了什么,究竟发生了什么?
  • @BalusC 添加了一个有趣的观察结果
  • @BalusC 我认为 web.xml 中的过滤器是在 tomcat 的 WsFilter 之前注册的。只是猜测?

标签: tomcat servlets websocket guice-servlet jsr356


【解决方案1】:

这对我来说也像是一个 Guice 问题(正如 cmets 中已经提到的)。在同一个应用程序中使用 servlet 和 WebSockets 应该不是问题,即使使用覆盖 /* 的 servlet 映射。

关于 servlet 和过滤器的 2 件事:

  • 如前所述,当收到 websocket 升级请求(一种非常特殊的 http 请求)时,通常 Tomcat 不应该让它通过过滤器。看起来 Guice 以某种方式打破了这一点。
  • 应用了过滤器in the order their mappings are defined in web.xml

所以,如果WsFilter在前,它会先拦截请求,然后check if it's a WebSocket upgrade request
如果它确实是一个 WebSocket 连接,则过滤器不会将其传递给链的其余部分
如果它是另一种类型的请求(GET、POST...),它将传递它,然后 Guice 会做它的事情。

(所以你在这里找到了第一个解决方案)

如果 Guice 过滤器是第一个,并且您使用 serve("/*")...,那么它会破坏您的 WS。

如果你注释掉serve("/*")...,那么Guice过滤器是不是第一个都没有关系,WsFilter甚至可以不存在:你的WS可以到达(单独建立GuiceFilter就可以了)。

所以 Guice 在 servlet 映射之上有自己的“拦截层”,我认为这就是破坏 WebSockets 的原因。我不知道 Guice 中是否存在错误或需要修复的任何内容(我的意思是可能,但不知道具体是什么),但您可以指定 Guice 的例外情况(与 @ 中的 servlet 映射不同) 987654326@).
替换这个:

serve("/*").with(TestServlet.class);

用这个:

serveRegex("/(?!websocket/).*").with(TestServlet.class);
// Regex that accepts /.* but excludes /websocket/.*

这样,您可以将 servlet 映射保留在 /* 上,并删除不需要的 WsFilter。 (经过测试,适合我)

这是第二种解决方案,它的优点是它还允许为非 WebSocket 内容指定异常。

【讨论】:

    猜你喜欢
    • 2015-05-07
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多