教程 hono tutorial

Context 详解

Hono 的处理函数接收一个 Context 对象,通常命名为 c。它代表当前请求生命周期,可以读取请求、写响应、访问环境变量、共享临时数据。

返回文本、JSON 和 HTML

ts
app.get('/text', (c) => {
  return c.text('Hello')
})

app.get('/json', (c) => {
  return c.json({ message: 'Hello' })
})

app.get('/html', (c) => {
  return c.html('<h1>Hello</h1>')
})

c.json() 会设置合适的 Content-Type,比手写 Response 更省心。

设置状态码

可以把状态码作为第二个参数:

ts
app.post('/users', (c) => {
  return c.json({ id: 1 }, 201)
})

也可以先调用 c.status():

ts
app.delete('/users/:id', (c) => {
  c.status(204)
  return c.body(null)
})

设置响应头

ts
app.get('/download', (c) => {
  c.header('Content-Disposition', 'attachment; filename="report.txt"')
  return c.text('report content')
})

多个中间件都可以修改响应头,但要避免不同位置重复设置同一个头,尤其是缓存、CORS 和安全相关头。

读取请求体

JSON 请求体:

ts
app.post('/api/users', async (c) => {
  const body = await c.req.json<{ name: string }>()
  return c.json({ name: body.name })
})

表单数据:

ts
app.post('/contact', async (c) => {
  const form = await c.req.formData()
  const email = String(form.get('email') ?? '')
  return c.json({ email })
})

原始文本:

ts
app.post('/webhook', async (c) => {
  const raw = await c.req.text()
  return c.text(raw)
})

重定向

ts
app.get('/old-page', (c) => {
  return c.redirect('/new-page', 301)
})

如果是 API,不要用重定向表示业务错误。API 更适合返回 JSON 错误和明确状态码。

在请求中共享数据

中间件可以通过 c.set() 保存请求级数据,后续处理函数用 c.get() 读取:

ts
app.use(async (c, next) => {
  c.set('requestId', crypto.randomUUID())
  await next()
})

app.get('/trace', (c) => {
  return c.json({ requestId: c.get('requestId') })
})

如果你使用 TypeScript,可以在 Hono 泛型中声明 Variables,让 set() 和 get() 获得类型提示。

返回原始 Response

Hono 也允许直接返回标准 Response:

ts
app.get('/raw', () => {
  return new Response('Raw response', {
    status: 200,
    headers: {
      'Content-Type': 'text/plain; charset=utf-8',
    },
  })
})

普通业务接口优先使用 c.text()、c.json()、c.html(),只有在需要完全控制响应时再返回原始 Response。