Appearance
中间件
¥Middleware
中间件在 Handler 之后/之前工作。我们可以在调度之前获取 Request
,或者在调度之后操作 Response
。
¥Middleware works after/before Handler. We can get Request
before dispatching or manipulate Response
after dispatching.
中间件定义
¥Definition of Middleware
处理程序 - 应该返回
Response
对象。只会调用一个处理程序。¥Handler - should return
Response
object. Only one handler will be called.中间件 - 应该不返回任何内容,将继续使用
await next()
进行下一个中间件¥Middleware - should return nothing, will be proceeded to next middleware with
await next()
用户可以使用 app.use
或使用 app.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 stopping the 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
内置中间件
¥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',
})
)
警告
在 Deno 中,可以使用与 Hono 版本不同的中间件版本,但这可能会导致错误。例如,由于版本不同,此代码无法正常工作。
¥In Deno, it is possible to use a different version of middleware than the Hono version, but this can lead to bugs. For example, this code is not working because the version is different.
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.
为了确保我们不会丢失 context
和 next
的类型定义,在分离中间件时,我们可以使用 Hono 工厂中的 createMiddleware()
。这也允许我们从下游处理程序安全地键入 访问我们在 Context
中的 set
的数据。
¥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()
})
信息
类型泛型可以与 createMiddleware
一起使用:
¥Type generics can be used with 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
提供的 context 参数即可。有关说明,请参阅下面的示例。
¥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!'))
})
第三方中间件
¥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 Server Middleware、Sentry Middleware、Firebase Auth Middleware 等。
¥For example, we have GraphQL Server Middleware, Sentry Middleware, Firebase Auth Middleware, and others.