Spring WebSocket 404 解决方案

近来学习 Spring WebSocket 时按照 Spring IN ACTION 中示例编写代码,运行时浏览器报 404 错误

WebSocket connection to 'ws://localhost/websocket/marco' failed: Error during WebSocket handshake: Unexpected response code: 404

这里写图片描述

按照 Spring IN ACTION 中步骤:
首先,继承 AbstractWebSocketHandler,重载以下 3 个方法:
- handleTextMessage – 处理文本类型消息
- afterConnectionEstablished – 新连接建立后调用
- afterConnectionClosed – 连接关闭后调用

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

public class MarcoHandler extends AbstractWebSocketHandler {

    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("Received message: " + message.getPayload());
        Thread.sleep(2000);
        session.sendMessage(new TextMessage("Polo!"));
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        System.out.println("Connection established!");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        System.out.println("Connection closed. Status: " + status);
    }

}

其次,使用 JavaConfig 启用 WebSocket 并映射消息处理器

import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(marcoHandler(), "/marco");
    }

    @Bean
    public MarcoHandler marcoHandler() {
        return new MarcoHandler();
    }

}

最后,编写前端 JS 代码发起连接请求及后续消息交互

var url = 'ws://' + window.location.host + '/websocket/marco';
var sock = new WebSocket(url);

sock.onopen = function() {
    console.log('Opening');
    sock.send('Marco!');
};

sock.onmessage = function(e) {
    console.log('Received Message: ', e.data);
    setTimeout(function() {
        sayMarco()
    }, 2000);
};

sock.onclose = function() {
    console.log('Closing');
};

function sayMarco() {
    console.log('Sending Marco!');
    sock.send('Marco!');
}

部署后打开浏览器运行,直接报 404 错误
这里写图片描述

上网搜索了一晚上解决方案,包括参考 stackoverflow.com 上的经验都未解决该问题,直到查看到以下文章:
http://blog.csdn.net/u012750283/article/details/65935998

在此自己也做个记录避免以后遗忘。

WebSocket 实质上借用 HTTP 请求进行握手,启用 Spring WebSocket 需要在 org.springframework.web.servlet.DispatcherServlet 里配置拦截此请求。

以下是解决步骤:
首先,修改 WebSocketConfig 类定义,在类上添加 @Configuration 注解,表明该类以 JavaConfig 形式用作 bean 定义的源(相当于 XML 配置中的 <beans> 元素)。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(marcoHandler(), "/marco");
    }

    @Bean
    public MarcoHandler marcoHandler() {
        return new MarcoHandler();
    }

}

其次,使用 JavaConfig 配置 DispatcherServlet,继承 org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer ,重载以下 3 个方法:
- getRootConfigClasses – 返回带有 @Configuration 注解的类将会用来配置 ContextLoaderListener 创建的应用上下文中的 bean
- getServletConfigClasses – 返回带有 @Configuration 注解的类将会用来定义 DispatcherServlet 应用上下文中的 bean
- getServletMappings – 将一个或多个路径映射到 DispatcherServlet

实际上,如果只需要 Spring WebSocket 生效,则只需要在 getServletConfigClasses 方法中返回用来定义 DispatcherServlet 应用上下文中的 bean

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebSocketInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebSocketConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }

}

重新部署后代开浏览器运行成功

客户端消息
这里写图片描述

服务器消息
这里写图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又言又语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值