Guide

事件处理器

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

在创建一个 应用实例 后,您可以开始使用事件处理器定义应用程序逻辑。

事件处理器是一个接收 Event 实例并返回响应的函数。您可以将其与其他框架中的控制器进行比较。

定义事件处理器

您可以使用 defineEventHandler 定义类型化的事件处理器:

import { defineEventHandler } from "h3";

defineEventHandler(() => "响应");

回调函数可以是同步的或异步的:

defineEventHandler(async () => "响应");

对象语法

您可以在 defineEventHandler 中使用对象语法,以获得更灵活的选项。

defineEventHandler({
  onRequest: [],
  onBeforeResponse: [],
  handler: () => "响应",
})

响应类型

从事件处理器返回的值会自动转换为响应。它可以是:

  • 可序列化的 JSON 值。如果返回一个 JSON 对象或可序列化的值,将会被字符串化并以默认的 application/json 内容类型发送。
  • string:按原样发送,使用默认的 text/html 内容类型。
  • null:h3 将以 204 - 无内容 状态码结束响应。
  • Web ReadableStreamnode Readable
  • Web ArrayBuffernode Buffer
  • Web Fetch 响应
  • Error 实例。支持这种方式,但 推荐 使用 createError 工具抛出错误而不是返回它们。

上述任意值也可以包装在一个 Promise 中。这意味着您可以从事件处理器返回一个 Promise,而 h3 会在发送响应之前等待它解析。

示例: 发送 HTML 响应:

app.use(defineEventHandler(async () => "<h1>你好,世界!</h1>"));

示例: 发送 JSON 响应:

app.use(
  "/api",
  defineEventHandler(async (event) => event.path),
);

示例: 发送一个 promise:

app.use(
  defineEventHandler(async (event) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(event.path);
      }, 1000);
    });
  }),
);

错误处理

您可以使用 createError 工具轻松控制返回的错误。

import { createError, defineEventHandler } from "h3";

app.use(
  "/validate",
  defineEventHandler((event) => {
    throw createError({
      status: 400,
      statusMessage: "错误请求",
      message: "无效的用户输入",
      data: { field: "电子邮件" },
    });
  }),
);

这会以 400 - 错误请求 状态码结束请求,并返回以下 JSON 响应:

{
  "statusCode": 400,
  "statusMessage": "错误请求",
  "stack": [],
  "data": {
    "field": "电子邮件"
  }
}

字符串与对象错误

使用 createError 创建错误时,您也可以选择传递一个字符串而不是对象。这样会设置错误的 message 属性。在这种情况下,statusCode 将默认设置为 500

import { createError, defineEventHandler } from "h3";

app.use(
  "/hello",
  defineEventHandler((event) => {
    throw createError("发生了错误");
  }),
);
通常,message 包含错误的简短、可读性强的描述,而 statusMessage 特指 HTTP 响应,并描述与响应状态码相关的状态文本。 在客户端-服务器的上下文中,建议使用简短的 statusMessage,因为它可以在客户端访问。否则,传递给服务器上的 createErrormessage 将不会传播到客户端(您可以改用 data)。考虑避免将动态用户输入放入消息中,以避免潜在的安全问题。

内部错误

如果在调用事件处理器时抛出一个 new Error() 的错误(未使用 createError),h3 会自动捕获并视为 500 - 内部服务器错误 状态响应,认为这是一个未处理的错误。

app.use(
  "/hello",
  defineEventHandler((event) => {
    // 不要这样做,请使用 createError()!
    throw new Error("出了些问题");
  }),
);

懒惰的事件处理器

您可以使用 defineLazyEventHandlerlazyEventHandler 工具定义懒惰的事件处理器。这允许您定义一些仅在接收到第一个匹配路由的请求时执行的一次性逻辑。

懒惰的事件处理器必须返回一个事件处理器:

import { defineLazyEventHandler } from "h3";

app.use(
  defineLazyEventHandler(() => {
    console.log("这将只执行一次");
    // 这将只执行一次
    return defineEventHandler((event) => {
      // 这将在每个请求中执行
      return "响应";
    });
  }),
);

这对于定义一些一次性逻辑,如配置、类初始化、繁重计算等非常有用。

中间件

不返回任何值的事件处理器充当中间件。它们可以用于为您的应用程序添加副作用,例如日志记录、缓存等,或修改请求或响应。

中间件模式 不推荐 在 h3 中使用。副作用可能会影响全局应用程序性能,并使追踪逻辑变得更加困难。 相反,使用 h3 组合和对象语法钩子。

与普通事件处理器类似,您可以使用 defineEventHandlereventHandler 工具定义中间件:

defineEventHandler((event) => {
  console.log(`中间件。路径: ${event.path}`);
});
中间件 不得 返回任何值或直接为 event 返回响应。 如果您返回响应,它将充当普通事件处理器!

注册中间件

然后,您可以使用 use 方法将中间件注册到 应用实例

app.use(
  defineEventHandler((event) => {
    console.log("中间件 1");
  }),
);
app.use(
  defineEventHandler((event) => {
    console.log("中间件 2");
  }),
);
app.use(defineEventHandler(() => "响应"));

您可以根据需要定义任意数量的中间件。它们将按照注册顺序被调用。

转换为 h3 处理器

在某些情况下,您可能希望将为 Node.js 或其他框架制作的事件处理器或工具转换为 h3。 有内置工具可以实现这一点。!

从 Node.js 处理器转换

如果您有一个带有 (req, res) => {} 语法的遗留请求处理器,为 Node.js 制作,您可以使用 fromNodeHandler 将其转换为 h3 事件处理器。

app.mjs
import { createApp, fromNodeHandler } from "h3";

import exampleMiddleware from "example-node-middleware";

export const app = createApp();

app.use(fromNodeHandler(exampleMiddleware()));
例如,这将帮助您将 Vite 中间件模式 与 h3 应用程序一起使用。

从 Web 处理器转换

您可以使用 fromWebHandler 工具将类似 fetch 的函数(具有 Request => Response 语法)转换为 h3 事件处理器。

app.mjs
import { webHandler } from "web-handler"; // 该包不存在,仅为示例
import { createApp, fromWebHandler } from "h3";

export const app = createApp();

app.use(fromWebHandler(webHandler));