Context

Context 对象在每次请求时都会被实例化,并在返回响应之前保持有效。你可以在其中存储值、设置响应头和状态码,还可以访问 HonoRequestResponse 对象。

req

reqHonoRequest 的实例。更多详情见 HonoRequest

  1. app.get('/hello', (c) => {
  2. const userAgent = c.req.header('User-Agent')
  3. // ...
  4. })

status()

使用 c.status() 设置 HTTP 状态码,默认是 200。如果状态码是 200,就不需要显式调用 c.status()

  1. app.post('/posts', (c) => {
  2. // 设置 HTTP 状态码
  3. c.status(201)
  4. return c.text('Your post is created!')
  5. })

header()

c.header() 为响应设置 HTTP Headers。

  1. app.get('/', (c) => {
  2. // 设置响应头
  3. c.header('X-Message', 'My custom message')
  4. return c.text('HellO!')
  5. })

body()

返回 HTTP 响应。

提示: 当返回文本或 HTML 时,推荐使用 c.text()c.html()

  1. app.get('/welcome', (c) => {
  2. c.header('Content-Type', 'text/plain')
  3. // 返回响应内容
  4. return c.body('Thank you for coming')
  5. })

也可以这样写:

  1. app.get('/welcome', (c) => {
  2. return c.body('Thank you for coming', 201, {
  3. 'X-Message': 'Hello!',
  4. 'Content-Type': 'text/plain',
  5. })
  6. })

等价于:

  1. new Response('Thank you for coming', {
  2. status: 201,
  3. headers: {
  4. 'X-Message': 'Hello!',
  5. 'Content-Type': 'text/plain',
  6. },
  7. })

text()

Content-Type:text/plain 渲染纯文本。

  1. app.get('/say', (c) => {
  2. return c.text('Hello!')
  3. })

json()

Content-Type:application/json 渲染 JSON。

  1. app.get('/api', (c) => {
  2. return c.json({ message: 'Hello!' })
  3. })

html()

Content-Type:text/html 渲染 HTML。

  1. app.get('/', (c) => {
  2. return c.html('<h1>Hello! Hono!</h1>')
  3. })

notFound()

返回一个 404 Not Found 响应。可以通过 app.notFound() 自定义。

  1. app.get('/notfound', (c) => {
  2. return c.notFound()
  3. })

redirect()

执行重定向,默认状态码是 302。

  1. app.get('/redirect', (c) => {
  2. return c.redirect('/')
  3. })
  4. app.get('/redirect-permanently', (c) => {
  5. return c.redirect('/', 301)
  6. })

res

可以访问即将返回的 Response 对象。

  1. // Response 对象
  2. app.use('/', async (c, next) => {
  3. await next()
  4. c.res.headers.append('X-Debug', 'Debug message')
  5. })

set() / get()

通过 c.setc.get 设置和获取任意的 key-value 对,生命周期仅限当前请求。这样可以在中间件与路由处理器之间传递值。

  1. app.use(async (c, next) => {
  2. c.set('message', 'Hono is cool!!')
  3. await next()
  4. })
  5. app.get('/', (c) => {
  6. const message = c.get('message')
  7. return c.text(`The message is "${message}"`)
  8. })

如果想让类型更安全,可以把 Variables 类型传入 Hono 构造函数的 Generics 中。

  1. type Variables = {
  2. message: string
  3. }
  4. const app = new Hono<{ Variables: Variables }>()

注意: c.set / c.get 的值只在同一请求内有效,不能跨请求共享或持久化。

var

你也可以用 c.var 访问变量。

  1. const result = c.var.client.oneMethod()

如果想在中间件中提供自定义方法,可以这样写:

  1. type Env = {
  2. Variables: {
  3. echo: (str: string) => string
  4. }
  5. }
  6. const app = new Hono()
  7. const echoMiddleware = createMiddleware<Env>(async (c, next) => {
  8. c.set('echo', (str) => str)
  9. await next()
  10. })
  11. app.get('/echo', echoMiddleware, (c) => {
  12. return c.text(c.var.echo('Hello!'))
  13. })

