优雅处理 WebSocket 错误
为服务器端和客户端的 WebSocket 错误实施健壮的错误处理机制。
优雅处理 WebSocket 错误 是 CoddyKit 上的免费 WebSockets & Real-Time Systems with Spring 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 WebSockets & Real-Time Systems with Spring 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 WebSockets & Real-Time Systems with Spring 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Intro to WebSocket Errors
Real-time applications, powered by WebSockets, need robust error handling. Just like any other software, things can go wrong!
Understanding and gracefully managing errors is crucial for a smooth user experience and a stable application. This lesson will cover common error types and how to handle them effectively.
Client-Side Error Types
Errors can occur on the client side (e.g., browser or mobile app) due to various reasons:
- Network Issues: Disconnected internet, firewall blocks.
- Server Problems: Server crash, unhandled exceptions on the server side.
- Protocol Violations: Sending malformed data that the server rejects.
- Client-Side Logic: Errors in processing received messages.
These often manifest as connection drops or messages failing to send/receive.
Handling Client 'onerror' Event
In JavaScript, the WebSocket API provides an onerror event listener. This event fires when a communication error occurs.
While onerror indicates a problem, it often doesn't provide detailed information. It's usually followed by an onclose event, which offers more specific status codes and reasons for the connection termination.
Client Error Handling Example
Here's how you might set up basic error and close handlers on the client side using JavaScript:
Notice how onclose provides more context with event.code and event.reason.
const ws = new WebSocket("ws://localhost:8080/my-ws");
ws.onopen = () => {
console.log("Connected!");
};
ws.onmessage = (event) => {
console.log(`Received: ${event.data}`);
};
ws.onerror = (error) => {
console.error("WebSocket Error: ", error);
// This is a generic error, often followed by onclose
};
ws.onclose = (event) => {
if (event.wasClean) {
console.log(`Closed cleanly, code=${event.code}, reason=${event.reason}`);
} else {
console.error(`Connection died, code=${event.code}, reason=${event.reason}`);
// Handle unexpected closure, e.g., attempt reconnect
}
};
// To trigger an error, try connecting to a non-existent port or URL.Server-Side Error Scenarios
On the server, particularly in a Spring WebSocket application, errors can arise from:
- Message Processing: Exceptions thrown by your
@MessageMappingmethods. - Authentication/Authorization: Security failures preventing message delivery.
- Broker Issues: Problems connecting to or interacting with an external STOMP message broker.
- Transport Errors: Low-level network issues or protocol violations (e.g., malformed frames).
Handling these prevents server crashes and provides meaningful feedback to clients.
Spring WebSocket Error Handling
Spring provides powerful mechanisms to manage server-side WebSocket errors:
@MessageExceptionHandler: For exceptions occurring during STOMP message processing in your controllers.WebSocketHandlerDecoratorFactory: To intercept and handle errors related to the WebSocket connection lifecycle and transport.
These allow you to centralize error logic and respond appropriately.
Using @MessageExceptionHandler
The @MessageExceptionHandler annotation works similarly to @ExceptionHandler in REST controllers. It catches exceptions thrown by methods annotated with @MessageMapping.
You can define methods that handle specific exception types and send a custom error message back to the client, often to a dedicated error topic.
Try sending a message containing "error" to see the exception handler in action.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@SpringBootApplication
public class ErrorHandlingApp {
public static void main(String[] args) {
SpringApplication.run(ErrorHandlingApp.class, args);
}
}
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
@Controller
class ChatController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public String greeting(String message) throws Exception {
if (message.contains("error")) {
throw new IllegalArgumentException("Message contains 'error' keyword!");
}
return "Hello, " + message + "!";
}
@MessageExceptionHandler
@SendTo("/topic/errors")
public String handleIllegalArgumentException(IllegalArgumentException ex) {
return "Error: " + ex.getMessage();
}
}WebSocketHandlerDecoratorFactory
For errors outside of specific @MessageMapping methods, such as low-level transport errors or issues during connection establishment/closure, you can use a WebSocketHandlerDecoratorFactory.
This factory allows you to wrap the default WebSocketHandler with your custom logic, intercepting events like handleTransportError or afterConnectionClosed for robust logging or custom responses.
Implementing a Decorator
Here's an example of implementing a WebSocketHandlerDecoratorFactory. It wraps the standard handler to add custom logging for connection events and transport errors.
This allows you to react to issues that might not be caught by @MessageExceptionHandler, like a sudden client disconnect.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
@SpringBootApplication
@EnableWebSocket
public class DecoratorApp implements WebSocketConfigurer {
public static void main(String[] args) {
SpringApplication.run(DecoratorApp.class, args);
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/ws-decorator").setAllowedOrigins("*");
}
public WebSocketHandler myHandler() {
return new TextWebSocketHandler() {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("Handler: Connection established for " + session.getId());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("Handler: Connection closed for " + session.getId() + " with status " + status);
}
};
}
@org.springframework.context.annotation.Bean
public WebSocketHandlerDecoratorFactory decoratorFactory() {
return (handler) -> new WebSocketHandlerDecorator(handler) {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("Decorator: Client connected: " + session.getId());
super.afterConnectionEstablished(session);
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
System.err.println("Decorator: Transport error for session " + session.getId() + ": " + exception.getMessage());
// You could send a generic error message to the client here if session is still open
super.handleTransportError(session, exception);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
System.out.println("Decorator: Client disconnected: " + session.getId() + ", Status: " + closeStatus.getCode());
super.afterConnectionClosed(session, closeStatus);
}
};
}
}Error Handling Best Practices
To build truly robust WebSocket applications, consider these best practices:
- Log Everything: Use a robust logging framework (e.g., SLF4J/Logback) to capture all errors, warnings, and important events.
- User Feedback: Provide clear, user-friendly messages on the client side when errors occur, avoiding technical jargon.
- Graceful Degradation: If a specific feature fails, ensure the rest of the application remains functional.
- Custom Error Messages: Never expose raw stack traces or internal server details to clients.
- Monitoring: Implement monitoring tools to track connection health and error rates.
Error Handling Challenge
You've learned about various ways to handle WebSocket errors on both the client and server. Let's test your understanding!
Recap: Handling Errors Gracefully
In this lesson, we explored the critical topic of handling WebSocket errors. We covered:
- Common client-side errors and how to use
onerrorandonclose. - Server-side error scenarios in Spring.
- Utilizing
@MessageExceptionHandlerfor STOMP message processing errors. - Implementing
WebSocketHandlerDecoratorFactoryfor connection lifecycle and transport errors. - Key best practices for building robust, error-tolerant real-time applications.
Mastering error handling is vital for creating reliable and user-friendly WebSocket services.
常见问题解答
「优雅处理 WebSocket 错误」课时是免费的吗?
是的 — 「优雅处理 WebSocket 错误」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 WebSockets & Real-Time Systems with Spring 课程的其余内容,请升级到 CoddyKit PRO。 WebSockets & Real-Time Systems with Spring 课程共包含 4 节课。
「优雅处理 WebSocket 错误」这节课中我会学到什么?
为服务器端和客户端的 WebSocket 错误实施健壮的错误处理机制。 你通过在浏览器中直接运行的动手代码来练习 WebSockets & Real-Time Systems with Spring,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 WebSockets & Real-Time Systems with Spring 需要有经验吗?
无需任何先前经验。CoddyKit 上的 WebSockets & Real-Time Systems with Spring 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「优雅处理 WebSocket 错误」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 WebSockets & Real-Time Systems with Spring 课中编写并运行代码吗?
能。每节 WebSockets & Real-Time Systems with Spring 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 优雅处理 WebSocket 错误
- 连接生命周期管理
- 重试与回退
- 心跳与 Ping/Pong 保活