I am highly confused with spring boot websockets. I have followed this tutorial https://www.youtube.com/watch?v=TywlS9iAZCM&t=2367s to create the Websocket backend. I wanted to test it with the Websocket capabilities of Postman and also created a console Application in C# to test and play around with the Websockets, however, i always get 404 back. This is really confusing for me since when i look into the logs of the spring boot application it looks like the Websocket request is always treated as a normal GET request, which also does not make a lot of sense to me. I already looked at this question spring boot websocket api 404 not found and this one Spring websocket getting 404 not found but in all tutorials i have watched so far nobody is using the Websocketconfig which implements WebSocketConfigurer and adding the @Controller annotation to my config did not change anything at all. What am i missing and what am i doing wrong?
The logs:
2023-07-11T11:11:23.890+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'stompWebSocketHandlerMapping'
2023-07-11T11:11:23.890+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'webSocketHandlerMapping'
2023-07-11T11:11:23.890+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'resourceHandlerMapping'
2023-07-11T11:11:23.892+02:00 INFO 19376 --- [ main] c.e.w.WebSocketTriesBackendApplication : Started WebSocketTriesBackendApplication in 1.72 seconds (process running for 2.274)
2023-07-11T11:11:23.893+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'springApplicationAdminRegistrar'
2023-07-11T11:11:23.893+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'springApplicationAdminRegistrar'
2023-07-11T11:11:23.893+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'applicationAvailability'
2023-07-11T11:11:23.893+02:00 DEBUG 19376 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
2023-07-11T11:11:23.894+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'springApplicationAdminRegistrar'
2023-07-11T11:11:23.894+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'springApplicationAdminRegistrar'
2023-07-11T11:11:23.894+02:00 TRACE 19376 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'applicationAvailability'
2023-07-11T11:11:23.894+02:00 DEBUG 19376 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
2023-07-11T11:12:23.659+02:00 INFO 19376 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
2023-07-11T11:16:01.904+02:00 INFO 19376 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-07-11T11:16:01.904+02:00 INFO 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-07-11T11:16:01.904+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'multipartResolver'
2023-07-11T11:16:01.904+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@6e4d781
2023-07-11T11:16:01.904+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'localeResolver'
2023-07-11T11:16:01.904+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@503209a1
2023-07-11T11:16:01.904+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'themeResolver'
2023-07-11T11:16:01.904+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.theme.FixedThemeResolver@614fac67
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'stompWebSocketHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'webSocketHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'welcomePageHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'welcomePageNotAcceptableHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'requestMappingHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'viewControllerHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'beanNameHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'routerFunctionMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'resourceHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'defaultServletHandlerMapping'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'requestMappingHandlerAdapter'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'handlerFunctionAdapter'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'httpRequestHandlerAdapter'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'simpleControllerHandlerAdapter'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'errorAttributes'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'handlerExceptionResolver'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'viewNameTranslator'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected DefaultRequestToViewNameTranslator
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'beanNameViewResolver'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'mvcViewResolver'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'defaultViewResolver'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'viewResolver'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'flashMapManager'
2023-07-11T11:16:01.905+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected SessionFlashMapManager
2023-07-11T11:16:01.905+02:00 DEBUG 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2023-07-11T11:16:01.905+02:00 INFO 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2023-07-11T11:16:01.908+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter : Bound request context to thread: org.apache.catalina.connector.RequestFacade@4379a6f
2023-07-11T11:16:01.910+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/ws/app/topic/public", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2023-07-11T11:16:01.917+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping : Mapped to HandlerExecutionChain with [org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@78a0ff63] and 1 interceptors
2023-07-11T11:16:01.925+02:00 DEBUG 19376 --- [nio-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : Processing transport request: GET http://localhost:8080/ws/app/topic/public
2023-07-11T11:16:01.925+02:00 WARN 19376 --- [nio-8080-exec-1] o.s.w.s.s.t.h.DefaultSockJsService : "Unknown transport type for http://localhost:8080/ws/app/topic/public"
2023-07-11T11:16:01.926+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned.
2023-07-11T11:16:01.926+02:00 DEBUG 19376 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND, headers={masked}
2023-07-11T11:16:01.927+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'springApplicationAdminRegistrar'
2023-07-11T11:16:01.927+02:00 TRACE 19376 --- [nio-8080-exec-1] o.s.b.w.s.f.OrderedRequestContextFilter : Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@4379a6f
The WebsocketConfig:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer
{
@Override
public void configureMessageBroker(MessageBrokerRegistry registry)
{
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry)
{
registry.addEndpoint("/ws").withSockJS();
}
}
Eventlistener:
@Component
@RequiredArgsConstructor
@Slf4j
public class WebSocketEventListener
{
private final SimpMessageSendingOperations messageTemplate;
@EventListener
public void HandleWebSocketDisconnectListener(SessionDisconnectEvent event)
{
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
String username = (String) headerAccessor.getSessionAttributes().get("username");
if(username != null)
{
log.info("User disconncted: " + username);
var message = ChatMessage.builder()
.type(MessageType.LEAVE)
.sender(username)
.build();
messageTemplate.convertAndSend("/topic/public", message);
}
}
}
Controller:
@Controller
public class ChatController
{
@MessageMapping("/chat.SendMessage")
@SendTo("/topic/public")
public ChatMessage SendMessage(@Payload ChatMessage message)
{
return message;
}
@MessageMapping("/chat.AddUser")
@SendTo("/topic/public")
public ChatMessage AddUser(@Payload ChatMessage message, SimpMessageHeaderAccessor headerAccessor)
{
headerAccessor.getSessionAttributes().put("username", message.getSender());
return message;
}
}
The method with which i am trying to access the WebSocket:
lass Program
{
static async Task Main()
{
var endpointUrl = "ws://localhost:8080/ws/app/topic/public"; // WebSocket endpoint URL
using (var client = new ClientWebSocket())
{
await client.ConnectAsync(new Uri(endpointUrl), CancellationToken.None); // Connect to the WebSocket endpoint
// Start a separate task to listen for incoming messages
_ = ReceiveMessages(client);
ChatMessage message = new ChatMessage()
{
Content = "test",
Sender = "test",
Type = MessageType.JOIN
};
string jsonString = JsonSerializer.Serialize(message);
// Send a message to the server
await SendMessage(client, jsonString);
// Keep the console application running until interrupted
Console.ReadLine();
await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Console application closed", CancellationToken.None);
}
}
static async Task ReceiveMessages(ClientWebSocket client)
{
var buffer = new byte[1024];
while (client.State == WebSocketState.Open)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Text)
{
var messageBytes = new ArraySegment<byte>(buffer, 0, result.Count);
var message = Encoding.UTF8.GetString(messageBytes.ToArray());
Console.WriteLine("Received message: " + message);
}
}
}
static async Task SendMessage(ClientWebSocket client, string message)
{
var messageBytes = Encoding.UTF8.GetBytes(message);
var buffer = new ArraySegment<byte>(messageBytes);
await client.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
}
}