Skip to content

中间件

🌐 Middleware

中间件在端点 Handler 之前/之后工作。我们可以在派发之前获取 Request,或在派发之后操作 Response

🌐 Middleware works before/after the endpoint Handler. We can get the Request before dispatching or manipulate the Response after dispatching.

中间件定义

🌐 Definition of Middleware

  • 处理器 - 应该返回 Response 对象。只会调用一个处理器。
  • 中间件 - 应该 await next() 并且不返回任何内容以调用下一个中间件,或者 返回一个 Response 以提前退出。

用户可以使用 app.useapp.HTTP_METHOD 注册中间件以及处理程序。对于此功能,指定路径和方法很容易。

🌐 The user can register middleware using app.use or using app.HTTP_METHOD as well as the handlers. For this feature, it's easy to specify the path and the method.

ts
// match any method, all routes
app.use(logger())

// specify path
app.use('/posts/*', cors())

// specify method and path
app.post('/posts/*', basicAuth())

如果处理程序返回 Response,它将被用于终端用户,并且会停止处理。

🌐 If the handler returns Response, it will be used for the end-user and will stop processing.

ts
app.post('/posts', (c) => c.text('Created!', 201))

在这种情况下,在调度之前会处理四个中间件,如下所示:

🌐 In this case, four middleware are processed before dispatching like this:

ts
logger() -> cors() -> basicAuth() -> *handler*

执行顺序

🌐 Execution order

中间件的执行顺序由其注册顺序决定。 首个注册的中间件在 next 之前的过程最先执行, 而 next 之后的过程最后执行。 见下文。

🌐 The order in which Middleware is executed is determined by the order in which it is registered. The process before the next of the first registered Middleware is executed first, and the process after the next is executed last. See below.

ts
app.use(async (_, next) => {
  console.log('middleware 1 start')
  await next()
  console.log('middleware 1 end')
})
app.use(async (_, next) => {
  console.log('middleware 2 start')
  await next()
  console.log('middleware 2 end')
})
app.use(async (_, next) => {
  console.log('middleware 3 start')
  await next()
  console.log('middleware 3 end')
})

app.get('/', (c) => {
  console.log('handler')
  return c.text('Hello!')
})

结果如下。

🌐 Result is the following.

middleware 1 start
  middleware 2 start
    middleware 3 start
      handler
    middleware 3 end
  middleware 2 end
middleware 1 end

请注意,如果处理程序或任何中间件抛出异常,hono 会捕获它,并要么将其传递给 你的 app.onError() 回调,要么在返回到中间件链之前自动将其转换为 500 响应。这意味着 next() 永远不会抛出异常,因此无需将其封装在 try/catch/finally 中。

🌐 Note that if the handler or any middleware throws, hono will catch it and either pass it to your app.onError() callback or automatically convert it to a 500 response before returning it up the chain of middleware. This means that next() will never throw, so there is no need to wrap it in a try/catch/finally.

内置中间件

🌐 Built-in Middleware

Hono 有内置中间件。

🌐 Hono has built-in middleware.

ts
import { Hono } from 'hono'
import { poweredBy } from 'hono/powered-by'
import { logger } from 'hono/logger'
import { basicAuth } from 'hono/basic-auth'

const app = new Hono()

app.use(poweredBy())
app.use(logger())

app.use(
  '/auth/*',
  basicAuth({
    username: 'hono',
    password: 'acoolproject',
  })
)

WARNING

在 Deno 中,可以使用与 Hono 版本不同的中间件版本,但这可能会导致错误。 例如,这段代码无法运行,因为版本不同。

ts
import { Hono } from 'jsr:@hono/hono@4.4.0'
import { upgradeWebSocket } from 'jsr:@hono/hono@4.4.5/deno'

const app = new Hono()

app.get(
  '/ws',
  upgradeWebSocket(() => ({
    // ...
  }))
)

自定义中间件

🌐 Custom Middleware

你可以直接在 app.use() 中编写你自己的中间件:

🌐 You can write your own middleware directly inside app.use():

ts
// Custom logger
app.use(async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next()
})

// Add a custom header
app.use('/message/*', async (c, next) => {
  await next()
  c.header('x-message', 'This is middleware!')
})

app.get('/message/hello', (c) => c.text('Hello Middleware!'))

然而,将中间件直接嵌入到 app.use() 中可能会限制其可重用性。因此,我们可以将我们的中间件拆分到不同的文件中。

🌐 However, embedding middleware directly within app.use() can limit its reusability. Therefore, we can separate our middleware into different files.

