小坑:使用requestListner解決不了這個問題!
如何獲取HttpSession
在使用webSocket實現p2p或者一對多聊天功能的時候我們經常會有這樣的需求:webSocket服務端需要獲取到用戶使用數據庫的用戶信息登錄后的HttpSession獲取個人資料信息。
於是,你會使用這樣的代碼:
package com.xinyulee.ws; import javax.servlet.http.HttpSession; import javax.websocket.HandshakeResponse; import javax.websocket.server.HandshakeRequest; import javax.websocket.server.ServerEndpointConfig; import javax.websocket.server.ServerEndpointConfig.Configurator; /** * Created by zipple on 2017/11/14. * 協助server獲取http session */ public class HttpSessionWSHelper extends Configurator { @Override public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { System.out.println("調用modifyHandshake方法..."); HttpSession session = (HttpSession) request.getHttpSession();//session有可能為空 if (session!=null){ System.out.println("獲取到session id:"+session.getId()); sec.getUserProperties().put(HttpSession.class.getName(),session); }else{ System.out.println("modifyHandshake 獲取到null session"); } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
然后在服務端這樣配置:
@ServerEndpoint(value ="/chatRoom/{username}",configurator=HttpSessionWSHelper.class,encoders = {ServerEncoder.class}) //encoders = {ServerEncoder.class}用於指定sendObject()方法調用的解析器
- 1
- 2
- 3
RequestListner解決不了這個問題的原因
在使用上述方法配置完成以后我滿懷信心的進行測試,但是意外的發現了Tomcat Localhost Log下報了null pointer exception。
經過調試,發現了modifyHandshake方法並不能獲取到HttpSession,在上述HttpSessionWSHelper 類代碼中可以看到我對它進行了判空處理。但是這樣並不能起到什么實際上的作用。我們需要弄明白為什么HandshakeRequest 獲取不到HttpSession。
一開始,我試着去百度了一下,發現有這樣的代碼:
package com.xinyulee.listener; import com.xinyulee.util.Log; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpServletRequest; /** * Created by zipple on 2017/11/14. */ @WebListener public class RequestListener implements ServletRequestListener { public void requestInitialized(ServletRequestEvent sre) { //將所有request請求都攜帶上httpSession----這樣會創建新的session!導致重新創建了會話 Log.info("request--:"+sre.getServletRequest().getLocalAddr()); ((HttpServletRequest) sre.getServletRequest()).getSession(); } public RequestListener() { // TODO Auto-generated constructor stub } public void requestDestroyed(ServletRequestEvent arg0) { // TODO Auto-generated method stub } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
網上解釋說,是因為進行ws連接的時候並沒有HttpSession處於激活狀態,於是便不能獲取到HttpSession。他們給出的解決辦法是對每一個request請求調用sre.getServletRequest()).getSession()方法。
這樣的確可以解決空指針異常的問題,但是新問題又出現了。
在沒有HttpSession激活狀態的時候,使用getSession()方法會新建一個HttpSession,也就是說實際上這個監聽器是在沒有激活HttpSession的情況下不斷新建會話。
這樣已經完全偏離了我們程序的需求:獲取同時在線的HttpSession中保存的個人信息。
那么怎么辦呢?
事實上,這個空指針異常的根源並不是出在我們后端代碼上。
localhost和127.0.0.1其實並不是同一個連接
在前端連接WebSocket的時候,我的代碼是這樣的:
loadWS("ws://127.0.0.1/chatRoom/null");
- 1
然而瀏覽器地址欄是這樣的:
http://localhost:8080/
- 1
網上解釋說如果不使用同一個host,則會創建不同的連接請求。具體細節有興趣的朋友可以去了解一下。
於是我們可以這樣拼接一下:
var host = window.location.host; var url = "ws://"+host+"/chatRoom/null";
- 1
- 2
這樣便可以正常建立ws請求,並且能夠使用自定義的HttpSessionWSHelper 類獲取到HttpSession了