为了账号安全,请及时绑定邮箱和手机立即绑定

从Web Socket @ServerEndpoint中的HttpServletRequest

从Web Socket @ServerEndpoint中的HttpServletRequest

饮歌长啸 2019-09-20 16:28:12
是否可以在@ServerEndpoint中获取HttpServletRequest?主要是我试图得到它所以我可以访问HttpSession对象。
查看完整描述

3 回答

?
慕村9548890

TA贡献1884条经验 获得超4个赞

Configurator可以同时由多个线程调用,很可能您无法在来自modifyHandshake()和的调用之间访问正确的HttpSession对象getEndpointInstance()。


或者说另一种方式......


请求A.

修改握手A.

请求B.

修改握手B.

获取端点实例A < - 这将具有请求B的HttpSession

获取端点实例B.

这是对Martin的代码的修改,它使用ServerEndpointConfig.getUserProperties()map 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将对象端点实例及其配置连接到特定逻辑连接的A。

可以将单个端点实例用于多个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()呼叫访问UserProperties,该呼叫始终可用。此用户属性地图总是可用的,通过注释的技术(如在会话期间的参数是它@OnOpen,@OnClose,@OnError,或@OnMessage呼叫),通过CDI注射会话的,或者即使使用,从延长非注释的WebSockets的javax.websocket.Endpoint。


升级如何运作


如前所述,每个定义的端点都将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)上使用class

创建Session,关联端点实例和EndpointConfig对象。

通知连接的端点实例

需要EndpointConfig的带注释的方法获取与此Session关联的方法。

调用Session.getUserProperties()返回EndpointConfig.getUserProperties()

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


这是有意的,并且是期望的,以允许实现者具有若干特征。


如果他们愿意,为多个对等体返回相同的Endpoint实例。所谓无状态的websocket写作方法。

为所有Endpoint实例提供昂贵资源的单点管理

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


查看完整回答
反对 回复 2019-09-20
?
天涯尽头无女友

TA贡献1831条经验 获得超9个赞

可能吗?

让我们回顾一下Java API for WebSocket规范,看看是否可以获取HttpSession对象。该规范说,第29页:


因为websocket连接是使用http请求启动的,所以在客户端运行的HttpSession和在该HttpSession中建立的任何websockets之间存在关联。API允许在打开握手中访问对应于同一客户端的唯一HttpSession。


所以是的,这是可能的。


但是,我认为你不可能获得HttpServletRequest对象的引用。您可以使用a 监听所有新的servlet请求ServletRequestListener,但您仍然需要确定哪个请求属于哪个服务器端点。如果您找到解决方案,请告诉我们!


抽象的方法

如何在说明书的第13页和第14页中对其进行了松散的描述,并在下一个标题下以代码示例。


在英语中,我们需要拦截握手过程以获取HttpSession对象。然后,为了将HttpSession引用传递给我们的服务器端点,我们还需要在容器创建服务器端点实例并手动注入引用时进行拦截。我们通过提供我们自己ServerEndpointConfig.Configurator的方法modifyHandshake()并覆盖方法来完成所有这些getEndpointInstance()。


自定义配置程序将按逻辑实例化一次ServerEndpoint(请参阅JavaDoc)。


代码示例

这是服务器端点类(我在此代码片段之后提供了CustomConfigurator类的实现):


@ServerEndpoint(value = "/myserverendpoint", configurator = CustomConfigurator.class)

public class MyServerEndpoint

{

    private HttpSession httpSession;


    public void setHttpSession(HttpSession httpSession) {

        if (this.httpSession != null) {

            throw new IllegalStateException("HttpSession has already been set!");

        }


        this.httpSession = httpSession;

    }


    @OnOpen

    public void onOpen(Session session, EndpointConfig config) {

        System.out.println("My Session Id: " + httpSession.getId());

    }

}

这是自定义配置器:


public class CustomConfigurator extends ServerEndpointConfig.Configurator

{

    private HttpSession httpSession;


    // modifyHandshake() is called before getEndpointInstance()!

    @Override

    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {

        httpSession = (HttpSession) request.getHttpSession();

        super.modifyHandshake(sec, request, response);

    }


    @Override

    public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {

        T endpoint = super.getEndpointInstance(endpointClass);


        if (endpoint instanceof MyServerEndpoint) {

            // The injection point:

            ((MyServerEndpoint) endpoint).setHttpSession(httpSession);

        }

        else {

            throw new InstantiationException(

                    MessageFormat.format("Expected instanceof \"{0}\". Got instanceof \"{1}\".",

                    MyServerEndpoint.class, endpoint.getClass()));

        }


        return endpoint;

    }

}


查看完整回答
反对 回复 2019-09-20
  • 3 回答
  • 0 关注
  • 4551 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信