事件处理程序 Event Handler

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

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

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

定义事件处理程序

您可以使用 defineEventHandlereventHandler 实用程序定义事件处理程序:

您可以互换使用 defineEventHandlereventHandler。它们是别名。您可以使用您喜欢的那个,但坚持使用以保持一致性。

js
import { defineEventHandler } from 'h3'

defineEventHandler((event) => {
  return 'Response'
})

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

js
defineEventHandler(async (event) => {
  return 'Response'
})

对象语法

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

js
defineEventHandler({
  onRequest: [],
  onBeforeResponse: [],
  handler: (event) => {
    return 'Response'
  },
})

响应类型

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

  • JSON 可序列化值。如果返回 JSON 对象或可序列化值,它将被字符串化并使用默认的 application/json 内容类型发送。
  • string:使用默认的 text/html 内容类型按原样发送。
  • null:h3,结束响应带有 204 - 无内容 状态代码。
  • Web ReadableStreamnode Readable
  • Web ArrayBuffernode Buffer
  • Web Fetch Response
  • Error 实例。支持,但建议抛出错误,而不是使用 createError 实用程序返回错误。

上述任何值都可以包装在 Promise 中。这意味着您可以从事件处理程序返回 Promise,h3 将等待它解析后再发送响应。

示例: 发送 HTML 响应:

js
app.use(defineEventHandler(async event => '<h1>Hello world!</h1>'))

示例: 发送 JSON 响应:

js
app.use(
  '/api',
  defineEventHandler(async event => ({ url: event.node.req.url })),
)

示例: Send a promise:

js
app.use(
  defineEventHandler(async (event) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve({ url: event.node.req.url })
      }, 1000)
    })
  }),
)

错误处理

您可以使用 createError 实用程序轻松控制返回的错误。

js
import { createError, defineEventHandler } from 'h3'

app.use(
  '/validate',
  defineEventHandler((event) => {
    throw createError({
      status: 400,
      statusMessage: 'Bad Request',
      message: 'Invalid user input',
      data: { field: 'email' }
    })
  }),
)

这将以400 - Bad Request状态代码和以下 JSON 响应结束请求:

json
{
  "status": 400,
  "message": "An error occurred"
}

字符串与对象错误

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

js
import { createError, defineEventHandler } from 'h3'

app.use(
  '/hello',
  defineEventHandler((event) => {
    throw createError('An error occurred')
  }),
)

!TIP 通常,“message”包含简短、人性化的错误描述,而“statusMessage”特定于 HTTP 响应,并描述与响应状态代码相关的状态文本。 在客户端-服务器上下文中,建议使用简短的“statusMessage”,因为它可以在客户端访问。否则,传递给服务器上“createError”的“message”将不会传播到客户端(您可以改用“data”)。考虑避免将动态用户输入放入消息中,以避免潜在的安全问题。

内部错误

如果在调用事件处理程序期间抛出带有new Error()的错误(没有createError),h3 将自动将其捕获为 “500 - 内部服务器错误” 状态响应,认为这是一个未处理的错误。

js
app.use(
  '/hello',
  defineEventHandler((event) => {
    // 不要这样做并使用 createError()!
    throw new Error('Something went wrong')
  }),
)

惰性事件处理程序

您可以使用 defineLazyEventHandlerlazyEventHandler 实用程序定义惰性事件处理程序。这允许您定义一些一次性逻辑,这些逻辑仅在收到与路由匹配的第一个请求时执行一次。

惰性事件处理程序必须返回一个事件处理程序:

js
import { defineLazyEventHandler } from 'h3'

app.use(
  defineLazyEventHandler(() => {
    console.log('This will be executed only once')
    // 这将仅执行一次
    return defineEventHandler((event) => {
      // 这将在每次请求时执行
      return 'Response'
    })
  }),
)

这对于定义一些一次性逻辑(如配置、类初始化、大量计算等)很有用。

中间件

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

!TIP 中间件模式不推荐用于 h3。副作用会影响全局应用程序性能并使跟踪逻辑更加困难。 而是使用 h3 可组合项和对象语法钩子。

与普通事件处理程序类似,您可以使用 defineEventHandlereventHandler 实用程序定义中间件:

js
defineEventHandler((event) => {
  console.log(`Middleware. Path: ${event.path}`)
})

!IMPORTANT 中间件不得返回任何值或直接返回 event 的响应。 如果您返回响应,它将充当普通事件处理程序!

注册中间件

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

js
app.use(
  defineEventHandler((event) => {
    console.log('Middleware 1')
  }),
)
app.use(
  defineEventHandler((event) => {
    console.log('Middleware 2')
  }),
)
app.use(
  defineEventHandler((event) => {
    return 'Response'
  }),
)

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

转换为 h3 处理程序

在某些情况下,您可能需要将为 Node.js 或其他框架制作的事件处理程序或实用程序转换为 h3。 有内置实用程序可以执行此操作。!

从 Node.js 处理程序转换

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

app.mjs
js
import { createApp, fromNodeMiddleware } from 'h3'

import exampleMiddleware from 'example-node-middleware'

export const app = createApp()

app.use(fromNodeListener(exampleMiddleware()))

!TIP 例如,这将帮助您将 Vite 中间件模式 与 h3 应用结合使用。

从 Web 处理程序转换

您可以使用 fromWebHandler 实用程序将类似 fetch 的函数(带有 Request => Response 签名)转换为 h3 事件处理程序。

app.mjs
js
import { webHandler } from 'web-handler' // 该包不存在,它只是一个例子
import { createApp, fromWebHandler } from 'h3'

export const app = createApp()

app.use(fromWebHandler(webHandler))