上下文
每个请求都会创建一个 Context 对象,并一直保留到响应返回。你可以在其中存储值,设置希望返回的响应头和状态码,还可以访问 HonoRequest 与 Response 对象。
req
req 是 HonoRequest 的实例。详情请参阅 HonoRequest。
app.get('/hello', (c) => {
const userAgent = c.req.header('User-Agent')
// ...
})status()
可以通过 c.status() 设置 HTTP 状态码。默认值为 200。如果状态码是 200,就不必调用 c.status()。
app.post('/posts', (c) => {
// Set HTTP status code
c.status(201)
return c.text('Your post is created!')
})header()
可以为响应设置 HTTP 响应头。
app.get('/', (c) => {
// Set headers
c.header('X-Message', 'My custom message')
return c.text('Hello!')
})body()
返回 HTTP 响应。
说明
注意:返回文本或 HTML 时,建议使用 c.text() 或 c.html()。
app.get('/welcome', (c) => {
c.header('Content-Type', 'text/plain')
// Return the response body
return c.body('Thank you for coming')
})也可以采用以下写法。
app.get('/welcome', (c) => {
return c.body('Thank you for coming', 201, {
'X-Message': 'Hello!',
'Content-Type': 'text/plain',
})
})响应与下面的 Response 对象相同。
new Response('Thank you for coming', {
status: 201,
headers: {
'X-Message': 'Hello!',
'Content-Type': 'text/plain',
},
})text()
以 Content-Type: text/plain 返回文本。
app.get('/say', (c) => {
return c.text('Hello!')
})json()
以 Content-Type: application/json 返回 JSON。
app.get('/api', (c) => {
return c.json({ message: 'Hello!' })
})html()
以 Content-Type: text/html 返回 HTML。
app.get('/', (c) => {
return c.html('<h1>Hello! Hono!</h1>')
})notFound()
返回 Not Found 响应。可以通过 app.notFound() 自定义该响应。
app.get('/notfound', (c) => {
return c.notFound()
})redirect()
进行重定向,默认状态码为 302。
app.get('/redirect', (c) => {
return c.redirect('/')
})
app.get('/redirect-permanently', (c) => {
return c.redirect('/', 301)
})res
可以访问即将返回的 Response 对象。
// Response object
app.use('/', async (c, next) => {
await next()
c.res.headers.append('X-Debug', 'Debug message')
})set() / get()
获取和设置任意键值对,其生命周期为当前请求。这样可以在中间件之间,或从中间件向路由处理程序传递特定值。
app.use(async (c, next) => {
c.set('message', 'Hono is cool!!')
await next()
})
app.get('/', (c) => {
const message = c.get('message')
return c.text(`The message is "${message}"`)
})将 Variables 作为泛型传入 Hono 的构造函数,就可以确保类型安全。
type Variables = {
message: string
}
const app = new Hono<{ Variables: Variables }>()c.set / c.get 中的值只在同一次请求内保留,不能在不同请求之间共享或持久化。
var
也可以通过 c.var 访问变量的值。
const result = c.var.client.oneMethod()如果想创建提供自定义方法的中间件, 可以采用以下写法:
type Env = {
Variables: {
echo: (str: string) => string
}
}
const app = new Hono()
const echoMiddleware = createMiddleware<Env>(async (c, next) => {
c.set('echo', (str) => str)
await next()
})
app.get('/echo', echoMiddleware, (c) => {
return c.text(c.var.echo('Hello!'))
})如果想在多个处理程序中使用该中间件,可以调用 app.use()。 此时,需要将 Env 作为泛型传入 Hono 的构造函数,以确保类型安全。
const app = new Hono<Env>()
app.use(echoMiddleware)
app.get('/echo', (c) => {
return c.text(c.var.echo('Hello!'))
})render() / setRenderer()
可以在自定义中间件中使用 c.setRenderer() 设置布局。
app.use(async (c, next) => {
c.setRenderer((content) => {
return c.html(
<html>
<body>
<p>{content}</p>
</body>
</html>
)
})
await next()
})随后就可以使用 c.render() 在该布局中创建响应。
app.get('/', (c) => {
return c.render('Hello!')
})输出结果如下:
<html>
<body>
<p>Hello!</p>
</body>
</html>此外,这个功能还允许灵活地自定义参数。 为了确保类型安全,可以这样定义类型:
declare module 'hono' {
interface ContextRenderer {
(
content: string | Promise<string>,
head: { title: string }
): Response | Promise<Response>
}
}以下是使用示例:
app.use('/pages/*', async (c, next) => {
c.setRenderer((content, head) => {
return c.html(
<html>
<head>
<title>{head.title}</title>
</head>
<body>
<header>{head.title}</header>
<p>{content}</p>
</body>
</html>
)
})
await next()
})
app.get('/pages/my-favorite', (c) => {
return c.render(<p>Ramen and Sushi</p>, {
title: 'My favorite',
})
})
app.get('/pages/my-hobbies', (c) => {
return c.render(<p>Watching baseball</p>, {
title: 'My hobbies',
})
})executionCtx
可以访问 Cloudflare Workers 特有的 ExecutionContext。
// ExecutionContext object
app.get('/foo', async (c) => {
c.executionCtx.waitUntil(c.env.KV.put(key, data))
// ...
})ExecutionContext 还有一个 exports 字段。要配合 Wrangler 生成的类型获得自动补全,可以使用模块扩展:
import 'hono'
declare module 'hono' {
interface ExecutionContext {
readonly exports: Cloudflare.Exports
}
}event
可以访问 Cloudflare Workers 特有的 FetchEvent。它用于“Service Worker”语法,但现在已不推荐使用。
// Type definition to make type inference
type Bindings = {
MY_KV: KVNamespace
}
const app = new Hono<{ Bindings: Bindings }>()
// FetchEvent object (only set when using Service Worker syntax)
app.get('/foo', async (c) => {
c.event.waitUntil(c.env.MY_KV.put(key, data))
// ...
})env
在 Cloudflare Workers 中,绑定到 Worker 的环境变量、密钥、KV 命名空间、D1 数据库、R2 存储桶等统称为绑定(bindings)。 无论类型如何,绑定始终作为全局变量可用,也可以通过上下文中的 c.env.BINDING_KEY 访问。
// Type definition to make type inference
type Bindings = {
MY_KV: KVNamespace
}
const app = new Hono<{ Bindings: Bindings }>()
// Environment object for Cloudflare Workers
app.get('/', async (c) => {
c.env.MY_KV.get('my-key')
// ...
})error
如果处理程序抛出错误,错误对象会存放在 c.error 中。 可以在中间件中访问它。
app.use(async (c, next) => {
await next()
if (c.error) {
// do something...
}
})ContextVariableMap
注意
ContextVariableMap 会向所有上下文全局添加类型,无论设置该变量的中间件是否实际执行过。这意味着,即使某个处理程序从未注册你的中间件,c.get('result') 也会看起来是类型安全的,从而可能掩盖运行时的 undefined 错误。
请看以下示例:
declare module 'hono' {
interface ContextVariableMap {
result: string
}
}
const mw = createMiddleware(async (c, next) => {
c.set('result', 'some values')
await next()
})
const app = new Hono()
// handler uses the middleware
app.get('/foo', mw, (c) => {
const val = c.get('result') // ✅ val is a string and typed as such, as expected
})
// handler doesn't use the middleware
app.get('/bar', (c) => {
const val = c.get('result') // ❌ val is undefined but typed as a string, which can lead to runtime errors
})可以扩展 ContextVariableMap 接口,为整个应用中的上下文变量全局定义类型。这适用于由应用全局中间件设置,且能保证在上下文中存在的变量。
例如:
declare module 'hono' {
interface ContextVariableMap {
result: string
}
}然后可以在中间件中使用它:
const mw = createMiddleware(async (c, next) => {
c.set('result', 'some values') // result is a string
await next()
})在处理程序中,变量会被推断为正确的类型:
app.get('/', (c) => {
const val = c.get('result') // val is a string
// ...
return c.json({ result: val })
})