如果要在多个路由中复用该中间件,可以用 app.use(),并将 Env 类型传入 Hono 构造函数:

  1. const app = new Hono<Env>()
  2. app.use(echoMiddleware)
  3. app.get('/echo', (c) => {
  4. return c.text(c.var.echo('Hello!'))
  5. })

render() / setRenderer()

可以通过 c.setRenderer() 设置一个渲染布局。

  1. app.use(async (c, next) => {
  2. c.setRenderer((content) => {
  3. return c.html(
  4. <html>
  5. <body>
  6. <p>{content}</p>
  7. </body>
  8. </html>
  9. )
  10. })
  11. await next()
  12. })

然后使用 c.render() 在该布局内渲染内容:

  1. app.get('/', (c) => {
  2. return c.render('Hello!')
  3. })

输出结果为:

  1. <html>
  2. <body>
  3. <p>Hello!</p>
  4. </body>
  5. </html>

你还可以自定义参数类型,例如:

  1. declare module 'hono' {
  2. interface ContextRenderer {
  3. (
  4. content: string | Promise<string>,
  5. head: { title: string }
  6. ): Response | Promise<Response>
  7. }
  8. }

使用示例:

  1. app.use('/pages/*', async (c, next) => {
  2. c.setRenderer((content, head) => {
  3. return c.html(
  4. <html>
  5. <head>
  6. <title>{head.title}</title>
  7. </head>
  8. <body>
  9. <header>{head.title}</header>
  10. <p>{content}</p>
  11. </body>
  12. </html>
  13. )
  14. })
  15. await next()
  16. })
  17. app.get('/pages/my-favorite', (c) => {
  18. return c.render(<p>Ramen and Sushi</p>, {
  19. title: 'My favorite',
  20. })
  21. })
  22. app.get('/pages/my-hobbies', (c) => {
  23. return c.render(<p>Watching baseball</p>, {
  24. title: 'My hobbies',
  25. })
  26. })

executionCtx

可访问 Cloudflare Workers 特有的 ExecutionContext

  1. app.get('/foo', async (c) => {
  2. c.executionCtx.waitUntil(c.env.KV.put(key, data))
  3. // ...
  4. })

event

可访问 Cloudflare Workers 的 FetchEvent(早期 Service Worker 语法),但不再推荐。

  1. type Bindings = {
  2. MY_KV: KVNamespace
  3. }
  4. const app = new Hono<{ Bindings: Bindings }>()
  5. // FetchEvent 对象(仅在 Service Worker 语法时可用)
  6. app.get('/foo', async (c) => {
  7. c.event.waitUntil(c.env.MY_KV.put(key, data))
  8. // ...
  9. })

env

Cloudflare Workers 的环境变量、密钥、KV、D1 数据库、R2 存储桶等被称为 bindings,可以通过 c.env.BINDING_KEY 访问。

  1. type Bindings = {
  2. MY_KV: KVNamespace
  3. }
  4. const app = new Hono<{ Bindings: Bindings }>()
  5. app.get('/', async (c) => {
  6. c.env.MY_KV.get('my-key')
  7. // ...
  8. })

error

如果处理器抛出错误,错误对象会被放入 c.error,可以在中间件中访问。

  1. app.use(async (c, next) => {
  2. await next()
  3. if (c.error) {
  4. // 处理错误
  5. }
  6. })

ContextVariableMap

如果你想在中间件中扩展变量的类型定义,可以扩展 ContextVariableMap

  1. declare module 'hono' {
  2. interface ContextVariableMap {
  3. result: string
  4. }
  5. }

然后在中间件中使用:

  1. const mw = createMiddleware(async (c, next) => {
  2. c.set('result', 'some values') // result 是 string
  3. await next()
  4. })

在处理器中,类型也会自动推断:

  1. app.get('/', (c) => {
  2. const val = c.get('result') // val 是 string
  3. return c.json({ result: val })
  4. })