为了确保在分离中间件时我们不会丢失 contextnext 的类型定义,我们可以使用 Hono 的工厂中的 createMiddleware()。这也允许我们从下游处理程序中类型安全地访问我们在 Contextset 的数据

🌐 To ensure we don't lose type definitions for context and next, when separating middleware, we can use createMiddleware() from Hono's factory. This also allows us to type-safely access data we've set in Context from downstream handlers.

ts
import { createMiddleware } from 'hono/factory'

const logger = createMiddleware(async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next()
})

INFO

类型泛型可以与 createMiddleware 一起使用:

ts
createMiddleware<{Bindings: Bindings}>(async (c, next) =>

下一步后修改响应

🌐 Modify the Response After Next

此外,中间件可以设计为在必要时修改响应:

🌐 Additionally, middleware can be designed to modify responses if necessary:

ts
const stripRes = createMiddleware(async (c, next) => {
  await next()
  c.res = undefined
  c.res = new Response('New Response')
})

中间件参数内的上下文访问

🌐 Context access inside Middleware arguments

要访问中间件参数中的上下文,直接使用 app.use 提供的上下文参数。详见下面的示例说明。

🌐 To access the context inside middleware arguments, directly use the context parameter provided by app.use. See the example below for clarification.

ts
import { cors } from 'hono/cors'

app.use('*', async (c, next) => {
  const middleware = cors({
    origin: c.env.CORS_ORIGIN,
  })
  return middleware(c, next)
})

扩展中间件中的上下文

🌐 Extending the Context in Middleware

要在中间件中扩展上下文,请使用 c.set。你可以通过将 { Variables: { yourVariable: YourVariableType } } 泛型参数传递给 createMiddleware 函数来使其类型安全。

🌐 To extend the context inside middleware, use c.set. You can make this type-safe by passing a { Variables: { yourVariable: YourVariableType } } generic argument to the createMiddleware function.

ts
import { createMiddleware } from 'hono/factory'

const echoMiddleware = createMiddleware<{
  Variables: {
    echo: (str: string) => string
  }
}>(async (c, next) => {
  c.set('echo', (str) => str)
  await next()
})

app.get('/echo', echoMiddleware, (c) => {
  return c.text(c.var.echo('Hello!'))
})

链式中间件的类型推断

🌐 Type Inference Across Chained Middleware

当你使用 .use() 链接多个中间件时,Hono 会自动累积 Variables 类型。跟随中间件链的路由处理程序可以以类型安全的方式访问每个前置中间件的所有变量:

🌐 When you chain multiple middleware using .use(), Hono automatically accumulates the Variables types. Route handlers that follow the middleware chain can access all variables from every preceding middleware in a type-safe way:

ts
import { createMiddleware } from 'hono/factory'

const authMiddleware = createMiddleware<{
  Variables: { user: { id: string; name: string } }
}>(async (c, next) => {
  c.set('user', { id: '123', name: 'Alice' })
  await next()
})

const dbMiddleware = createMiddleware<{
  Variables: { db: { query: (sql: string) => Promise<unknown> } }
}>(async (c, next) => {
  c.set('db', {
    query: async (sql) => {
      /* ... */
    },
  })
  await next()
})

const app = new Hono()
  .use(authMiddleware)
  .use(dbMiddleware)
  .get('/', (c) => {
    // Both `user` and `db` are available and type-safe
    const user = c.var.user // { id: string; name: string }
    const db = c.var.db // { query: (sql: string) => Promise<unknown> }
    return c.json({ user })
  })

这是可行的,因为每次 .use() 调用都会返回一个带有合并类型的新 Hono 实例,所以在链式使用中类型会逐渐增长。这消除了在大多数用例中需要手动预先声明组合 Env 类型的必要性。

🌐 This works because each .use() call returns a new Hono instance with the merged type, so the type grows as middleware is chained. This eliminates the need to manually declare a combined Env type upfront for most use cases.

第三方中间件

🌐 Third-party Middleware

内置中间件不依赖外部模块,但第三方中间件可以依赖第三方库。因此,使用它们,我们可以构建一个更复杂的应用。

🌐 Built-in middleware does not depend on external modules, but third-party middleware can depend on third-party libraries. So with them, we may make a more complex application.

我们可以探索各种第三方中间件。例如,我们有 GraphQL 服务器中间件、Sentry 中间件、Firebase 认证中间件等。

🌐 We can explore a variety of third-party middleware. For example, we have GraphQL Server Middleware, Sentry Middleware, Firebase Auth Middleware, and others.

Hono 中文网 - 粤ICP备13048890号