是否可以在@ServerEndpoint中获取HttpServletRequest?首先,我正在尝试获取它,以便可以访问HttpSession对象。

最佳答案

更新(2016年11月):此答案中提供的信息适用于JSR356规范,该规范的各个实现可能会在此信息之外有所不同。注释和其他答案中提供的其他建议都是JSR356规范之外的特定于实现的行为。
  
  如果此处的建议引起您问题,请升级Jetty,Tomcat,Wildfly或Glassfish / Tyrus的各种安装。据报告,这些实现的所有当前版本都以下面概述的方式工作。


现在回到2013年8月的原始答案...

Martin Andersson的答案存在并发缺陷。 Configurator可以同时由多个线程调用,在modifyHandshake()getEndpointInstance()的调用之间,您很可能无法访问正确的HttpSession对象。

或说另一种方式...


请求A
修改握手A
要求B
修改握手B
获取端点实例A 获取端点实例B


这是对Martin代码的修改,该代码使用ServerEndpointConfig.getUserProperties()映射使HttpSession@OnOpen方法调用期间可用于套接字实例。

GetHttpSessionConfigurator.java

package examples;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;

public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator
{
    @Override
    public void modifyHandshake(ServerEndpointConfig config,
                                HandshakeRequest request,
                                HandshakeResponse response)
    {
        HttpSession httpSession = (HttpSession)request.getHttpSession();
        config.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}


GetHttpSessionSocket.java

package examples;

import java.io.IOException;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/example",
                configurator = GetHttpSessionConfigurator.class)
public class GetHttpSessionSocket
{
    private Session wsSession;
    private HttpSession httpSession;

    @OnOpen
    public void open(Session session, EndpointConfig config) {
        this.wsSession = session;
        this.httpSession = (HttpSession) config.getUserProperties()
                                           .get(HttpSession.class.getName());
    }

    @OnMessage
    public void echo(String msg) throws IOException {
        wsSession.getBasicRemote().sendText(msg);
    }
}


额外功能:无需instanceof或强制转换。

一些EndpointConfig知识

每个“端点实例”确实存在EndpointConfig个对象。

但是,“端点实例”在规范中有2个含义。


JSR的默认行为,其中每个传入的升级请求都会导致端点类的新对象实例
一个javax.websocket.Session将对象端点实例及其配置与特定的逻辑连接联系在一起。


可能有一个Singleton Endpoint实例用于多个javax.websocket.Session实例(这是ServerEndpointConfig.Configurator支持的功能之一)

ServerContainer实现将跟踪一组ServerEndpointConfig,它们代表服务器可以响应Websocket升级请求的所有已部署终结点。

这些ServerEndpointConfig对象实例可以来自几个不同的来源。


javax.websocket.server.ServerContainer.addEndpoint(ServerEndpointConfig)手动提供


通常在javax.servlet.ServletContextInitializer.contextInitialized(ServletContextEvent sce)调用中完成

javax.websocket.server.ServerApplicationConfig.getEndpointConfigs(Set)调用。
通过扫描Web应用程序以查找@ServerEndpoint带注释的类自动创建。


这些ServerEndpointConfig对象实例作为最终创建javax.websocket.Session时的默认值存在。

ServerEndpointConfig.Configurator实例

现在,在接收或处理任何升级请求之前,所有ServerEndpointConfig.Configurator对象均已存在,并准备执行其主要和唯一目的,以允许自定义Websocket连接到最终javax.websocket.Session的升级过程。

访问特定于会话的EndpointConfig

注意,您不能从端点实例内访问ServerEndpointConfig对象实例。您只能访问EndpointConfig实例。

这意味着,如果您在部署期间提供了ServerContainer.addEndpoint(new MyCustomServerEndpointConfig()),但后来又尝试通过注释访问它,则它将不起作用。

以下所有内容均无效。

@OnOpen
public void onOpen(Session session, EndpointConfig config)
{
    MyCustomServerEndpointConfig myconfig = (MyCustomServerEndpointConfig) config;
    /* this would fail as the config is cannot be cast around like that */
}

// --- or ---

@OnOpen
public void onOpen(Session session, ServerEndpointConfig config)
{
    /* For @OnOpen, the websocket implementation would assume
       that the ServerEndpointConfig to be a declared PathParam
     */
}

// --- or ---

@OnOpen
public void onOpen(Session session, MyCustomServerEndpointConfig config)
{
    /* Again, for @OnOpen, the websocket implementation would assume
       that the MyCustomServerEndpointConfig to be a declared PathParam
     */
}


您可以在Endpoint对象实例的生存期内但在有限的时间内访问EndpointConfig。 javax.websocket.Endpoint.onOpen(Session,Endpoint),带注释的@OnOpen方法或通过使用CDI。 EndpointConfig无法以任何其他方式或在任何其他时间使用。

但是,您始终可以通过Session.getUserProperties()调用(始终可用)访问UserProperty。通过注释技术(例如,在@OnOpen@OnClose@OnError@OnMessage调用期间的Session参数),通过Session的CDI注入,甚至通过使用从javax.websocket.Endpoint扩展的非注释websocket。

升级的工作方式

如前所述,每个定义的端点都会有一个ServerEndpointConfig与其关联。

这些ServerEndpointConfigs是单个实例,表示EndpointConfig的默认状态,这些默认状态最终可供可能并最终创建的端点实例使用。

当传入的升级请求到达时,它已在JSR上进行了以下操作。


路径是否匹配任何ServerEndpointConfig.getPath()条目


如果不匹配,则返回404进行升级

将升级请求传递到ServerEndpointConfig.Configurator.checkOrigin()


如果无效,则返回错误以升级响应
创建HandshakeResponse

将升级请求传递到ServerEndpointConfig.Configurator.getNegotiatedSubprotocol()


将答案存储在HandshakeResponse中

将升级请求传递到ServerEndpointConfig.Configurator.getNegotiatedExtensions()


将答案存储在HandshakeResponse中

创建新的终结点特定的ServerEndpointConfig对象。复制编码器,解码器和用户属性。这个新的ServerEndpointConfig包装了路径,扩展名,端点类,子协议,配置器的默认值。
将升级请求,响应和新的ServerEndpointConfig传递到ServerEndpointConfig.Configurator.modifyHandshake()
调用ServerEndpointConfig.getEndpointClass()
在ServerEndpointConfig.Configurator.getEndpointInstance(Class)上使用类
创建会话,关联端点实例和EndpointConfig对象。
通知连接的端点实例
需要EndpointConfig的带注释的方法获取与此会话相关联的方法。
调用Session.getUserProperties()返回EndpointConfig.getUserProperties()


注意,ServerEndpointConfig.Configurator是每个映射的ServerContainer端点的一个单例。

这是有意实现的,它允许实现者具有多个功能。


如果多个对等方愿意,则返回相同的Endpoint实例。 Websocket编写的所谓无状态方法。
对所有Endpoint实例进行昂贵资源的单点管理


如果实现为每次握手创建一个新的Configurator,则此技术将无法实现。

(公开:我编写并维护了Jetty 9的JSR-356实现)

关于websocket - 从Web套接字@ServerEndpoint中的HttpServletRequest访问HttpSession,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28000112/

10-10 19:29