环境变量管理:c.env 如何从 Deno/Workers 获取配置?
Deno.env.get()、process.env、binding 的兼容层
在 Hono 中,c.env 是访问环境变量和平台绑定资源(如 KV、D1、R2)的统一接口。它的强大之处在于:无论你运行在 Node.js、Deno 还是 Cloudflare Workers,c.env 都提供一致的 API。这背后是 Hono 对不同运行时环境变量机制的抽象与兼容。
一、不同运行时的环境变量机制
| 运行时 | 环境变量读取方式 | 平台绑定资源 |
|---|---|---|
| Node.js | process.env.MY_VAR | 无(需通过库集成) |
| Deno | Deno.env.get("MY_VAR") | Deno.kv, Deno.openKv() 等 |
| Cloudflare Workers | 不支持 process.env | 通过 binding(如 env.MY_KV) |
Hono 的 c.env 统一了这些差异,让你无需关心底层实现。
二、c.env 的工作原理:Hono 的“兼容层”
当你在 Hono 中使用 c.env.MY_VAR 或 c.env.MY_KV 时,Hono 会根据运行时自动从正确的来源读取:
app.get('/config', (c) => {
const apiKey = c.env.API_KEY // 环境变量
const db = c.env.DB // D1 数据库(Workers)
const kv = c.env.MY_KV // KV Namespace(Workers)
const bucket = c.env.ASSETS // R2 Bucket
return c.json({ apiKey: 'masked' })
})Hono 内部如何映射?
c.env.X 查找顺序 | Node.js | Deno | Cloudflare Workers |
|---|---|---|---|
c.env.API_KEY | process.env.API_KEY | Deno.env.get("API_KEY") | env.API_KEY (binding) |
c.env.DB | env.DB (需手动注入) | env.DB (需手动注入) | env.DB (binding) |
关键点:c.env 是 Hono 将运行时原生环境变量和绑定资源注入到请求上下文的结果。
三、各平台具体实现
1. Cloudflare Workers(最典型)
在 Workers 中,env 是 fetch 事件的第二个参数:
// worker.ts
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// env 包含所有定义的 binding
const app = new Hono<{ Bindings: Env }>()
return app.fetch(request, env, ctx) // ← env 被传入,成为 c.env
}
}wrangler.toml 配置 binding:
[vars]
API_KEY = "your-secret-key"
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "my-assets"
[[d1_databases]]
binding = "DB"
database_name = "my_db"
database_id = "..."此时 c.env.API_KEY、c.env.ASSETS、c.env.DB 均可直接使用。
2. Deno
Deno 使用 Deno.env 和模块化权限:
// deno.ts
const app = new Hono()
app.get('/secret', (c) => {
const key = c.env.API_KEY // ← 映射到 Deno.env.get("API_KEY")
return c.text(key || 'not set')
})
// 手动启动服务,注入 env
Deno.serve({
port: 8080,
}, (req) => {
const env = {
API_KEY: Deno.env.get("API_KEY"), // 手动读取
DB:'' // ... 注入 Deno KV 或其他
}
return app.fetch(req, env)
})Hono 在 Deno 中通过 Deno.env.get() 读取环境变量,并依赖开发者将平台资源(如 Deno.kv)注入 env 对象。
3. Node.js
Node.js 使用 process.env:
// node.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/config', (c) => {
const port = c.env.PORT || '3000' // ← process.env.PORT
return c.text(`Running on port ${port}`)
})
// 启动时,env 默认为 process.env
app.fire() // 或使用 server.listen()你也可以手动注入:
app.fetch(request, {
...process.env,
DB: databaseClient,
REDIS: redisClient
})四、Hono 的“兼容层”是如何工作的?
Hono 在 app.fetch(request, env, ...) 调用时,会:
- 接收
env参数(来自运行时); - 创建
Context实例; - 将
env挂载到c.env; - 在
c.env上实现get拦截(如需要)以兼容不同行为。
这使得 c.env 成为一个运行时无关的统一接口。
五、最佳实践:类型安全的 c.env
定义 Bindings 类型,避免 any:
// types.ts
type Env = {
API_KEY: string
DB: D1Database
ASSETS: R2Bucket
MY_KV: KVNamespace
REDIS_URL: string
}
const app = new Hono<{ Bindings: Env }>()
app.get('/profile', async (c) => {
const user = await c.env.DB.prepare('SELECT * FROM users WHERE id = ?')
.bind(c.req.param('id'))
.first()
const cacheKey = `user:${user.id}`
await c.env.MY_KV.put(cacheKey, JSON.stringify(user), { expirationTtl: 3600 })
return c.json(user)
})c.env.DB类型为D1Database,有完整方法提示;c.env.MY_KV类型为KVNamespace,避免拼写错误。
六、总结
| 机制 | Node.js | Deno | Cloudflare Workers |
|---|---|---|---|
| 环境变量来源 | process.env | Deno.env.get() | env binding |
| 平台资源 | 手动注入 | 手动注入 | env binding |
c.env 行为 | 代理 process.env | 代理 Deno.env + 注入资源 | 直接使用 env |
c.env 是 Hono 的“环境抽象层”,它:
- 统一了不同平台的环境变量和绑定资源访问方式;
- 提供了类型安全的接口;
- 让你的代码可以在 Node.js、Deno、Workers 之间迁移而无需修改
c.env用法。
掌握 c.env 的工作机制,你就能更自信地构建跨平台、可移植的 Hono 应用。