Guide

事件处理器

事件处理器定义应用逻辑。

在创建一个 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 会等待其解析完成后再发送响应。

nullundefined

路由处理器 中发送一个空响应体,在 中间件处理器 中则会进入下一个处理器。

如果事件处理器中没有 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 里设置头部。

ReadableStreamReadable

发送标准 Web ReadableStream 或 Node.js Readable

ArrayBufferUint8ArrayBuffer

发送二进制的 ArrayBufferUint8Array 或 Node.js 的 Buffer

content-length 头部会自动设置。

Error

返回一个 Error 实例将发送该错误。

建议 throw 错误而非返回,并且使用 createError 工具。

BigInt

该值会作为字符串化后的 BigInt 数字发送。

返回 JSON 对象时不支持 BigInt 序列化。你需要实现 toJSON。详情请见 MDN 文档

Blob

以流的形式发送标准 Web Blob

Content-TypeContent-Length 头部会自动设置。

SymbolFunction

返回 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,因为它能被客户端访问。否则,服务器端传入 createErrormessage 不会传播给客户端(可以使用 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}`);
  // 不返回值
});

你可以定义任意多的中间件,它们会按注册顺序调用。

Read more in Guide > Routing#adding Middleware.

转换为 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 事件处理器。

Node.js 处理器只能在 Node.js 服务器运行时执行!
import { H3, fromNodeHandler } from "h3";

export const app = new H3();

const nodeHandler = (req, res) => {
  res.end("Node handlers work!");
};

app.get("/web", fromNodeHandler(nodeHandler));