ARTICLE DETAIL

资讯详情

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

Effect 4 中 HttpClientRequest 与 Web Request 的双向转换:toWeb / fromWeb 实战指南

Effect 4 中 HttpClientRequest 与 Web Request 的双向转换:toWeb / fromWeb 实战指南 Effect 4 中 HttpClientRequest 与 Web Request 的双向转换toWeb / fromWeb 实战指南【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect导读本篇文章围绕 EffectTypeScript 生产级应用开发库4.0 中unstable/http模块新增的两个 Web 互操作 API——HttpClientRequest.toWeb与HttpClientRequest.fromWeb展开。这两个 API 让 Effect 的不可变请求模型与标准 WebRequest对象之间可以互相转换是打通 Effect HTTP 客户端与 Fetch API、Service Worker、浏览器/Deno/Node 平台胶水层的桥梁。读完本文你将掌握两者的函数签名与设计动机、请求体Raw / Uint8Array / FormData / Stream / Empty在转换中的底层处理逻辑、toWebResult纯函数形态的容错价值以及如何用仓库中的真实测试用例验证转换行为的正确性。变更背景一个 patch 级别的 Web 互操作增强本次变更记录在 .changeset/pre/eff-742-http-client-request-web.md 中其内容非常精简--- effect: patch --- unstable/http HttpClientRequest: add toWeb and fromWeb conversions for web Request objects从中可以提取三条事实变更对象是effect主包变更级别为patch属于向后兼容的小幅增强不影响既有 API变更位置在unstable/http命名空间下的HttpClientRequest模块变更内容为新增toWeb与fromWeb两个转换函数用于 WebRequest对象的互转。值得注意的是从仓库中 packages/effect/package.json 可以看到当前主包版本为4.0.0-rc.115因此这批 API 标注的since 4.0.0指的就是当前迭代周期中的新能力。由于功能位于unstable命名空间其 API 形状仍可能在未来版本中调整使用时建议锁定并关注主包更新日志。HttpClientRequestEffect 的不可变出站请求模型在深入转换函数之前先理解HttpClientRequest的本质。该模块的源码头注释给出了准确定义见 HttpClientRequest.ts描述不可变的出站 HTTP 客户端请求。HttpClientRequest是 Effect HTTP 客户端与平台适配器共享的请求模型。一个请求将方法、URL、查询参数、hash、头与请求体存储为结构化数据。其核心接口HttpClientRequest.ts#L52-L60export interface HttpClientRequest extends Inspectable.Inspectable, Pipeable { readonly [TypeId]: typeof TypeId readonly method: HttpMethod readonly url: string readonly urlParams: UrlParams.UrlParams readonly hash: Option.Optionstring readonly headers: Headers.Headers readonly body: HttpBody.HttpBody }即一个HttpClientRequest由六部分组成HTTP 方法、URL 字符串、独立的查询参数集合UrlParams、可选的 fragment/hash、请求头集合Headers以及结构化的请求体HttpBody。这种将请求拆解为结构化数据的设计正是它与标准Request对象的根本差异——后者将 URL 与查询串揉在一起而前者允许以类型安全的方式独立操作urlParams与hash。在包导出层面unstable/http通过export * as HttpClientRequest from ./HttpClientRequest.ts见 unstable/http/index.ts将其作为命名空间导出因此使用方式为import { HttpClientRequest } from effect/unstable/httpfromWeb将 Web Request 折叠为结构化模型函数签名与行为fromWeb的签名与实现位于 HttpClientRequest.ts#L901-L909export const fromWeb (request: globalThis.Request): HttpClientRequest { const method request.method.toUpperCase() as HttpMethod return modify(empty, { method, url: new URL(request.url), headers: request.headers, body: fromWebBody(request, method) }) }它是一个同步纯函数输入一个标准 WebRequest输出一个HttpClientRequest。转换过程中会将request.method规范化为大写toUpperCase后映射为HttpMethod用new URL(request.url)解析原始 URL而 query 与 hash 会由modify的底层逻辑拆解到urlParams与hash字段中直接继承请求头集合通过fromWebBody决定请求体映射。请求体的智能化处理fromWebBody的实现HttpClientRequest.ts#L911-L919const fromWebBody (request: globalThis.Request, method: HttpMethod): HttpBody.HttpBody { if (!hasBody(method) || request.body null) { return HttpBody.empty } return HttpBody.raw(request.body, { contentType: request.headers.get(content-type) ?? undefined, contentLength: bodyInternal.parseContentLength(request.headers.get(content-length)) }) }其中有两个值得注意的防御性设计方法不允许带请求体时直接置空hasBody(method)为假如GET、HEAD或request.body null时返回HttpBody.empty避免把无意义或不可读的 body 带入模型。Content-Length 的严格校验parseContentLength会解析content-length头一旦遇到畸形或不安全的数值如2junk、1.5、1e3、超出安全整数范围的9007199254740992该头会被丢弃。对应的测试用例在 HttpClientRequest.test.ts#L247-L263 中逐一验证了这四种非法输入。实测验证完整往返链路仓库测试 HttpClientRequest.test.ts#L221-L245 给出了一个完整的fromWeb用例const webRequest new Request(http://localhost:3000/todos/1?a1a2#top, { method: POST, headers: { content-type: application/json, content-length: 13, x-test: ok }, body: {\foo\:\bar\} }) const request HttpClientRequest.fromWeb(webRequest) strictEqual(request.method, POST) strictEqual(request.url, http://localhost:3000/todos/1) assertSome(request.hash, top) strictEqual(request.headers[content-type], application/json) strictEqual(request.headers[content-length], 13) strictEqual(request.headers[x-test], ok) deepStrictEqual([...request.urlParams], [[a, 1], [a, 2]]) strictEqual(request.body._tag, Raw)该用例验证了三个关键结论URL 中的?a1a2被拆解为urlParams且保留重复键#top被提取到hash字段请求头含自定义头x-test被完整保留文本 body 被包装为Raw标签的HttpBody随后通过HttpClientRequest.toWeb转回 WebRequest后await webRequest2.text()仍能还原出原始载荷{foo:bar}。toWeb从结构化模型构建标准 Web Request双形态 APItoWebResult 与 toWeb模块提供了两个方向相反的导出HttpClientRequest.ts#L921-L992toWebResult纯函数返回Result.ResultRequest, Url.UrlError不进入 Effect 运行时适合在纯逻辑中安全地处理URL 非法这一失败分支toWebEffect 版本返回Effect.EffectRequest, Url.UrlError自动读取当前 Effect 上下文适合在 Effect 流程中与其他 effect 组合。两者的签名export const toWebResult (self: HttpClientRequest, options?: { readonly signal?: AbortSignal | undefined readonly context?: Context.Contextnever | undefined }): Result.ResultRequest, Url.UrlError export const toWeb (self: HttpClientRequest, options?: { readonly signal?: AbortSignal | undefined }): Effect.EffectRequest, Url.UrlErrortoWeb的实现通过Effect.contextWith读取当前 Effect 上下文并把它透传给toWebResult其目的在下面的请求体映射中会体现。逐标签的请求体映射策略toWebResult的核心是根据self.body._tag对五种请求体分别构造RequestInit.bodyHttpClientRequest.ts#L942-L968body._tag映射目标关键细节Empty不设置 body直接break保持无 bodyRaw原始 body若为ReadableStream额外设置duplex: halfUint8Array字节数组直接赋给requestInit.bodyFormDataformData原样传递Stream可读流通过Stream.toReadableStreamWith(self.body.stream, options?.context)将 EffectStream转成 Web 可读流同样设置duplex: half两个细节值得展开duplex: half的作用当请求体是流ReadableStream或 EffectStream转换出的流时Fetch 规范要求必须显式声明duplex: half否则流式 body 的Request构造会失败。这正是toWeb需要把 Effect 上下文传下去的原因之一——Stream.toReadableStreamWith需要从上下文解析运行 Stream 所需的依赖例如调度器。URL 的重新组装toWebResult通过Url.make(self.url, self.urlParams, Option.getOrUndefined(self.hash))将拆解后的 url、query 与 hash 重新拼回完整 URL若拼接失败如http://[::1这类非法地址返回Result.fail(url.failure)。对应的失败用例见 HttpClientRequest.test.ts#L281-L288。流式 body 的完整往返测试 HttpClientRequest.test.ts#L265-L279 验证了流式请求体场景const body new Uint8Array([104, 101, 108, 108, 111]) const request HttpClientRequest.post(http://localhost:3000/stream).pipe( HttpClientRequest.bodyStream(Stream.succeed(body), { contentType: text/plain, contentLength: body.length }) ) const webRequest yield* HttpClientRequest.toWeb(request) strictEqual(webRequest.method, POST) strictEqual(webRequest.url, http://localhost:3000/stream) strictEqual(yield* Effect.promise(() webRequest.text()), hello)这说明即使请求体来自 EffectStream经toWeb转换出的 WebRequest也能以标准方式消费webRequest.text()得到helloEffect 的流抽象与 Web 可读流之间实现了无缝衔接。两种转换的对称性与取舍将两个方向放在一起对比维度fromWebtoWeb/toWebResult输入标准 WebRequestHttpClientRequest输出HttpClientRequest同步RequestEffect 或 Result失败模式无URL 由new URL保证可解析失败抛出异常显式UrlErrortoWebResult为ResulttoWeb为Effect请求体来源request.bodycontent-type/content-length头HttpBody五种标签的逐类映射典型应用接收 Service Worker / Fetch 拦截层传入的 Web 请求并转成 Effect 模型将 Effect 请求投递给fetch、fetch兼容平台或直接用于测试一个值得注意的不对称点是错误处理哲学fromWeb假设输入已是合法 WebRequest其 URL 必然可解析因此是同步无Result的函数而toWeb需要把 Effect 模型中的 url urlParams hash 重新组装存在失败可能例如setUrl(http://[::1)构造的畸形地址因此以Result/Effect形式显式暴露失败。在 Effect 生态中的典型用法用法一把 Web 请求交给 Effect HTTP 客户端当你身处 Fetch 回调、Service Worker 或任何只能拿到标准Request的环境时import { Effect } from effect import { HttpClient, HttpClientRequest } from effect/unstable/http const handleIncoming (webRequest: Request) Effect.gen(function*() { const request HttpClientRequest.fromWeb(webRequest) const client yield* HttpClient.HttpClient const response yield* client.execute(request) return response })用法二将 Effect 请求投递给任意 fetch 实现当你希望复用HttpClientRequest的构建逻辑但把实际发送交给环境自带的fetch或测试用的 mock时import { Effect } from effect import { HttpClientRequest } from effect/unstable/http const sendViaFetch (request: HttpClientRequest) Effect.gen(function*() { const webRequest yield* HttpClientRequest.toWeb(request, { signal: AbortSignal.timeout(5_000) }) const response yield* Effect.promise(() fetch(webRequest)) return response })options.signal参数允许在转换阶段就挂上中止信号从而复用调用方的取消语义。用法三纯逻辑中的容错转换若转换发生在非 Effect 上下文中使用toWebResult以Result形式处理失败import { HttpClientRequest } from effect/unstable/http import { Result } from effect const maybeWebRequest HttpClientRequest.toWebResult( HttpClientRequest.get(http://localhost).pipe( HttpClientRequest.setUrl(http://[::1) // 非法地址 ) ) if (Result.isFailure(maybeWebRequest)) { // 处理 Url.UrlError }总结toWeb与fromWeb的加入补齐了HttpClientRequest与标准 WebRequest之间的双向互操作能力fromWeb负责将 Web 请求折叠为 Effect 的结构化模型自动完成 URL 拆分、方法规范化、请求头继承并对畸形content-length做防御性丢弃toWeb/toWebResult负责将 Effect 模型展开为可被fetch直接消费的 WebRequest五种HttpBody标签各有明确的映射策略流式请求体通过duplex: half与Stream.toReadableStreamWith获得支持URL 组装失败则以UrlError显式暴露。对希望把 Effect 请求管线与浏览器 Fetch API、Service Worker 或跨平台适配层打通的开发者而言这两个 API 提供了类型安全且行为可测的官方通道。完整的源码实现位于 HttpClientRequest.ts行为契约由 HttpClientRequest.test.ts 中的web conversions测试组覆盖可作为迁移与联调时的行为参照。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表