一:传参:
正常情况下,websocket可以接收路径参数和文本参数(当然也可以接收字节数据),
@OnMessage
public void onMessage(String message, Session session, @PathParam("username") String username) {
log.info("接收到:{}", message + ":" + username);
session.getAsyncRemote().sendText("hi:" + username + ", " + "copy:" + message);
}
websocket不支持直接接收实体类,启动时就会报错No decoder was found for message parameters present on the method [onMessage] of class that was annotated with OnMessage,也不支持直接响应实体类,测试时会发现前端无法接收到,是因为websocket需要实现它自己的encoder和decoder接口,才能正常序列化和反序列化;
当然也可以不实现encoder和decoder接口,接收时直接接收json字符串,然后手动反序列化,输出时也手动序列化以字符串形式响应。
二:WebSocket不是单例模式
@ServerEndpoint(value = "/ws/agg/orderAgg")
@Component
@Slf4j
public class OrderAggWebSocketServer {
private static InspectResultService inspectResultService;
//@Autowired
//private RedisTemplate<String, String> redisTemplate;
@Autowired
public OrderAggWebSocketServer(InspectResultService inspectResultService) {
OrderAggWebSocketServer.inspectResultService = inspectResultService;
}
public OrderAggWebSocketServer() {
}
}
如上,虽然我使用的springboot框架,且上面添加了@Component注解,但是WebSocket并不是单例模式。每一个ws连接都会创建一个ws对象,所以当接口中有依赖其他bean时,不能直接使用@Autowired自动注入,而是将依赖的bean声明为静态属性,然后使用构造器注入才能正常注入。
三:websocket请求头固定的key
在websocket请求中我们想自定义请求头然后添加参数,在使用postman测试时完全正常:如下:
我自定义了Authorization请求头字段,传入token进行校验,测试时正常,但是前端联调时不行。
原因:一般情况下,ws请求自定义请求数据只能添加到key为Sec-WebSocket-Protocol的属性下面,且请求中请求头有什么数据,响应时必须同传回客户端,否则接口异常,正确示例如下:
四:WS接口鉴权:
服务中http请求鉴权是家常便饭了,但是ws接口略有不同,但是也比较简单:在过滤器中,只需要区分出http和ws类型请求,ws类型请求从固定的请求头中获取token然后正常鉴权即可,只是要注意在对请求头参数鉴权和响应时注意上述第三节即可。
区分http和ws接口方法如下:
private boolean isWebSocket(HttpServletRequest request) {
String upgrade = request.getHeader("Upgrade");
return "websocket".equals(upgrade);
}
对于ws接口取出token进行,并将请求头信息放在响应头中。
if (isWebSocket(request)) {
String wsToken = request.getHeader("Sec-WebSocket-Protocol");
if (!StringUtils.hasText(wsToken)) {
return;
} else {
response.addHeader("Sec-WebSocket-Protocol", wsToken);
if (authenticateWS(wsToken, method, uri)) {
filterChain.doFilter(request, response);
return;
}
}
return;
}