Tomcat: 在同一网络应用程序中使用 servlet 和 websocket (jsr356)
Tomcat: using servlet and websocket (jsr356) in same web app
我在 tomcat 中使用 Guice-servlet 和 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);
}
....
}
观察:
- 现在如果我 运行 应用程序并点击 http://app:8080/test 它 returns
SUCCESS
- 但是如果我尝试使用 ws://app:8080/websocket/chat2 连接到 websocket,它会失败
现在如果我评论 serve("/*").with(MyDispatcher.class);
基本上如果我们关闭 guice 路由,websocket 就会开始工作
如果我关闭 guice-servlet 但在 web.xml 中添加一个 servlet 映射,如下所示,websocket 仍然有效
< servlet-mapping >
< servlet-name > HelloWorld< / servlet-name >
< url-pattern > /* < / url-pattern >
< / servlet-mapping >
我错过了什么或做错了什么?
编辑:
连续观察:
- 我所做的是定义一个简单的过滤器,它只响应
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
return FILTER
。
但是尝试连接 websocket 失败,因为请求显示类似这样的内容。
我还注意到,当我更改字符串 MyFilter
return 时,响应中的内容长度会发生变化,这意味着在 tomcat 为 websocket 处理它之前请求已达到 MyFilter
。
我将 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
这对我来说也像是一个 Guice 问题(正如评论中已经提到的那样)。在同一个应用程序中使用 servlet 和 WebSocket 应该不是问题,即使 servlet 映射覆盖 /*
.
2 条有关 servlet 和过滤器的相关信息:
- 如前所述,当收到 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 filter是不是first并不重要,WsFilter甚至可以不存在:你的WS可以达到(单独建立GuiceFilter就可以了)。
所以 Guice 在 servlet 映射之上有自己的 "layer of interception",我认为这就是破坏 WebSockets 的原因。我不知道 Guice 中是否存在错误或需要修复的任何内容(我的意思是可能,但不知道具体是什么),但是您 可以 指定 Guice 的例外情况(与 servlet 不同web.xml
).
中的映射
替换为:
serve("/*").with(TestServlet.class);
有了这个:
serveRegex("/(?!websocket/).*").with(TestServlet.class);
// Regex that accepts /.* but excludes /websocket/.*
这样,您可以将 servlet 映射保留在 /*
上,并删除不需要的 WsFilter
。 (经过测试,适合我)
这是第二种解决方案,它的优点是它还允许为非 WebSocket 内容指定异常。
我在 tomcat 中使用 Guice-servlet 和 websocket 创建了一个示例 webapp,现在一旦使用 guice 过滤器,websocket 就会停止工作
基本信息:
在我的 web.xml 中,我使用 GuiceBasedListener
<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);
}
....
}
观察:
- 现在如果我 运行 应用程序并点击 http://app:8080/test 它 returns
SUCCESS
- 但是如果我尝试使用 ws://app:8080/websocket/chat2 连接到 websocket,它会失败
现在如果我评论
serve("/*").with(MyDispatcher.class);
基本上如果我们关闭 guice 路由,websocket 就会开始工作如果我关闭 guice-servlet 但在 web.xml 中添加一个 servlet 映射,如下所示,websocket 仍然有效
< servlet-mapping >
< servlet-name > HelloWorld< / servlet-name >
< url-pattern > /* < / url-pattern >
< / servlet-mapping >
我错过了什么或做错了什么?
编辑:
连续观察:
- 我所做的是定义一个简单的过滤器,它只响应
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
return FILTER
。
但是尝试连接 websocket 失败,因为请求显示类似这样的内容。
我还注意到,当我更改字符串 MyFilter
return 时,响应中的内容长度会发生变化,这意味着在 tomcat 为 websocket 处理它之前请求已达到 MyFilter
。
我将 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
这对我来说也像是一个 Guice 问题(正如评论中已经提到的那样)。在同一个应用程序中使用 servlet 和 WebSocket 应该不是问题,即使 servlet 映射覆盖 /*
.
2 条有关 servlet 和过滤器的相关信息:
- 如前所述,当收到 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 filter是不是first并不重要,WsFilter甚至可以不存在:你的WS可以达到(单独建立GuiceFilter就可以了)。
所以 Guice 在 servlet 映射之上有自己的 "layer of interception",我认为这就是破坏 WebSockets 的原因。我不知道 Guice 中是否存在错误或需要修复的任何内容(我的意思是可能,但不知道具体是什么),但是您 可以 指定 Guice 的例外情况(与 servlet 不同web.xml
).
中的映射
替换为:
serve("/*").with(TestServlet.class);
有了这个:
serveRegex("/(?!websocket/).*").with(TestServlet.class);
// Regex that accepts /.* but excludes /websocket/.*
这样,您可以将 servlet 映射保留在 /*
上,并删除不需要的 WsFilter
。 (经过测试,适合我)
这是第二种解决方案,它的优点是它还允许为非 WebSocket 内容指定异常。