ARTICLE DETAIL

资讯详情

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

Puter.js 网络 API 实战:用 `puter.net.fetch()` 在浏览器中摆脱 CORS 约束,安全发起 HTTP/HTTPS 请求

Puter.js 网络 API 实战:用 `puter.net.fetch()` 在浏览器中摆脱 CORS 约束,安全发起 HTTP/HTTPS 请求 Puter.js 网络 API 实战用puter.net.fetch()在浏览器中摆脱 CORS 约束安全发起 HTTP/HTTPS 请求【免费下载链接】puter The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puterputer.net.fetch()是 Puter.js 客户端 SDK 在puter.net命名空间下提供的高层 HTTP 客户端它允许网页应用与 Puter 应用直接请求任意http/https资源而完全不受浏览器同源策略CORS的束缚。本文以仓库中 Networking/fetch.md 文档为主体结合 requests.js 的完整实现与 net.suite.ts 中的测试用例系统讲解它的语法、参数、返回值、底层原理、边界行为与可运行示例。读完后你既能直接调用该 API 完成跨域请求也能理解它“为何能绕过 CORS”这一核心机制。puter.net.fetch()是什么在 Puter.js 的网络能力体系中puter.net同时提供两个层次的 API见 Networking API 概览底层 Socket 能力puter.net.Socket(hostname, port)创建明文 TCP 连接puter.net.tls.TLSSocket(hostname, port)创建 TLS 加密连接高层 HTTP 客户端puter.net.fetch()即本文主题是对底层 Socket 的封装让你用熟悉的fetch风格直接取回一个标准Response。puter.net.fetch()的定位是“Fetch web resources securely without being bound by CORS restrictions”——在不被 CORS 限制的前提下安全地获取网络资源。该能力对websites普通网站与appsPuter 应用两类平台开放见 fetch.md 文件头的platforms: [websites, apps]frontmatter这意味着在浏览器页面里引入 Puter.js 后即可使用。语法与参数puter.net.fetch(url) puter.net.fetch(url, options)urlString必填要访问资源的 URL可以是http或https协议。协议的选择会决定底层走哪条连接通道详见“底层原理”一节传入其他 scheme 会被直接拒绝。optionsObject可选一个标准的RequestInit风格对象可包含method、headers、body、mode、credentials、cache、redirect、referrer、signal等标准请求选项。需要特别说明的是实现并非逐字段手工解析这些选项而是先执行new Request(...args)把参数归一化为标准 Request 对象再进行后续处理见 requests.jsexport function pFetch (...args) { return new Promise(async (res, rej) { let reqObj; try { reqObj new Request(...args); const parsedURL new URL(reqObj.url); // ...因此凡是当前运行环境Request构造器支持并校验通过的选项组合都可以正常传入而构造Request阶段就会抛出的非法组合例如在GET上携带body会在真正建立连接之前就作为TypeError拒绝有专门测试覆盖见下文“错误与边界行为”。返回值一个PromiseResponseresolve 出标准 WebResponse对象。由于返回的是标准Response你可以照常调用response.text()、response.json()、response.arrayBuffer()、response.blob()并读取response.status、response.statusText、response.ok与response.headers。它为什么能绕过 CORS浏览器 HTTP 栈之外的实现浏览器原生fetch/XHR之所以受 CORS 约束是因为浏览器会在真正发起跨域请求前执行预检preflight、校验服务端返回的Access-Control-Allow-*响应头。而puter.net.fetch的巧妙之处在于——它根本不调用浏览器的 HTTP 协议栈。在 requests.js 中协议到连接通道的分发逻辑清晰可见// Socket creation: regular for HTTP, TLS for https let socket; if ( parsedURL.protocol http: ) { socket new puter.net.Socket(parsedURL.hostname, parsedURL.port || 80); } else if ( parsedURL.protocol https: ) { socket new puter.net.tls.TLSSocket(parsedURL.hostname, parsedURL.port || 443); } else { const errorMsg Failed to fetch. URL scheme ${parsedURL.protocol} is not supported.; // ... rej(errorMsg); return; }http://目标 → 使用明文puter.net.Socket(hostname, port || 80)https://目标 → 使用puter.net.tls.TLSSocket(hostname, port || 443)其他 scheme → 拒绝并返回错误信息。而puter.net.Socket/puter.net.tls.TLSSocket本身是浏览器端可用的原始 TCP 能力。从实现看PSocket.js它是一个“tunnelled over the Wisp relay”的原始 TCP Socket——浏览器无法直接建立裸 TCP于是 Puter.js 通过 WebSocket 形式的 Wisp 隧道协议把 TCP 字节流经 Puter 的中继服务器转发到目标主机客户端通过POST {APIOrigin}/wisp/relay-token/create获取中继服务器地址与一次性 tokenPSocket.js 中会在首次使用时自动获取并在 index.js 中暴露为puter.net.generateWispV1URL()建立PWispHandler(wispServer, wispToken)维护中继连接通过handler.register(host, port, callbacks)注册目标主机与端口的流之后write/close都直接映射到中继的 TCP 流上。由于整条请求链路不再经过浏览器对跨域fetch的 CORS 强制检查跨域限制自然失效。文档层面对此的描述是从你的前端即可建立网络连接无需自己再架设服务器或反向代理见 Networking.md对开发需要调用第三方 API 的 Web 应用来说非常实用。这就是puter.net.fetch与原生fetch最本质的区别。请求发送的完整生命周期了解了“不走浏览器 HTTP 栈”之后再来看puter.net.fetch是如何亲手完成一次 HTTP/1.1 会话的。源码把客户端注释为“Trivial stream based HTTP 1.1 client”一个基于流的轻量 HTTP/1.1 客户端requests.js其流程如下。1. 组装请求头把归一化后的 Request 转成原始 HTTP/1.1 请求行与头部requests.jslet reqHead ${reqObj.method} ${parsedURL.pathname}${parsedURL.search} HTTP/1.1\r\nHost: ${parsedURL.host}\r\nConnection: close\r\n; for ( const [key, value] of headers ) { reqHead ${key}: ${value}\r\n; }几点值得注意的细节默认 User-Agent若调用方没有显式提供user-agent实现会尝试读取globalThis.navigator?.userAgent并填入。源码专门说明在workerdCloudflare Workers 运行时等环境中并不存在navigator因此这里做了可选链保护——宁可不发送 UA 也不伪造一个requests.js。这也侧面说明该 API 的实现在部分服务端 JavaScript 运行时同样可用。请求行由 method、pathname、search 拼出不含 hash 片段Host取自解析后的完整 host。连接使用Connection: close即每个请求都会在完成后关闭底层 TCP。2. 请求体的 Content-Length 处理如果请求携带 body代码会先把 body 读成字节reqObj.arrayBuffer()然后自动补齐Content-Lengthrequests.jsif ( reqObj.body ) { requestBody new Uint8Array(await reqObj.arrayBuffer()); // If we have a body, we need to set the content length if ( ! headers.has(content-length) ) { headers.set(content-length, requestBody.length); } else if ( headers.get(content-length) ! String(requestBody.length) ) { return rej(Content-Length header does not match the body length. Please check your request.); } reqHead Content-Length: ${requestBody.length}\r\n; }也就是说你通常不必手动计算 Content-Length但如果你显式传了与真实 body 长度不一致的content-length请求会被拒绝对应报错原文见上。测试 net.suite.ts 专门验证了这一行为并注明了一个有趣的平台差异在浏览器中Content-Length是受禁止的请求头new Headers()会直接将其丢弃因此该守卫主要针对 node / workerd 环境生效。3. 通过 Socket 发送并解析响应请求头以及可选的请求体在 socket 的open事件中被写入requests.js。随后实现开始逐字节处理服务端返回的数据先把数据累积起来查找\r\n\r\n一旦拿到完整响应头就调用parseHTTPHead()切出status、statusText与Headersrequests.js响应体通过ReadableStream以流的方式暴露并立即res(new Response(outStream, parsedHead))把 Response 交还给调用方requests.js——因此你拿到的 Response 是边下载边可读的流而不是等全部数据到达后才返回对传输编码做了两种处理基于Content-Length的定长读取以及基于Transfer-Encoding: chunked的分块解码解析器逐块读取十六进制块大小并还原真实数据当内容全部读尽ingestedContent contentLength或 socket 关闭时关闭内部流保证response.text()等读取操作能正常结束。错误与边界行为含测试印证puter.net.fetch的错误路径在测试套件 net.suite.ts 中有精确断言整理如下场景行为报错信息测试佐证传入ftp://等不支持的 scheme直接 reject不发起任何连接Failed to fetch. URL scheme ftp: is not supported.net.fetch refuses a URL scheme it cannot tunnel构造 Request 即非法如 GET 带 body以TypeErrorreject未发生拨号由运行环境Request抛出net.fetch surfaces an invalid request rather than dialing显式content-length与 body 长度不一致rejectContent-Length header does not match the body length. Please check your request.net.fetch rejects a Content-Length that disagrees with the bodynode / workerd 平台socket 层连接失败rejectSocket errored with the following reason: reason见 requests.js 的错误事件处理特别值得注意的一点是这三类 fetch 失败都在真正拨号之前发生因此它们不依赖可用的 Wisp 中继即可被测试测试注释明确说明凡是无需真实中继即可触达的路径——参数校验、事件接线、失败拨号路径——都不做能力门控见 net.suite.ts。反过来只要目标主机可达、中继服务正常成功路径会返回一个携带真实状态码与流式 body 的标准Response。此外从 requests.js 的// TODO optional redirect handling注释可以推断该实现目前不会自动跟随重定向3xx响应会原样作为 Response 返回给调用方若需要处理重定向需要你在应用层自行判断状态码并再次调用。完整示例官方示例GET 并打印响应正文以下来自 fetch.md 原文档html body script srchttps://js.puter.com/v2//script script (async () { // Send a GET request to example.com const request await puter.net.fetch(https://example.com); // Get the response body as text const body await request.text(); // Print the body as a code block puter.print(body, { code: true }); })() /script /body /html代码里通过script srchttps://js.puter.com/v2/引入 Puter.js SDKputer.print(body, { code: true })是在 Puter 环境中以代码块形式展示文本的辅助方法。补充示例POST JSON 并读取结构化结果(async () { const response await puter.net.fetch(https://api.example.com/echo, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ hello: puter }), }); console.log(status:, response.status); const data await response.json(); console.log(data); })();这里没有手动填写Content-Length——实现会依据 body 字节长度自动生成若你同时显式提供则必须与实际长度一致否则请求会被拒绝。补充示例渲染到普通网页puter.net.fetch面向普通网站同样可用platforms 含websites在非 Puter 的页面里可以把puter.print换成常规 DOM 渲染html body pre idoutputloading.../pre script srchttps://js.puter.com/v2//script script (async () { const request await puter.net.fetch(https://example.com); const body await request.text(); document.getElementById(output).textContent body; })().catch((err) { document.getElementById(output).textContent Error: err; }); /script /body /html注意将catch挂在外层因为 scheme 不支持、Content-Length 冲突、socket 失败等错误都会以 Promise 拒绝rejection的形式出现。使用限制与前置条件在把puter.net.fetch用于生产前请留意以下几点均以仓库现状为准协议范围仅支持http与https其他 scheme 一律报错HTTPS 请求依赖puter.net.tls.TLSSocket其加密在客户端侧完成仓库的 TLS Socket 文档 说明其 TLS 由 WASM 化的 rustls 实现提供。中继依赖由于底层 Socket 需要经由 Wisp 中继建立 TCP 流因此必须保证 Puter 后端自托管部署时为你的实例的/wisp/relay-token/create等 Wisp 相关服务可用并能连通中继服务器。可见 Networking.md 的 Socket 示例与 net.suite.ts 中对 relay-token 接口的端到端测试。无自动重定向遵循源码中的 TODO 注释3xx不会自动跟进需要应用层处理。浏览器禁止头Content-Length等受浏览器保护的头在浏览器环境中会被new Headers()丢弃相关校验主要在 node / workerd 运行时生效见 net.suite.ts 的注释说明。类型支持与相关 API类型声明Puter.js 通过 index.d.ts 对外导出Networking、SocketEvent、PSocket、PTLSSocket等类型其内部注解把pFetch描述为(input: RequestInfo | URL, init?: RequestInit) PromiseResponse见 requests.js类型签名与标准fetch高度一致迁移成本低。命名空间挂载puter.net在 index.js 中被装配fetch字段直接指向pFetch导出。姊妹 API当你需要比fetch更底层的控制例如自定义协议、保持长连接、服务端推送流时可参考 Socket API 文档 与 TLS Socket API 文档二者分别对应puter.net.Socket与puter.net.tls.TLSSocket。进一步阅读在仓库中继续深挖推荐按以下路径阅读原始 API 文档Networking/fetch.md 与模块总览 Networking.mdpFetch完整实现src/puter-js/src/modules/networking/requests.js底层 Socket 实现PSocket.jsWisp 隧道、relay-token 获取、数据读写底层 TLS 实现PTLS.js行为测试net.suite.ts含 scheme 拒绝、非法请求、Content-Length 冲突、relay-token 校验等端到端断言命名空间装配与generateWispV1URLsrc/puter-js/src/index.js【免费下载链接】puter The Internet Computer! Free, Open-Source, and Self-Hostable.项目地址: https://gitcode.com/GitHub_Trending/pu/puter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表