HTML5 WebSocket 原生 API 不支持请求/响应拦截,但可通过封装 WSClient 类实现拦截器链,支持发送前鉴权、日志、序列化和接收后解密、错误映射等统一处理。

在 HTML5 的 WebSocket 应用中,原生 WebSocket API 不支持类似 HTTP 中的请求/响应拦截机制(如 Axios 的 interceptors),但可以通过封装一个增强型客户端类,手动实现“发送前拦截”和“接收后拦截”逻辑,从而统一处理鉴权、日志、错误重试、消息序列化等通用需求。
核心思路:代理 WebSocket 实例 + 拦截器链
不直接暴露原生 WebSocket 对象,而是创建一个包装类(如 WSClient),内部维护连接状态、消息队列,并提供 useRequestInterceptor 和 useResponseInterceptor 方法注册拦截器函数。每个拦截器接收上下文对象(含 data、type、id、timestamp 等),可同步修改或拒绝操作。
- 请求拦截器在调用
send()前执行,可用于添加 token、打日志、序列化 payload - 响应拦截器在
onmessage触发后、分发给业务回调前执行,可用于解密、校验、统一错误码映射 - 拦截器支持链式调用,任一拦截器返回
false或抛出异常,可中断后续流程
基础封装示例(TypeScript 风格伪代码)
以下为轻量级可运行结构(兼容现代浏览器):
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
class WSClient {
private socket: WebSocket | null = null;
private requestInterceptors: Array<(ctx: WSCtx) => void | false> = [];
private responseInterceptors: Array<(ctx: WSCtx) => void | false> = [];
constructor(url: string) {
this.connect(url);
}
useRequestInterceptor(fn: (ctx: WSCtx) => void | false) {
this.requestInterceptors.push(fn);
}
useResponseInterceptor(fn: (ctx: WSCtx) => void | false) {
this.responseInterceptors.push(fn);
}
send(data: any, type = 'message') {
const ctx: WSCtx = { data, type, id: Date.now().toString(36), timestamp: Date.now() };
for (const fn of this.requestInterceptors) {
if (fn(ctx) === false) return; // 中断发送
}
const payload = JSON.stringify(ctx);
this.socket?.send(payload);
}
private handleIncoming(raw: string) {
let ctx: WSCtx;
try {
ctx = JSON.parse(raw);
} catch {
return;
}
for (const fn of this.responseInterceptors) {
if (fn(ctx) === false) return; // 中断分发
}
// 分发到具体 type 处理器,例如 dispatch('auth_result', ctx.data)
}
}
典型拦截器场景与写法
实际项目中常用拦截器包括:
立即学习“前端免费学习笔记(深入)”;
-
鉴权注入:请求拦截器自动读取 localStorage token 并挂载到
ctx.headers或扩展字段 -
心跳保活:请求拦截器识别
type === 'ping',自动设置超时重连;响应拦截器对'pong'更新最后活跃时间 -
协议标准化:响应拦截器将原始数据包裹为
{ code: 0, msg: '', data: ... }格式,屏蔽服务端差异 -
离线缓存:请求拦截器检测
socket.readyState !== 1,暂存消息并触发重发队列
注意事项与边界处理
WebSocket 是全双工长连接,不同于 HTTP 请求-响应模型,因此拦截器设计需注意:
- 响应拦截无法“阻塞等待”,不能像 Axios 那样 await 异步处理;所有拦截器必须同步执行
- 建议为每条消息分配唯一
id,便于请求-响应匹配(尤其在 RPC 场景下) - 避免在拦截器中执行耗时操作(如大型 JSON 解析、DOM 操作),否则会阻塞消息流
- 连接断开时需清空拦截器引用,防止内存泄漏(尤其在 SPA 页面切换时)


















