news 2026/9/18 23:29:51

Cloudflare Agents 跨域通信实战:React 客户端与 Worker Agent 的 WebSocket/HTTP 完整接入指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cloudflare Agents 跨域通信实战:React 客户端与 Worker Agent 的 WebSocket/HTTP 完整接入指南

Cloudflare Agents 跨域通信实战:React 客户端与 Worker Agent 的 WebSocket/HTTP 完整接入指南

【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents

跨域(Cross-Domain)是 Web 前端接入 Agent 后端时最常踩的坑:浏览器出于同源策略,会拦截来自不同端口、不同域名下的 WebSocket 升级请求与 HTTP 响应,导致 Agent 消息"发出去却收不到"。本指南以 examples/cross-domain 为例,讲解如何在 React 客户端与 Cloudflare Worker Agent 运行在不同域时,打通 WebSocket 实时通信与 HTTP 请求——核心结论只有一句话:务必给routeAgentRequest传入cors: true。读完本文,你将掌握跨域场景下 Worker 端 CORS 配置、鉴权中间件接入,以及前端useAgent的静态与异步两种鉴权连接方式。

示例概览:一前端一 Worker 的跨域拓扑

示例由两个独立进程组成,各自运行在不同的"域"(本例为不同的本地端口,生产环境则对应不同的域名):

  • React 客户端:Vite 开发服务器,默认运行在http://localhost:5173,页面代码见 src/client.tsx;
  • Worker 服务端:Wrangler 本地开发服务器,运行在http://localhost:8787,Agent 实现见 src/server.ts。

浏览器将51738787视为两个不同的源(origin),这正是触发 CORS 与 WebSocket 握手校验的典型场景。客户端的host明确指向 Worker 地址,其实现也在 UI 中直接展示了两端拓扑(见 src/client.tsx):

Client: http://localhost:5173(当前页面) Server: http://localhost:8787(不同端口)

一键启动两端的方式来自 package.json 的start脚本,用concurrently同时拉起 Vite 与 Wrangler,并保证任一进程退出时杀掉另一个:

npm i && npm start

等价于分别执行:

# 终端 A:前端开发服务器 npx vite dev # 终端 B:Agent Worker 本地开发服务器 npx wrangler dev

服务端:cors: true 与 CORS 预检处理

routeAgentRequest 的 cors 选项

README 的 tl;dr 指出,跨域能否成功,关键在于调用routeAgentRequest时传入cors: truerouteAgentRequestagents包提供的核心路由函数,用于把进入 Worker 的请求分发给对应的 Agent(Durable Object)。其cors选项支持boolean | HeadersInit两种形态,从 packages/agents/src/agent-routing.ts 的类型定义可见:

  • cors: true:自动启用一组默认 CORS 响应头;
  • cors: HeadersInit:传入自定义响应头,覆盖默认值,适合需要指定具体来源、携带凭据的场景;
  • 不传(或false):不添加任何 CORS 头,跨域浏览器请求将失败。

当传入true时,默认解析出的响应头如下(见 packages/agents/src/agent-routing.ts):

响应头
Access-Control-Allow-Origin*
Access-Control-Allow-MethodsGET, POST, HEAD, OPTIONS
Access-Control-Allow-Headers*
Access-Control-Max-Age86400

同时,底层路由逻辑会自动拦截OPTIONS预检请求并直接返回 CORS 头(见 packages/agents/src/agent-routing.ts),并在非 WebSocket 响应上统一附加这些头(见 packages/agents/src/agent-routing.ts)。也就是说,开启cors: true后,预检和实际响应两头都由框架代劳。

自定义 CORS 头与预检处理

示例并未满足于默认配置,而是在 src/server.ts 中定义了更贴近生产的一组响应头:

const CORS_HEADERS = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS, PUT, DELETE", "Access-Control-Allow-Headers": "Content-Type, Authorization, X-API-Key", "Access-Control-Allow-Credentials": "true", "Access-Control-Max-Age": "86400" };

相比默认值,这里额外声明了X-API-Key请求头与Access-Control-Allow-Credentials。随后在 Worker 的fetch入口中,先手动处理OPTIONS预检请求,再把其余请求交给routeAgentRequest(见 src/server.ts):

