事件处理器
在创建一个 h3 应用实例 后,你可以开始使用事件处理器来定义你的应用逻辑。
事件处理器是一个接收 H3Event 上下文并返回响应的函数。
定义事件处理器
你可以使用 defineEventHandler
来定义类型化的事件处理器。
defineEventHandler
是可选的。当使用 app 方法 注册路由或中间件时,类型提示依然可用。import { defineEventHandler } from "h3";
defineEventHandler((event) => "Response");
回调函数可以是同步或异步的:
defineEventHandler(async (event) => "Response");
对象语法
你可以在 defineEventHandler
中使用对象语法以获得更灵活的选项。
defineEventHandler({
onRequest: [],
onBeforeResponse: [],
handler: () => "Response",
})
响应类型
事件处理器返回的值会被自动转换为 HTTP 响应。
如果事件处理器返回一个 Promise 或者是一个 异步函数,h3 会等待其解析完成后再发送响应。
null
或 undefined
在 路由处理器 中发送一个空响应体,在 中间件处理器 中则会进入下一个处理器。
return
语句,这等同于 return undefined
。字符串
返回一个字符串值会作为纯文本体发送。
content-type
,默认为 text/plain;charset=UTF-8
。示例: 发送 HTML 响应
app.get("/", (event) => {
event.res.headers.set("Content-Type", "text/html;charset=UTF-8");
return "<h1>hello world</h1>";
});
可 JSON 序列化的值
返回一个 JSON 可序列化的值(对象、数组、数字 或 布尔值)时,会使用 JSON.stringify() 进行字符串化,并以默认的 application/json
类型发送。
示例:
app.get("/", (event) => ({ hello: "world" }));
toJSON()
方法来自定义序列化行为。更多信息请查看 MDN 文档。Response
发送标准 Web Response。
示例:
app.get("/", (event) => Response.json({ hello: "world" }));
Response
时,以前通过 event.res.headers
设置的任何头部将作为默认头部合并。event.res.{status/statusText}
会被忽略。为了性能考虑,最好仅在最终
Response
里设置头部。ReadableStream
或 Readable
发送标准 Web ReadableStream 或 Node.js Readable。
ArrayBuffer
、Uint8Array
或 Buffer
发送二进制的 ArrayBuffer、Uint8Array 或 Node.js 的 Buffer。
content-length
头部会自动设置。
Error
返回一个 Error 实例将发送该错误。
建议 throw
错误而非返回,并且使用 createError
工具。
BigInt
该值会作为字符串化后的 BigInt 数字发送。
toJSON
。详情请见 MDN 文档。Blob
以流的形式发送标准 Web Blob。
Content-Type
和 Content-Length
头部会自动设置。
Symbol
或 Function
返回 Symbol 或 Function 的行为未确定。当前版本会发送未知的 Symbol 和 Function 的字符串化表示,但未来可能改为抛出错误或从不返回。
h3 内部使用一些已知 Symbol(未来可能变更):
Symbol.for("h3.notFound")
:表示未找到路由以抛出 404 错误。Symbol.for("h3.handled")
:表示请求已被处理,h3 不应继续(仅 Node.js)。
错误处理
你可以使用 createError
工具轻松控制返回的错误。
import { createError } from "h3";
app.get("/error", (event) => {
throw createError({
status: 400,
statusMessage: "Bad Request",
message: "Invalid user input",
data: { field: "email" },
});
});
这会以 400 - Bad Request
状态码结束请求,并返回以下 JSON 响应:
{
"statusCode": 400,
"statusMessage": "Bad Request",
"stack": [],
"data": {
"field": "email"
}
}
字符串 vs 对象错误
使用 createError
创建错误时,你也可以传递一个字符串而非对象,这将设置错误的 message
属性,statusCode
默认是 500
。
import { createError } from "h3";
app.get("/error", (event) => {
throw createError("An error occurred");
});
message
包含错误的简短、人类可读的描述,而 statusMessage
特指 HTTP 响应中与状态码相关的状态文本。在客户端-服务器环境中,推荐使用简短的
statusMessage
,因为它能被客户端访问。否则,服务器端传入 createError
的 message
不会传播给客户端(可以使用 data
)。最好避免将动态用户输入直接放入 message
以避免潜在安全风险。内部错误
如果调用事件处理器时抛出了一个 new Error()
(未使用 createError
),h3 会自动捕获并作为 500 - Internal Server Error
状态响应,视为未处理错误。
app.get("/error", (event) => {
// 不推荐这样用,请使用 createError()!
throw new Error("Something went wrong");
});
延迟处理器
你可以使用 defineLazyEventHandler
定义延迟事件处理器。它允许你定义一些只会在首次接收到匹配路由的请求时执行一次的逻辑。
延迟事件处理器必须返回一个事件处理器:
import { defineLazyEventHandler } from "h3";
app.get(
"/lazy",
defineLazyEventHandler(() => {
console.log("This will be executed only once");
// 这里的代码只会执行一次
return (event) => {
// 这里的代码会在每次请求时执行
return "Response";
};
}),
);
这适合定义一些一次性逻辑,如配置、类初始化、复杂计算等。
中间件处理器
不返回任何值的事件处理器会作为中间件使用。它们可用来为应用添加副作用,比如日志记录、缓存等,或修改请求和响应。
中间件处理器应通过 app.use()
注册。不返回值的普通路由事件处理器(如 app.get()
)会导致 404 错误。
若从中间件处理器返回值,则其行为等同于普通事件处理器,并将其作为响应发送。
示例: 简单的请求日志中间件:
app.use((event) => {
console.log(`[${event.req.method}] ${event.req.url}`);
// 不返回值
});
你可以定义任意多的中间件,它们会按注册顺序调用。
转换为 H3 处理器
某些情况下,你可能想把为 Node.js 或其他框架写的事件处理器或工具转换成 h3 可用。
h3 内置了相关工具!
从 Web 处理器转换
使用 Request => Response 语法的请求处理器可以原生转换为 h3 事件处理器:
import { H3, fromWebHandler } from "h3";
export const app = new H3();
const webHandler = (request) => new Response("👋 Hello!");
// 使用 fromWebHandler 工具
app.get("/web", fromWebHandler(webHandler));
// 使用简单包装器
app.get("/web", event => webHandler(event.req));
从 Node.js 处理器转换
如果你有一个用 (req, res) => {}
语法写的 Node.js 旧请求处理器,可以用 fromNodeHandler
转换成 h3 事件处理器。
import { H3, fromNodeHandler } from "h3";
export const app = new H3();
const nodeHandler = (req, res) => {
res.end("Node handlers work!");
};
app.get("/web", fromNodeHandler(nodeHandler));