H3 Utils 工具集全览:可组合 HTTP 框架的轻量功能模块
【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3
H3 是一个以可组合(composable)为核心设计理念的极简 HTTP 框架:它不提供臃肿的内核,而是以一个轻量 H3 实例 为起点,围绕请求生命周期提供一系列内置工具函数(utilities),供开发者按需选取,或自由编写自己的工具。本文将系统梳理 H3 Utils 的完整分类体系,逐一讲解 Request、Response、Cookie、Security、Proxy、MCP、More 与 Community 八大模块的核心 API、实战用法与源码级实现原理,帮助你快速定位并掌握这套"即取即用"的工具库。
从"小而精的核心 + 丰富的工具集"理解 H3 的设计哲学
H3 的定位是 minimal framework,但这并不意味着功能匮乏。恰恰相反,它的强大之处在于分层:核心只负责事件(event)、路由与中间件编排,而一切业务能力——解析请求体、构造响应、Cookie 操作、安全校验、反向代理、WebSocket、JSON-RPC——都被封装为独立、可组合的工具函数。
这种设计带来两个直接收益:
- 按需加载:用不到的能力不会进入你的代码路径,应用保持轻量;
- 自由扩展:内置工具与自研工具遵循同一套
(event) => ...的签名约定,可以无缝混用。
全部内置工具函数统一从 src/index.ts 导出,按功能域分组,覆盖:Request、Query、Response、Middleware、Proxy、Body、Cookie、SSE、Timing、Sanitize、Cache、Path、Static、Base、Session、Cors、Auth、Fingerprint、WebSocket、JSON-RPC 等二十余个类别。文档侧的导航索引 docs/2.utils/0.index.md 将其归纳为八个大类,下面逐一展开。
Request:入站请求的读取与校验
对应文档 docs/2.utils/1.request.md,该模块负责把原始请求转化为可用的业务数据,同时内置了大量安全校验。
请求体解析三件套
readBody(event, options?):读取请求体并按Content-Type智能解析。默认按 JSON 解析,当Content-Type为application/x-www-form-urlencoded时回退为 URL 编码解析;其他类型(如multipart/form-data)必须显式传入options.type: "formData"才会解析,绝不从请求头自动探测。从源码 src/utils/body.ts 可以看到,这种严格 opt-in 的设计是为了防止不可信的请求把应用拖入昂贵的 multipart 解析(见源码注释中的 #875 问题)。readValidatedBody(event, validate, options?):读取请求体后立即用校验器验证。校验器可以是普通函数,也可以是 Standard-Schema 兼容的库(如 zod、valibot);失败时抛出校验错误,也可通过options.onError自定义错误响应(如返回自定义statusText和汇总的 issues 信息)。assertBodySize(event, limit):声明式地限制请求体大小。实现上并非预缓冲,而是借助 srvx 的limitRequestBody把请求包装为"边读边计数"的限流代理,一旦累计字节超过limit立即以413中断(src/utils/body.ts)。它还有两个前置防御:诚实的超大Content-Length会在处理前直接413拒绝;同时携带Content-Length与Transfer-Encoding的请求会被判定为请求走私(RFC 7230)并返回400。
app.post("/", async (event) => { assertBodySize(event, 10 * 1024 * 1024); // 10MB const data = await event.req.formData(); });QUERY 方法(RFC 10008)支持
appendAcceptQuery(event, mediaTypes):通过Accept-Query响应头声明资源接受的查询格式(如application/sql;charset=UTF-8),媒体类型按 Structured Fields(RFC 8941)List 序列化。requireContentType(event, acceptedTypes):断言请求Content-Type存在且属于接受列表,缺失返回400、格式非法返回422、类型不被接受返回415;接受类型支持通配符(*、type/*)。
更常用的请求信息工具
getQuery(event):获取解析后的查询字符串对象。getValidatedQuery(event, validate):解析后立即校验查询参数(支持函数/zod/valibot,可自定义onError)。getRequestHost(event, { xForwardedHost? })、getRequestURL(event, opts?)、getRequestProtocol(event, { xForwardedProto? }):分别获取主机、完整 URL 与协议。三个函数默认都不信任x-forwarded-*头(opt-in),因为它是客户端可伪造的输入;仅在确认应用运行在会覆写这些头的可信反向代理/CDN 之后才应开启。文档明确警示:由Host头推导出的 origin 绝不能用于 CSRF/origin 校验、缓存键或生成发给其他用户的绝对链接,除非上游已对 Host 做了白名单固定。getRequestIP(event, { xForwardedFor? }):获取客户端 IP。默认来自event.req.ip(连接对端,或服务器配置信任代理后解析出的客户端地址);xForwardedFor: true时改为取x-forwarded-for链中的第一个条目——文档特别提醒,该位置正是客户端能自行写入的值,启用它等于允许任何调用者自报 IP,从而击穿 IP 白名单、限流与审计。更优做法是让服务器(如 srvxtrustProxy)从右往左解析可信代理链,而不要打开此选项。isMethod(event, expected, allowHead?)/assertMethod(event, expected, allowHead?):校验请求方法,前者返回布尔值,后者不匹配时抛出带Allow响应头的405(符合 RFC 9110)。allowHead: true时允许对 GET 目标放行 HEAD。getRouterParam(event, name, { decode? })/getRouterParams(event, { decode? })/getValidatedRouterParams(event, validate):读取路由参数。默认返回 URL 中百分号编码的原始形态;decode: true时仅解码一层,且路径分隔符(%2f、%5c及其任意%25嵌套)永远不解码——路由按单段匹配的参数绝不能悄悄长出路由与中间件从未见过的/或\。解码一次的结果仍可能包含%XX(如%252e%252e→%2e%2e),切勿再次 decode,否则%2e%2e会还原为../造成路径穿越。
app.get("/files/**:rest", (event) => { // GET /files/%252e%252e/x getRouterParams(event); // { rest: "%252e%252e/x" } getRouterParams(event, { decode: true }); // { rest: "%2e%2e/x" } —— 保持编码,勿再解码 });getRequestFingerprint(event, opts):为入站请求生成唯一指纹,见 src/utils/fingerprint.ts。requestWithBaseURL(req, base, { url? })/requestWithURL(req, url)/toRequest(input, options?):请求对象的高级操作。toRequest可把输入规范化为 WebRequest;若输入是相对 URL,则基于host头合成完整路径,但协议恒为http(忽略x-forwarded-proto),且 host 仅作为合成 URL 的 authority,无法横向扩展进路径——若需控制 origin,请传入绝对 URL。
Response:响应构造、流式输出与清理
对应文档 docs/2.utils/2.response.md,该模块覆盖从安全净化到流式传输的完整响应链路。
安全净化
sanitizeStatusCode(statusCode?, defaultStatusCode):确保状态码为合法 HTTP 状态码。sanitizeStatusMessage(statusMessage):确保状态描述文本安全,仅允许水平制表符、空格与可见 ASCII 字符(RFC 7230 §3.1.2)。
流式与动态内容
iterable(iterable):按顺序逐块发送内容,支持在产出 chunk 的同时穿插异步任务。每个 chunk 必须是字符串或 Buffer;生成器(yielding)函数的返回值与 yield 值同等对待。首个 chunk 会在响应创建前被 await,因此第一块之前设置的event.res.status与 headers 仍然生效,之后设置的一切都将被忽略(头已上线路)。源码位于 src/utils/response.ts。
return iterable(async function* work() { yield "<!DOCTYPE html>\n<html><body><h1>Executing...</h1><ol>\n"; for (let i = 0; i < 1000; i++) { await delay(1000); yield `<li>Completed job #${i}</li>\n`; } return "</ol></body></html>"; });html(first)/raw(value):安全的 HTML 模板标签。html会自动转义插值;raw将一段字符串标记为"已信任、已预转义",绕过转义直接输出。切勿把用户输入传入raw,否则重新引入 XSS 风险。示例:html${raw(heading)} ${userName}`。noContent(status):返回空负载响应。writeEarlyHints(event, hints):写出HTTP/1.1 103 Early Hints;在不原生支持早提示的运行时,回退为设置可供 CDN 使用的响应头。
重定向
redirect(location, status, statusText?):设置location头并默认返回302;响应体会附带一个 meta refresh 页面以兜底忽略响应头的老旧客户端。若location来自用户输入,必须对照白名单校验,否则构成开放重定向漏洞。redirectBack(event, { fallback, allowQuery? }):基于referer头返回上一页。默认只取 referer 的pathname(剥离查询串与 hash),allowQuery: true可保留查询串;referer 缺失或跨源时回退到fallback(默认"/")。fallback必须是可信的硬编码路径,严禁使用用户输入。
生命周期清理
onDispose(event, cb):注册一个在事件彻底结束(响应体流式传输完成、客户端断开或响应体出错)时执行的回调,所有运行时均可用,回调在全局onResponse钩子之后按注册顺序执行。需注意它表示"h3 处理完该事件",而非"客户端已收到响应";若要在仍在产出响应时响应客户端断开(如中止上游 fetch),应使用event.req.signal。
app.get("/sse", (event) => { const interval = setInterval(() => {}, 1000); onDispose(event, () => clearInterval(interval)); // ... 返回流式响应 });Cookie:读取、写入与块状 Cookie
对应文档 docs/2.utils/3.cookie.md,实现位于 src/utils/cookie.ts,覆盖常规 Cookie 与自动分块的 chunked Cookie 两组 API:
- 常规组:
getCookie(event, name)、parseCookies(event)(解析整个Cookie头为键值对象)、setCookie(event, name, value, options?)、deleteCookie(event, name, serializeOptions?)、getValidatedCookies(event, validate, { onError? })(校验 cookie 值,兼容 Standard-Schema)。 - 块状组:
setChunkedCookie(event, name, value, options?)(按需自动分块)、getChunkedCookie(event, name)(读取并按序拼接各块)、deleteChunkedCookie(event, name, serializeOptions?)。这一组用于绕过浏览器对单个 Cookie 的大小限制,例如存储较大的会话数据。
Security:认证、会话、CORS 与路径安全
对应文档 docs/2.utils/4.security.md,这是 H3 工具集中安全密度最高的一类。
认证(Basic Auth)
basicAuth(opts):创建 Basic 认证中间件,认证结果写入event.context.basicAuth(含username)。requireBasicAuth(event, opts):对当前请求就地应用 Basic 认证,失败时抛错。
import { H3, serve, basicAuth } from "h3"; const auth = basicAuth({ password: "test" }); app.get("/", (event) => `Hello ${event.context.basicAuth?.username}!`, [auth]);会话
useSession(event, config):创建会话管理器;getSession/updateSession(event, config, update?)/clearSession分别读取、更新(可传差量更新函数)与清空会话数据;sealSession/unsealSession负责会话数据的加密签名与解密验签。实现位于 src/utils/session.ts,基于 src/utils/internal/iron-crypto.ts 的加密原语。
指纹
getRequestFingerprint(event, opts):为请求生成唯一指纹(src/utils/fingerprint.ts),可用于限流、防滥用等场景。
CORS
handleCors(event, options):一站式处理 CORS。若是预检请求,自动附加预检头并返回204;返回值非false即表示请求已被处理,无需后续操作。appendCorsHeaders/appendCorsPreflightHeaders:分别向响应追加普通与预检 CORS 头。isCorsOriginAllowed(origin, options)/isPreflightRequest(event):判定 origin 是否放行、判断是否预检请求。
app.all("/", async (event) => { const corsRes = handleCors(event, { origin: "*", preflight: { statusCode: 204 }, methods: "*", }); if (corsRes !== false) { return corsRes; } // 你的业务代码 });路径安全(路径穿越防御的核心)
resolveDotSegments(path, opts?):解析路径中的./..段,且永远不会逃逸到根/之上,结果恒为单一前导/的绝对路径(杜绝//host协议相对形态)。它还会在任意%25嵌套深度解码百分号编码的点段(%2e、%252e...),并把\规范化为/,因此编码或反斜杠式的穿越(%2e%2e/、..\..\)与字面../一样会被捕获。%2f/%5c(编码的路径分隔符)默认保持原样;尾部./..按目录处理并保留结尾斜杠(RFC 3986 §5.2.4),内部空段保留(/a//b不变),仅前导空段钳制为单个/。isCanonicalPath(path, opts?):判断路径是否已处于规范形态(即resolveDotSegments会原样返回它)。这是解析器自身的快速路径守卫,导出它便于在热路径(每次请求的 scope 检查或规则匹配)上跳过规范化调用——文档强调,scope 检查中漏掉一次规范化是绕过漏洞而非性能问题。
文档针对路由参数给出了明确警示:getRouterParams(event, { decode: true })只解码一层,返回值请勿再次解码,二次decodeURIComponent会把%2e%2e/x还原为../x、把%00还原为 NUL 字节;若参数将用作文件系统或上游路径,请用resolveDotSegments解析而非继续解码。
Proxy:内部子请求与外部代理
对应文档 docs/2.utils/5.proxy.md,实现位于 src/utils/proxy.ts,提供三个层层递进的代理工具:
fetchWithEvent(event, url, init?):携带事件上下文发起 fetch。内部 URL(以/开头)通过event.app.fetch()派发为子请求,永不出进程,继承入站请求的过滤后头(经getProxyRequestHeaders)与运行时元数据(ip、waitUntil等);外部 URL则用原生fetch(url, init)原样发送——事件的头与上下文不会被继承(向任意主机转发 Cookie/Authorization 是不安全的),且流式init.body会自动补上 Node fetch 要求的duplex: "half"。proxyRequest(event, target, opts):把入站请求代理到目标。请求体流式透传、不缓冲,因此此前若读过 body(readBody()、readFormData()或读体的中间件)会锁死流导致代理失败——需要"先检查再代理"时,请从event.req.clone()读取,保持原请求流完好。入站的Cookie与Authorization头会被原样转发(对同信任的反向代理是正确行为,但对不完全信任的上游请用filterHeaders: ["cookie", "authorization"]剥离)。上游 3xx 默认透传而非跟随,可设fetchOptions: { redirect: "follow" }跟随,但流式请求体在跟随重定向时可能因 body 无法重放而失败。proxy(event, target, opts):代理请求并把响应回传给客户端。与proxyRequest的关键差异:它默认不转发入站头,只发送调用方通过opts.headers显式传递的头(opts.filterHeaders对它无效,仅proxyRequest使用)。getProxyRequestHeaders(event):取出已剔除"已知会在代理时引发问题"的头后的请求头对象。
三个函数共同的安全红线:永远不要直接把未经净化的用户输入当作url/target,调用方必须负责校验与限制目标(主机白名单、拦截内部路径、强制协议);以/开头的内部 target 会绕过任何外部安全层(反向代理认证、IP 白名单、mTLS),务必谨慎。
MCP:JSON-RPC 与 WebSocket 双向通道
对应文档 docs/2.utils/6.mcp.md,实现位于 src/utils/json-rpc.ts。这一模块让 H3 应用可以直接承担 MCP(Model Context Protocol)等 JSON-RPC 服务端职责。
defineJsonRpcHandler(methods):创建实现 JSON-RPC 2.0 规范的 H3 事件处理器。内置安全默认值:要求请求为 JSONContent-Type(防 CSRF)、拒绝跨源请求(防 CSRF 与 DNS rebinding)、批处理请求上限 50 条(防扇出放大攻击)。
app.post( "/rpc", defineJsonRpcHandler({ methods: { echo: ({ params }, event) => `Received \`${params}\` on path \`${event.url.pathname}\``, sum: ({ params }, event) => params.a + params.b, }, }), );defineJsonRpcWebSocketHandler(methods, hooks?):实现 JSON-RPC 2.0 over WebSocket,每条入站文本消息作为 JSON-RPC 请求处理并回发响应,支持open/close等钩子。安全提示:与 HTTP 版本不同,它不校验请求Origin——WebSocket 升级不受 CORS 约束,任意源页面都能携带访客 Cookie 建立连接(跨站 WebSocket 劫持),请在upgrade钩子中校验Origin,必要时抛出Response中止连接。
More:基础能力与适配层
对应文档 docs/2.utils/9.more.md,包含若干高频的基础工具:
withBase(base, input):返回一个新的事件处理器,调用原处理器前先剥离 base 前缀,适合把子应用挂载到某一路径下。
const api = new H3().get("/", () => "Hello API!"); const app = new H3().use("/api/**", withBase("/api", api.handler));- 事件工具:
isEvent(input)(判断是否为 H3Event 对象)、isHTTPEvent(input)(判断是否为{ req: Request }形态的对象)、getEventContext(event)(获取/初始化事件上下文,存放于req.context)、mockEvent(_request, options?)(构造模拟事件,便于测试)。 - 中间件工具:
bodyLimit(limit)(限流中间件,基于assertBodySize的"随读随限"语义,超限在读体时以413呈现,未被读取的 body 不计数)、onError(hook)(错误钩子,可返回新 Response 优雅兜底)、onRequest(hook)/onResponse(hook)(请求前/响应后钩子,后者可返回新 Response 替换原响应)。 - WebSocket 工具:
defineWebSocket(hooks)定义 hooks(open/message/close等);defineWebSocketHandler(http?)定义 WebSocket 事件处理器——非升级(普通 HTTP)请求默认返回426 Upgrade Required,传入http处理器可让同一路由同时服务 WebSocket 升级与普通 HTTP;需要自定义升级握手本身请用 crossws 的upgrade钩子。 - 适配器:
defineNodeHandler(handler)、defineNodeMiddleware(handler)、fromNodeHandler(handler)、fromWebHandler(handler),用于与 Node.js 生态互操作,全部在 src/adapters.ts 中实现并统一从 src/index.ts 导出。
Community:社区生态工具
对应文档 docs/2.utils/99.community.md。H3 的开放性吸引了丰富的社区工具,目前收录了多个与 H3 v2 兼容的库:
- Apitally:带 H3 插件的 API 监控、分析与请求日志工具;
- H3ravel Framework:基于 H3 构建、向 JavaScript 生态移植 Laravel 开发体验的 TypeScript 运行时无关框架;
- Intlify srvmid:国际化相关的服务端框架、中间件与工具集;
- Clear Router:面向 H3 与 Express.js 的 Laravel 风格路由系统;
- unjwt:基于 Web Crypto API、零运行时依赖的底层 JWT 工具(JWS/JWE/JWK),含 H3 v2 专用的头与 Cookie 会话管理适配器;
- Arkstack:以 H3 为一等公民的运行时无关 TypeScript 后端框架。
文档同时欢迎社区提交新的兼容库(PR 即可收录)。
如何开始使用 H3 Utils
所有工具函数均可直接从包入口导入,例如:
import { readBody, getQuery, setCookie, handleCors, proxyRequest, defineJsonRpcHandler, } from "h3";从源码结构看,src/index.ts 的 "Utils" 段是完整的工具清单,src/utils/ 目录下每个文件对应一个功能域,且每个工具函数都带有完整的 JSDoc 与示例。你可以在 examples 目录找到大量可运行示例(如 cookies.mjs、cors.mjs、query-params.mjs、server-sent-events.mjs、websocket.mjs 等),在 test 目录找到对应的测试用例(如 cookies.test.ts、proxy.test.ts、security.test.ts、json-rpc.test.ts)作为行为基准。
结语
H3 Utils 是"小而精核心"哲学的完整体现:请求解析、响应构造、Cookie、安全、代理、JSON-RPC 等能力都以独立工具函数的形式按需供给,且处处内置安全默认值——从 body 的 opt-in 解析、代理的凭据转发策略,到路径穿越的编码防御与 Host 头的信任边界,每个决策都能在源码与文档中找到明确依据。掌握这套工具集,你就能用最少的代码、最强的安全基线,构建跨运行时的高性能 HTTP 应用。
【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考