ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Umi Utoopack 开发服务器的 WebSocket 代理实战:配置、实现与源码验证

Umi Utoopack 开发服务器的 WebSocket 代理实战:配置、实现与源码验证 Umi Utoopack 开发服务器的 WebSocket 代理实战配置、实现与源码验证【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi本文以 Umi 仓库中的示例examples/with-utoopack-websocket-proxy为主体完整讲解如何在 Utoopack 打包器的开发服务器下为业务 WebSocket 后端/ws路径配置代理使浏览器通过 Umi 开发端口透明访问独立运行的 WebSocket 服务。读完本文你将掌握该示例的三步启动流程、.umirc.ts中proxy配置各参数target/ws/changeOrigin的实际含义并能结合 Umi 源码理解开发服务器的双端口代理架构、WebSocket upgrade 请求如何被路由到正确的后端以及对应的测试如何验证这一行为。示例目标与整体架构该示例要验证的核心问题是当项目使用 Utoopack 作为开发打包器时Umi 的proxy配置能否同时承载两类流量——浏览器到业务后端的 WebSocket 长连接本示例为/ws→ws://127.0.0.1:3000浏览器到 Utoopack 内部 dev server 的 HMR WebSocket/turbopack-hmr。两者互不干扰且都经过 Umi 开发端口转发。要理解这一点先看 Utoopack 开发模式的架构从 packages/bundler-utoopack/src/index.ts 的dev()函数可以看到Umi 对外监听port默认 8000见 L228-L229的 Express 服务器同时 Utoopack 自身的utooPackServe监听utooServePort port 1即 8001const port opts.port || 8000; const utooServePort port 1;Express 服务器承担了 CORS、compression、业务代理opts.config.proxy、静态资源代理和 history fallback 等职责静态资源请求经express-http-proxy转发到 8001 端口HMR 的 WebSocket 则通过一个专门的http-proxy-middleware实例转发到 8001。业务 WebSocket 代理与这条 HMR 通道走的是不同的中间件这正是该示例要验证的路由正确性。快速开始继承自原 README以下操作流程完整继承自 示例 README在仓库根目录执行第一步构建一次本地 Utoopack 适配器只需执行一次pnpm --filter umijs/bundler-utoopack build第二步启动 WebSocket 后端示例自带的无依赖 Node 服务监听ws://127.0.0.1:3000/wspnpm --filter example/with-utoopack-websocket-proxy backend第三步在另一个终端启动 Umi 示例pnpm --filter example/with-utoopack-websocket-proxy dev打开http://127.0.0.1:8000页面应显示状态connected和消息connected through the Umi proxy后端终端应恰好打印一条对/ws的连接日志。三条脚本定义在 package.json 中backend执行node ./scripts/ws-server.mjsdev与build分别是umi dev和umi build。配置解析.umirc.ts中的 proxy 配置示例的完整配置见 .umirc.tsimport { defineConfig } from umi; export default defineConfig({ // utoopack: {}, mako: {}, proxy: { /ws: { target: ws://127.0.0.1:3000, changeOrigin: true, ws: true, }, }, });各参数含义如下参数取值作用键/ws路径前缀作为context匹配所有以/ws开头的请求含 upgrade 请求targetws://127.0.0.1:3000转发目标即后端 WebSocket 服务地址使用ws://协议表明目标是 WebSocket 服务wstrue启用 WebSocket 代理代理层会监听 upgrade 事件并转发握手changeOrigintrue将请求头中的origin改写为目标地址的 origin避免跨域后端拒绝握手注意当前仓库中该文件将utoopack: {}注释、启用了mako: {}说明打包器字段可按需切换而proxy配置本身与所用打包器无关。配置的消费路径可以直接在源码中追踪dev()中执行if (opts.config.proxy) { createProxy(opts.config.proxy, app); }packages/bundler-utoopack/src/index.ts#L257-L259最终落到 packages/bundler-utils/src/proxy.ts 的createProxy。该函数支持三种 proxy 写法对象数组、单对象、键值对形式本示例使用的是键值对形式——键即context随后为每个条目创建http-proxy-middleware中间件并在onProxyReq中处理changeOrigin把origin头替换为target的 origin、在onProxyRes中写入x-real-url响应头packages/bundler-utils/src/proxy.ts#L25-L45。后端实现零依赖的原始 WebSocket 服务后端 scripts/ws-server.mjs 不引入任何第三方 WebSocket 库直接用node:http完成 RFC 6455 握手这对理解代理“到底转发了什么”很有价值普通 HTTP 请求一律返回426 Upgrade RequiredL5-L8强调该端口只接受 WebSocket 握手upgrade事件里校验req.url /ws且存在sec-websocket-key头否则直接销毁 socketL10-L14按协议计算Sec-WebSocket-Accept将客户端的 key 与固定 GUID258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接后做 SHA1再 Base64 编码写入101 Switching Protocols响应头完成握手L16-L30握手完成后立即发送一帧文本消息帧头0x81表示 FIN text opcodeconnected through the Umi proxy并打印[backend] WebSocket connected: /ws日志L31-L32——这正是 README 中“后端终端应报告恰好一条/ws连接”这一验收标准的来源。前端页面浏览器侧的验证逻辑页面 pages/index.tsx 的要点按当前协议动态构造 WebSocket 地址location.protocol https:时用wss:否则用ws:然后拼接location.host与/ws路径L7-L9。注意浏览器访问的是 Umi 开发端口 8000而不是后端的 3000——连接能否建立完全取决于代理是否生效监听open/message/error/close四个事件分别把状态更新为connected、回填后端消息、error、closedL11-L14useEffect清理函数中关闭 socket避免组件卸载后连接泄漏。页面上Status: connected与Backend message: connected through the Umi proxy同时出现即代表整条链路浏览器 → Umi 8000 端口代理 → 后端 3000 端口贯通。源码原理upgrade 请求如何被路由回到 packages/bundler-utoopack/src/index.ts 的dev()WebSocket 相关的关键代码有两处。其一HMR 专用代理在用户 proxy 之前注册L249-L259// proxy ws to utoopack server const wsProxy createProxyMiddleware(/turbopack-hmr, { target: http://127.0.0.1:${utooServePort}, ws: true, logLevel: silent, }); app.use(/turbopack-hmr, wsProxy); if (opts.config.proxy) { createProxy(opts.config.proxy, app); }其二也是最容易被忽略的一步——把 upgrade 事件显式挂到 HTTP 服务器上L350-L354// prevent first websocket auto disconnected // ref https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade if (wsProxy.upgrade) { server.on(upgrade, wsProxy.upgrade); }http-proxy-middleware的 WebSocket 订阅并非在模块加载时立即生效而是在首个常规 HTTP 请求经过时才注册 upgrade 监听显式调用wsProxy.upgrade绑定到server.on(upgrade, ...)可以保证首个 WebSocket 握手不被自动断开。用户的/ws代理则由createProxy内部创建的中间件各自处理其context匹配的 upgrade 请求。这套“两个 WebSocket 代理各管一条路径”的设计被 packages/bundler-utoopack/src/dev.test.ts 中的测试routes WebSocket upgrades to the matching proxy only直接验证L156-L218测试先起一个模拟业务后端响应 upgrade 时记录收到的路径再调用dev()并传入与示例同构的proxy: { /ws: { target: ws://127.0.0.1:port, ws: true } }配置随后分别向开发端口发起/ws和/turbopack-hmr两个 upgrade 请求断言业务后端只收到[/ws]、Utoopack 内部服务只收到[/turbopack-hmr]。这从自动化角度确认了示例所依赖的路由隔离行为。常见问题dev server 启动阶段的 503由于 Express 代理服务器与 Utoopack 内部服务是并发启动的Promise.all([serverReady, utooPackServe(...)])L377-L388静态资源代理存在一个短暂的窗口期目标端口尚未监听时请求会被拒绝。dev()中对这类ECONNREFUSED错误做了专门处理L22-L28 的isUtoopackProxyStartupError与 L288-L294 的错误处理器返回503和文本Utoopack dev server is starting.而不是抛出异常。若你在页面刚打开时看到 503稍等片刻刷新即可。业务 WebSocket 侧同理——后端服务未启动时页面状态会显示error此时优先确认backend脚本已在监听 3000 端口。小结这个看似只有三个文件的示例实际上覆盖了 Utoopack 开发模式下的完整 WebSocket 代理链路配置层.umirc.ts的proxy以路径前缀为键ws: truetarget: ws://...声明 WebSocket 转发changeOrigin: true处理 origin 头改写架构层Umi 8000 端口的 Express 代理服务器与 8001 端口的 Utoopack 内部服务并存/turbopack-hmrHMR与用户代理/ws各走各的中间件验证层前端页面上的状态/消息展示、后端终端的连接日志以及 dev.test.ts 中对 upgrade 路由隔离的单测三层证据互相印证。按上述三步启动流程即可在本地完整复现先构建umijs/bundler-utoopack再起backend与dev在http://127.0.0.1:8000上观察connected状态与connected through the Umi proxy消息即验证成功。【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表