export default { async fetch(request: Request) { // 1. 处理 CORS 预检请求 if (request.method === "OPTIONS") { return new Response(null, { headers: CORS_HEADERS }); } // 2. 路由 Agent 请求并注入鉴权中间件 return ( (await routeAgentRequest(request, env, { cors: true, onBeforeConnect: async (request: Request) => { console.log("🔍 onBeforeConnect called!"); return authMiddleware(request); }, onBeforeRequest: async (request: Request) => { console.log("🔍 onBeforeRequest called!"); return authMiddleware(request); } })) || new Response("Not found", { status: 404 }) ); } };

这里值得注意的实践组合是:框架的cors: true负责兜底、自定义的OPTIONS分支负责精细化控制(如允许凭据与自定义头),两者同时启用并不冲突——手动预检返回后,Agent 路由内的 CORS 逻辑自然不会再有OPTIONS进入。

鉴权中间件:WebSocket 与 HTTP 的统一拦截

跨域之外,示例还演示了如何在握手与请求两个阶段统一鉴权。authMiddleware从 URL 查询参数或Authorization请求头提取 token,校验通过则原样返回request放行,失败则返回 401 响应中断流程(见 src/server.ts):

function authMiddleware(request: Request): Response | Request { const url = new URL(request.url); // URL 参数可能进入应用日志,切勿在日志中记录长期有效 token let token: string | null | undefined = url.searchParams.get("token"); if (!token) token = request.headers.get("Authorization")?.substring(7); if (token) { console.log("Token found:", token); // 超级"强"的 token 校验(演示用) if (token === "demo-token-123") { return request; // 放行,继续请求流程 } } // 中断请求,返回 401 console.log("Authentication failed"); return new Response("Unauthorized: Invalid or missing authentication", { status: 401 }); }

它通过routeAgentRequest的两个回调接入:

  • onBeforeConnect:在WebSocket 握手建立之前执行,决定是否允许客户端连接 Agent;
  • onBeforeRequest:在HTTP 请求派发到 Agent 的onRequest之前执行,决定是否允许请求进入。

两个回调的返回契约一致:返回Request表示放行,返回Response表示中断。代码中Authorization的解析request.headers.get("Authorization")?.substring(7)正是提取Bearer <token>Bearer之后的 token 部分。

Agent 端到端逻辑

鉴权通过后,请求会被路由到MyAgent(Durable Object),其生命周期回调覆盖了连接的完整周期(见 src/server.ts):

  • onConnect:握手成功后读取 URL 查询参数中的tokenuserId,向新连接发送欢迎消息;
  • onMessage:收到客户端消息后,回复带时间戳的回执,并向其他所有连接广播Client <id> says: ...(通过this.getConnections()遍历排除自己);
  • onClose:客户端断开时打印日志;
  • onRequest:处理 HTTP 请求,返回"已鉴权"的处理结果文本。

Worker 的 Durable Object 绑定在 wrangler.jsonc 中声明,注意migrations使用的是new_sqlite_classes(SQLite 支持的 Durable Object),并启用了nodejs_compat兼容标志:

{ "compatibility_date": "2026-06-11", "compatibility_flags": ["nodejs_compat"], "durable_objects": { "bindings": [ { "class_name": "MyAgent", "name": "MyAgent" } ] }, "main": "src/server.ts", "migrations": [ { "new_sqlite_classes": ["MyAgent"], "tag": "v1" } ], "name": "cross-domain" }

客户端:useAgent 的两种跨域鉴权接入方式

React 端通过agents/react提供的useAgenthook 连接 Worker,页面 index.html 挂载 src/client.tsx。示例在同一个页面里提供了两种鉴权模式的切换(通过顶部 checkbox),并在 ReactSuspense中渲染,加载鉴权数据期间展示 fallback。

静态鉴权:query 传参 + Bearer 头

StaticAuthApp使用对象形式的query,把tokenuserId作为 WebSocket 握手 URL 的查询参数传给服务端(对应服务端onConnect中从searchParams读取的值),见 src/client.tsx:

const agent = useAgent({ agent: "my-agent", host: "http://localhost:8787", // 跨域指向 Worker query: { token: authToken, // 鉴权 token(demo-token-123) userId: "demo-user" // 服务端校验用的用户标识 }, onMessage: (message) => { /* 收到消息追加到消息列表 */ }, onError: (error) => { console.error("WebSocket auth error:", error); } });

UI 上还提供了输入框允许修改 token 并"Update Token"刷新连接。与此同时,HTTP 通道使用fetch直接请求 Agent 的 REST 路径/agents/my-agent/default,并把 token 放进Authorization: Bearer <token>头(见 src/client.tsx)——这正是服务端authMiddleware中"Authorization头 → 取Bearer后缀"那条解析路径的来源。

异步鉴权:async query 自动缓存

生产场景中,token 往往需要先从鉴权服务异步获取。AsyncAuthApp演示了把query写成异步函数的写法:useAgent会自动检测并缓存该函数的返回结果,连接会等到鉴权数据就绪后才发起,见 src/client.tsx:

// 模拟鉴权服务:并发获取 token 与用户信息 const asyncQuery = useCallback(async () => { console.log("🔐 Fetching authentication data..."); const [token, user] = await Promise.all([getAuthToken(), getCurrentUser()]); return { token, userId: user.id, timestamp: Date.now().toString() // 转为字符串以兼容 WebSocket 查询参数 }; }, []); const agent = useAgent({ agent: "my-agent", host: "http://localhost:8787", query: asyncQuery, // 异步函数——自动检测并缓存 onMessage: (message) => { /* ... */ }, onError: (error) => { console.error("WebSocket error:", error); } });

这种写法与useAgent的类型定义一致:query既可以是静态的QueryObject,也可以是返回Promise<QueryObject>的函数(见 packages/agents/src/react.tsx),同时配套queryDeps(异步查询缓存依赖)与cacheTtl(毫秒级缓存 TTL,适合时间敏感的 token)。UI 顶部的Suspensefallback("🔐 Loading authentication...")正是在异步查询期间展示的加载态。

端到端效果与验证

启动npm start后,打开 Vite 提供的页面,即可验证两条跨域链路:

  1. WebSocket 链路:在输入框发送消息 → 服务端onMessage触发,客户端收到回执Server received "..." at <时间>;若同时开两个浏览器标签,还能看到广播消息Client <id> says: ...
  2. HTTP 链路:点击 "Send Authenticated HTTP Request" →fetch携带Bearertoken 请求/agents/my-agent/default→ 服务端onBeforeRequest校验后进入onRequest,客户端收到🔐 Authenticated HTTP request processed ...响应文本。

若去掉cors: true或不处理OPTIONS预检,浏览器控制台将出现典型的 CORS 报错(如Failed to fetch、WebSocket 握手被浏览器拦截)。这也再次印证 README 的核心结论:跨域场景下,cors: truerouteAgentRequest的必选项;而结合自定义 CORS 头、onBeforeConnect/onBeforeRequest鉴权中间件与useAgent的静态/异步query,即可在 React 与 Worker Agent 分域部署时,构建一条完整、安全的实时通信链路。

进一步参考:packages/agents/src/react.tsx(useAgent全部选项)、packages/agents/src/agent-routing.ts(routeAgentRequest的 CORS 与路由实现),以及 examples/agents-as-tools、examples/channels 等其他 Agent 接入示例。

【免费下载链接】agentsBuild and deploy AI Agents on Cloudflare项目地址: https://gitcode.com/GitHub_Trending/agents1/agents

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 23:28:01

图片文本分析 API,TaoToken 让 Agent 在 public-apis 做初筛

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 23:27:24

Arduino安装与CH340驱动全攻略:从下载到上传第一个程序

1. 为什么Arduino安装总在第一步卡住很多人第一次接触Arduino&#xff0c;板子还没摸热乎&#xff0c;就被软件安装和驱动识别这两座大山拦住了。我见过太多人兴冲冲拆开快递盒&#xff0c;插上USB线&#xff0c;结果电脑毫无反应&#xff0c;设备管理器里躺着一个带黄色感叹号…

作者头像 李华
网站建设 2026/9/18 23:25:03

UE4引用查看器数据来源与AssetRegistry依赖排查指南

1. 先搞明白引用查看器到底给你看了什么UE4 里的引用查看器&#xff08;ReferenceViewer&#xff09;算是我在项目里点开频率最高的面板之一&#xff0c;尤其是接手别人做的工程、或者大版本合并之后资源莫名其妙报错的时候&#xff0c;第一反应就是把目标资源丢进去看一眼&…

作者头像 李华