绑定(binding)允许 Worker 与 Cloudflare Developer Platform 上的资源交互。与面向非 Workers 应用程序的 REST API 相比,从 Workers 访问资源时,绑定提供更好的性能和更少的限制。
在本地开发期间,绑定默认连接到本地模拟资源。您也可以配置它们使用远程绑定(remote bindings) 连接到真实的生产资源。
以下绑定目前可用:
- AI
- Analytics Engine
- Assets
- Browser Run
- D1
- Durable Objects
- Hyperdrive
- Images
- KV
- Media Transformations
- mTLS
- Queues
- R2
- Secrets
- Secrets Store
- Stream
- Vectorize
- Workflows
- 分派器(Workers for Platforms)
- 动态 Worker 加载器
- 服务绑定
- 版本元数据
- 环境变量
- 速率限制
在 Worker 上声明绑定时,您授予它特定能力,例如能够读写 R2 存储桶中的文件。例如:
{
"main": "./src/index.js",
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "<MY_BUCKET_NAME>"
}
]
}main = "./src/index.js"
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "<MY_BUCKET_NAME>"export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.pathname.slice(1);
await env.MY_BUCKET.put(key, request.body);
return new Response(`Put ${key} successfully!`);
},
};from workers import WorkerEntrypoint, Response
from urllib.parse import urlparse
class Default(WorkerEntrypoint):
async def fetch(self, request):
url = urlparse(request.url)
key = url.path.slice(1)
await self.env.MY_BUCKET.put(key, request.body)
return Response(f"Put {key} successfully!")您可以将绑定视为权限和 API 的结合体。使用绑定时,您永远不必向 Worker 添加密钥或 token 来访问 Cloudflare 账户上的资源——权限嵌入在 API 本身中。底层 secret 永远不会暴露给 Worker 的代码,因此不会意外泄露。
部署 Worker 更改且仅更改其绑定(即不更改 Worker 代码)时,Cloudflare 可能会复用已在运行 Worker 的现有 isolate。这提高了性能——您可以更改环境变量或其他绑定,而无需不必要地重新加载代码。
因此,在全局作用域中"污染"绑定的派生对象时必须小心。尽管您更改了底层绑定,在那里创建的任何内容可能仍会继续存在。考虑一个使用从 env 访问的 secret API 密钥的外部客户端实例:如果将此客户端实例放在全局作用域中然后更改 secret,使用原始值的客户端实例可能仍会继续存在。正确的方法是每个请求创建一个新的客户端实例。
以下是良好的做法:
export default {
fetch(request, env) {
let client = new Client(env.MY_SECRET); // `client` is guaranteed to be up-to-date with the latest value of `env.MY_SECRET` since a new instance is constructed with every incoming request
// ... do things with `client`
},
};与此替代方案相比,后者可能产生意外和不希望的行为:
let client = undefined;
export default {
fetch(request, env) {
client ??= new Client(env.MY_SECRET); // `client` here might not be updated when `env.MY_SECRET` changes, since it may already exist in global scope
// ... do things with `client`
},
};如果您有更高级的需求,请探索 AsyncLocalStorage API,它提供一种机制,将值向下暴露给子执行处理程序。
绑定位于 env 对象上,可以通过多种方式访问:
-
它是入口点处理程序(如
fetch)的参数:export default { async fetch(request, env) { return new Response(`Hi, ${env.NAME}`); }, };
-
它是 WorkerEntrypoint、DurableObject 和 Workflow 上的类属性:
export class MyDurableObject extends DurableObject { async sayHello() { return `Hi, ${this.env.NAME}!`; } }from workers import WorkerEntrypoint, Response class Default(WorkerEntrypoint): async def fetch(self, request): return Response(f"Hi {self.env.NAME}") -
它可以从
cloudflare:workers导入:import { env } from "cloudflare:workers"; console.log(`Hi, ${env.Name}`);from workers import env print(f"Hi, {env.NAME}")
从 cloudflare:workers 导入 env 在需要在顶层全局作用域访问secrets 或环境变量 等绑定时很有用。例如,初始化 API 客户端:
import { env } from "cloudflare:workers";
import ApiClient from "example-api-client";
// API_KEY and LOG_LEVEL now usable in top-level scope
let apiClient = ApiClient.new({ apiKey: env.API_KEY });
const LOG_LEVEL = env.LOG_LEVEL || "info";
export default {
fetch(req) {
// you can use apiClient or LOG_LEVEL, configured before any request is handled
},
};from workers import WorkerEntrypoint, env
from example_api_client import ApiClient
api_client = ApiClient(api_key=env.API_KEY)
LOG_LEVEL = getattr(env, "LOG_LEVEL", "info")
class Default(WorkerEntrypoint):
async def fetch(self, request):
# ...Workers 不允许在请求上下文之外进行 I/O。这意味着即使 env 可从顶层作用域访问,您也无法访问每个绑定的方法。
例如,环境变量和 secrets 可访问,您可以在顶层上下文中调用 env.NAMESPACE.get 获取 Durable Object stub。但是,在 Durable Object stub 上调用方法、调用 KV 存储以及调用其他 Workers 将不起作用。
import { env } from "cloudflare:workers";
// This would error!
// env.KV.get('my-key')
export default {
async fetch(req) {
// This works
let myVal = await env.KV.get("my-key");
Response.new(myVal);
},
};from workers import Response, WorkerEntrypoint, env
# This would fail!
# env.KV.get('my-key')
class Default(WorkerEntrypoint):
async def fetch(self, request):
# This works
mv_val = await env.KV.get("my-key")
return Response(my_val)此外,从 cloudflare:workers 导入 env 可让您避免在多个函数调用中传递 env 作为参数,如果您需要从深层嵌套函数访问绑定。这在复杂代码库中很有帮助。
import { env } from "cloudflare:workers";
export default {
fetch(req) {
Response.new(sayHello());
},
};
// env is not an argument to sayHello...
function sayHello() {
let myName = getName();
return `Hello, ${myName}`;
}
// ...nor is it an argument to getName
function getName() {
return env.MY_NAME;
}from workers import Response, WorkerEntrypoint, env
class Default(WorkerEntrypoint):
def fetch(req):
return Response(say_hello())
# env is not an argument to say_hello...
def say_hello():
my_name = get_name()
return f"Hello, {myName}"
# ...nor is it an argument to getName
def get_name():
return env.MY_NAMEwithEnv 函数提供一种覆盖 env 值的机制。
假设用户在 Wrangler 配置文件中将环境变量 "NAME" 定义为 "Alice" 并部署了 Worker。默认情况下,记录 env.NAME 会打印 "Alice"。使用 withEnv 函数,您可以覆盖 "NAME" 的值。
import { env, withEnv } from "cloudflare:workers";
function logName() {
console.log(env.NAME);
}
export default {
fetch(req) {
// this will log "Alice"
logName();
withEnv({ NAME: "Bob" }, () => {
// this will log "Bob"
logName();
});
// ...etc...
},
};from workers import Response, WorkerEntrypoint, env, patch_env
def log_name():
print(env.NAME)
class Default(WorkerEntrypoint):
async def fetch(req):
# this will log "Alice"
log_name()
with patch_env(NAME="Bob"):
# this will log "Bob"
log_name()
# ...etc...这在测试依赖导入的 env 对象的代码时很有用。