跳至內容

中介層

中介層在處理器 (Handler) 之後/之前工作。我們可以在分發前獲取 Request,或者在分發後操作 Response

中介層的定義

  • 處理器 (Handler) - 應該返回 Response 物件。只會呼叫一個處理器。
  • 中介層 - 應該不返回任何值,將透過 await next() 繼續下一個中介層。

使用者可以使用 app.useapp.HTTP_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,它將被用於最終使用者,並停止處理程序。

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

在這種情況下,會像這樣在分發之前處理四個中介層

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

執行順序

中介層的執行順序由其註冊順序決定。 首先執行第一個註冊的中介層的 next 之前的處理程序,最後執行 next 之後的處理程序。 請參閱下方。

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!')
})

結果如下。

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

內建中介層

Hono 具有內建的中介層。

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 版本不同的中介層版本,但這可能會導致錯誤。 例如,此程式碼無法運作,因為版本不同。

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(() => ({
    // ...
  }))
)

自訂中介層

您可以直接在 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() 中嵌入中介層可能會限制其可重複使用性。 因此,我們可以將中介層分離到不同的檔案中。

為確保我們不會遺失 contextnext 的類型定義,當分離中介層時,我們可以使用 Hono 工廠的 createMiddleware()

ts
import { createMiddleware } from 'hono/factory'

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

資訊

類型泛型可用於 createMiddleware

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

在 Next 之後修改 Response

此外,如果需要,可以設計中介層來修改回應

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

中介層引數中的上下文存取

要存取中介層引數內的上下文,請直接使用 app.use 提供的上下文參數。 請參閱以下範例以釐清。

ts
import { cors } from 'hono/cors'

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

在中介層中擴展上下文

要在中介層中擴展上下文,請使用 c.set。 您可以透過將 { Variables: { yourVariable: YourVariableType } } 泛型引數傳遞給 createMiddleware 函式,使其具有類型安全。

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!'))
})

第三方中介層

內建中介層不依賴外部模組,但第三方中介層可能會依賴第三方函式庫。 因此,有了它們,我們可以建立更複雜的應用程式。

例如,我們有 GraphQL 伺服器中介層、Sentry 中介層、Firebase Auth 中介層等等。

依 MIT 授權發佈。