响应
事件流
EventStream()
isEventStream(input)
清理
sanitizeStatusCode(statusCode?, defaultStatusCode)
确保状态码是有效的 HTTP 状态码。
sanitizeStatusMessage(statusMessage)
确保状态消息安全可用于响应。
允许的字符:水平制表符、空格或可见 ASCII 字符:https://www.rfc-editor.org/rfc/rfc7230#section-3.1.2
静态资源服务
serveStatic(event, options)
根据请求路径动态提供静态资源。
安全性 — 路径遍历:serveStatic 会解析 ./.. 段并规范化请求路径,但会有意将编码后的分隔符以百分号编码形式保留在传递给 getMeta/getContents 的 id 中:%2f(编码后的 /)始终会保留,而双重编码的反斜杠会作为字面量 %5c 到达(单重编码的 %5c 会被解码为 \ 并被规范化移除)。因此,遍历安全性取决于这些后端不对 id 进行解码:对其进行百分号解码的后端(例如额外调用 decodeURIComponent,或会进行解码的查找层)会重新引入分隔符,从而再次打开遍历漏洞。请将 id 作为不透明字符串解析到资源根目录下。
在基于真实文件系统实现自定义 getMeta/getContents 时,集成方还需要负责两件 serveStatic 无法强制执行的事项。不区分大小写的文件系统(macOS、Windows):对任何允许/拒绝检查的两侧都进行大小写折叠——否则 /SECRET.env 可能绕过针对 /secret.env 编写的检查。**符号链接包含性:**跟随链接后,重新确认解析出的路径仍位于资源根目录内(例如将 realpath(target) 与根目录进行比较),因为符号链接可能指向根目录之外。
更多响应实用工具
html(first)
以 HTML 内容响应。
示例:
app.get("/", (event) => html(event, "<h1>Hello, World!</h1>"));
iterable(iterable)
遍历一个数据块源,按顺序发送每个数据块。支持异步操作与数据块的混合发送。
每个数据块必须是字符串或缓冲区。
对于生成器(yield)函数,返回值与 yield 的值处理方式相同。
第一个数据块会在创建响应之前等待完成,因此在生成该数据块期间暂存的状态和标头(event.res.status、event.res.headers)仍会生效。第一个数据块之后设置的所有内容都会被忽略——届时标头已经发送到线路上。(返回原始 ReadableStream 不存在这样的时间窗口:它的响应会在读取流之前创建。)
示例:
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 #`;
yield i;
yield `</li>\n`;
}
// 关闭报告
return `</ol></body></html>`;
});
async function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
noContent(status)
响应一个空负载。
示例:
app.get("/", () => noContent());
onDispose(event, cb)
注册一个回调,在事件完全结束后运行:响应正文完成流式传输、客户端断开连接或正文发生错误——适用于所有运行时环境,而不仅仅是 Node.js。
回调在正常完成时接收 undefined,否则接收取消/中止原因。回调按照注册顺序、在全局 onResponse 钩子之后运行;同步抛出和异步拒绝会被吸收(除非应用配置了 silent,否则会通过 console.error 报告),待处理的异步回调会传递给 waitUntil。
在事件销毁后注册会立即调用该回调。只有在请求处理期间(处理器、中间件或 onResponse 中)注册时,才能保证观察到事件的结束。
注意:这表示的是 “h3 已完成对该事件的处理”,而不是 “客户端已收到响应” ——对于非 Node.js 运行时中的非流式正文,它会在响应交给运行时后触发。若要在仍然生成响应期间对客户端断开连接作出反应(例如中止上游 fetch),请改用 event.req.signal。
示例:
app.get("/sse", (event) => {
const interval = setInterval(() => {}, 1000);
onDispose(event, () => clearInterval(interval));
// ... 返回一个流式响应
});
raw(value)
将字符串标记为可信的、已预转义的 HTML,使其被 {@link html} 工具使用时不会再次转义。
仅对完全由你控制的标记使用此方法——将用户输入传递给 raw 会重新引入 XSS 风险。
示例:
// `heading` 是可信的标记;`userName` 会自动转义。
app.get("/", () => html`<div>${raw(heading)}<span>${userName}</span></div>`);
示例:
// 原样发送可信的标记字符串:
app.get("/", () => html(raw("<h1>Hello, World!</h1>")));
redirect(location, status, statusText?)
向客户端发送重定向响应。
它会在响应中添加 location 头,默认状态码为 302。
响应体发送一个简单的 HTML 页面,包含 meta 刷新标签,以防客户端忽略响应头时进行重定向。
安全性: 如果 location 来源于用户输入(查询参数、表单字段、标头等),请在重定向前根据允许的目标列表对其进行验证。未经检查地传递用户控制的值会导致开放重定向漏洞。对于“返回上一页”的流程,建议使用 redirectBack,它只接受同源的 referer。
示例:
app.get("/", () => {
return redirect("https://example.com");
});
示例:
app.get("/", () => {
return redirect("https://example.com", 301); // 永久重定向
});
redirectBack(event)
使用 referer 头将客户端重定向回上一个页面。
如果 referer 头缺失或是不同的来源,则回退到提供的 URL(默认为 "/")。
默认情况下,只使用 referer 的 pathname(查询字符串和哈希被剥离)以防止伪造的 referer 携带意外参数。设置 allowQuery: true 以保留查询字符串。
安全性: fallback 值必须是一个可信的硬编码路径——绝不要使用用户输入。将用户控制的值(例如查询参数)作为 fallback 会导致开放重定向漏洞。
示例:
app.post("/submit", (event) => {
// 处理表单...
return redirectBack(event, { fallback: "/form" });
});
writeEarlyHints(event, hints)
向客户端写入 HTTP/1.1 103 Early Hints。
在本身不支持早期提示的运行时环境中,此函数会退回到设置响应头,这些响应头可以被 CDN 使用。