Recently, when learning Spring WebSocket, I wrote the code according to the examples in Spring IN ACTION, and the browser reported a 404 error at runtime.
WebSocket connection to 'ws://localhost/websocket/marco' failed: Error during WebSocket handshake: Unexpected response code: 404
Follow the steps in Spring IN ACTION:
First, inherit the AbstractWebSocketHandler and overload the following 3 methods:
- handleTextMessage handles text type messages
- afterConnectionEstablished Called after a new connection is established
- afterConnectionClosed call after the connection is closed
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); }}Second, enable WebSocket and map message processors using JavaConfig
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;@EnableWebSocketpublic class WebSocketConfigurer implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(marcoHandler(), "/marco"); } @Bean public MarcoHandler marcoHandler() { return new MarcoHandler(); }}Finally, write front-end JS code to initiate connection requests and subsequent message interactions
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!');}After deployment, open the browser and run it, and directly report a 404 error
Searching the internet for a night's solution, including the experience on stackoverflow.com, did not solve the problem until I viewed the following article:
Solution to Spring Integrated WebSocket Page Access 404 Problem
Let’s make a record here to avoid forgetting later.
WebSocket essentially borrows HTTP requests for handshake. To enable Spring WebSocket, you need to configure the interception of this request in org.springframework.web.servlet.DispatcherServlet.
Here are the solutions:
First, modify the WebSocketConfig class definition and add the @Configuration annotation to the class, indicating that the class is used as the source of bean definition in the form of JavaConfig (equivalent to the <beans> element in the XML configuration).
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@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(marcoHandler(), "/marco"); } @Bean public MarcoHandler marcoHandler() { return new MarcoHandler(); }} Secondly, use JavaConfig to configure the DispatcherServlet, inherit org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer, and overload the following 3 methods:
- getRootConfigClasses Returns the class with @Configuration annotation to configure beans in the application context created by ContextLoaderListener
- getServletConfigClasses Returns the class with @Configuration annotation to define the bean in the DispatcherServlet application context
- getServletMappings Map one or more paths to DispatcherServlet
In fact, if you only need Spring WebSocket to take effect, you only need to return the bean in the getServletConfigClasses method that defines the DispatcherServlet application context.
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[] {"/"}; }}Redeploy the descendant and open the browser successfully
Client Message
Server Message
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.