Context 对象(c)
每个处理函数都接收一个 Context 对象 c,它是 Hono 的核心接口,封装了当前请求的所有信息和响应构建方法:
// Context 的主要属性概览
app.get('/demo', async (c) => {
// 请求相关
c.req // HonoRequest 对象
c.req.raw // 原始 Web API Request 对象
c.req.method // "GET" | "POST" | ...
c.req.url // 完整 URL 字符串
c.req.path // 路径部分: "/demo"
// 环境相关(Cloudflare Workers 的 env/ctx 在这里)
c.env // 绑定的环境变量
c.executionCtx // 执行上下文(Workers 的 ctx)
// 自定义变量(通过中间件传递数据)
c.set('userId', '42')
c.get('userId') // → '42'
return c.json({ ok: true })
})
请求信息提取
路径参数、查询参数、Header
app.get('/users/:id', async (c) => {
// 路径参数
const id = c.req.param('id')
// 查询参数
const format = c.req.query('format') ?? 'json'
// 请求头
const auth = c.req.header('Authorization')
const contentType = c.req.header('Content-Type')
return c.json({ id, format, auth })
})
解析请求体
// JSON body
app.post('/json', async (c) => {
const body = await c.req.json<{ name: string }>()
return c.json({ received: body.name })
})
// 文本 body
app.post('/text', async (c) => {
const text = await c.req.text()
return c.text(`Received: ${text}`)
})
// ArrayBuffer(用于二进制数据)
app.post('/binary', async (c) => {
const buffer = await c.req.arrayBuffer()
return c.json({ bytes: buffer.byteLength })
})
// FormData(表单提交)
app.post('/form', async (c) => {
const form = await c.req.formData()
const username = form.get('username') as string
return c.json({ username })
})
响应构建
// JSON 响应(最常用)
c.json({ key: 'value' }) // 200
c.json({ error: 'Not found' }, 404) // 自定义状态码
c.json(data, 201, { 'X-Custom': 'header' }) // 带额外 Header
// 纯文本
c.text('Hello World')
c.text('Created', 201)
// HTML
c.html('<h1>Hello</h1>')
c.html(`<!DOCTYPE html><html><body>${content}</body></html>`)
// 重定向
c.redirect('/login') // 302
c.redirect('/new-url', 301) // 永久重定向
// 无内容响应
c.body(null, 204) // 204 No Content
// 设置响应 Header(在返回响应前调用)
c.header('X-Powered-By', 'Hono')
c.header('Cache-Control', 'max-age=3600')
return c.json(data)
Cookie 操作
import { getCookie, setCookie, deleteCookie } from 'hono/cookie'
// 读取 Cookie
app.get('/profile', (c) => {
const sessionId = getCookie(c, 'session_id')
if (!sessionId) {
return c.redirect('/login')
}
return c.json({ session: sessionId })
})
// 设置 Cookie
app.post('/login', async (c) => {
// ... 验证逻辑
setCookie(c, 'session_id', 'abc123', {
httpOnly: true, // 防止 JS 读取
secure: true, // 仅 HTTPS
sameSite: 'Lax', // CSRF 防护
maxAge: 60 * 60 * 24, // 1天,单位秒
path: '/',
})
return c.json({ ok: true })
})
// 删除 Cookie(登出)
app.post('/logout', (c) => {
deleteCookie(c, 'session_id')
return c.redirect('/')
})
流式响应
基础流式传输
import { stream } from 'hono/streaming'
app.get('/stream', (c) => {
return stream(c, async (s) => {
for (let i = 0; i < 5; i++) {
await s.write(`Chunk ${i}\n`)
await s.sleep(200) // 200ms 间隔
}
})
})
SSE(Server-Sent Events)
SSE 是一种服务器向客户端单向推送数据的协议,适合实时通知、AI 流式输出等场景:
import { streamSSE } from 'hono/streaming'
app.get('/sse/notifications', (c) => {
return streamSSE(c, async (stream) => {
let id = 0
// 发送 SSE 事件
await stream.writeSSE({
data: JSON.stringify({ type: 'connected' }),
event: 'connect',
id: String(id++),
})
// 定期推送数据
while (true) {
if (stream.closed) break // 客户端断开时退出
await stream.writeSSE({
data: JSON.stringify({
time: new Date().toISOString(),
value: Math.random(),
}),
event: 'update',
id: String(id++),
})
await stream.sleep(1000)
}
})
})
// 前端 EventSource 接收 SSE
const es = new EventSource('/sse/notifications')
es.addEventListener('connect', (e) => {
console.log('Connected:', JSON.parse(e.data))
})
es.addEventListener('update', (e) => {
const data = JSON.parse(e.data)
console.log('Update:', data)
})
SSE vs WebSocket:SSE 是单向的(服务器→客户端),基于 HTTP,更简单且天然支持重连。WebSocket 是双向的,需要升级协议。对于 AI 流式输出、实时通知等单向数据流场景,SSE 是更合适的选择。
文件上传处理
app.post('/upload', async (c) => {
const formData = await c.req.formData()
// 获取文件
const file = formData.get('file') as File | null
if (!file || !(file instanceof File)) {
return c.json({ error: 'No file provided' }, 400)
}
// 读取文件内容
const arrayBuffer = await file.arrayBuffer()
const bytes = new Uint8Array(arrayBuffer)
// 验证文件类型
if (!file.type.startsWith('image/')) {
return c.json({ error: 'Only images allowed' }, 415)
}
// 验证文件大小(最大 5MB)
if (file.size > 5 * 1024 * 1024) {
return c.json({ error: 'File too large' }, 413)
}
// 在 Cloudflare Workers 中可上传到 R2
// await c.env.MY_BUCKET.put(file.name, bytes)
return c.json({
name: file.name,
size: file.size,
type: file.type,
})
})
Raw Response
当 Hono 内置方法不够用时,可以直接返回原生 Response 对象:
// 返回图片(二进制数据)
app.get('/image', async (c) => {
const imageData = await fetch('https://example.com/img.png')
const buffer = await imageData.arrayBuffer()
return new Response(buffer, {
status: 200,
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=86400',
},
})
})
// 返回 CSV 文件下载
app.get('/export.csv', (c) => {
const csv = `id,name,email\n1,Alice,alice@example.com`
return new Response(csv, {
headers: {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': 'attachment; filename="export.csv"',
},
})
})
c.var 共享变量:在中间件中通过 c.set('key', value) 设置的数据,可以在后续中间件和处理函数中通过 c.get('key') 或 c.var.key 读取。这是中间件向处理函数传递数据(如当前用户信息)的标准方式。