跳转到内容
本站为社区维护的非官方中文翻译,与 Cloudflare, Inc. 无关联。本站不提供账户登录,请勿在任何非官方站点输入您的 Cloudflare 账户信息。官方文档:developers.cloudflare.com

自定义路由

最后更新 查看 MarkdownAgent 设置

routeAgentRequest() 是可选的。任何通过 fetch() 到达 Lifecycle Durable Object 的请求都会经过 Lifecycle,无论 Worker 如何找到该对象。如果你想使用自己的 URL,或者由服务器而非客户端决定请求发往哪个对象,请使用自定义路由。

将请求转发到命名对象

按名称获取存根并调用 fetch():

src/index.jsjs
export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		// /chat/:room goes to the ChatRoom object named :room.
		const room = url.pathname.match(/^\/chat\/([^/]+)/)?.[1];
		if (room) {
			return env.ChatRoom.getByName(decodeURIComponent(room)).fetch(request);
		}

		return new Response("Not found", { status: 404 });
	},
};
src/index.tsts
export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const url = new URL(request.url);

		// /chat/:room goes to the ChatRoom object named :room.
		const room = url.pathname.match(/^\/chat\/([^/]+)/)?.[1];
		if (room) {
			return env.ChatRoom.getByName(decodeURIComponent(room)).fetch(request);
		}

		return new Response("Not found", { status: 404 });
	},
} satisfies ExportedHandler<Env>;

若对象处于冷状态,转发的请求会先启动对象,然后交给其能力和 onRequest() 处理。WebSocket 升级请求的处理方式相同,因此 WebSockets 能力会接受这些请求。

设置位置时,请像 routeAgentRequest() 一样寻址对象:

const namespace = env.ChatRoom.jurisdiction("eu");
const stub = namespace.get(namespace.idFromName("general"), {
	locationHint: "weur",
});
const response = await stub.fetch(request);
const namespace = env.ChatRoom.jurisdiction("eu");
const stub = namespace.get(namespace.idFromName("general"), {
	locationHint: "weur",
});
const response = await stub.fetch(request);

props 路由选项仅适用于 routeAgentRequest()。对于转发请求,请在请求中发送这些值,或通过 RPC 调用对象。

在服务器上选择对象

如果对象取决于调用者,请从会话中选择对象名称。客户端连接到固定路径,既看不到也不会发送名称:

src/index.jsjs
import { routeAgentRequest } from "agents/routing";

async function getUserId(request) {
	// Read the user from your session cookie or token.
	return request.headers.get("x-user-id");
}

export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		if (url.pathname === "/me") {
			const userId = await getUserId(request);
			if (!userId) return new Response("Unauthorized", { status: 401 });
			return env.UserAgent.getByName(userId).fetch(request);
		}

		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
};
src/index.tsts
import { routeAgentRequest } from "agents/routing";

async function getUserId(request: Request): Promise<string | null> {
	// Read the user from your session cookie or token.
	return request.headers.get("x-user-id");
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const url = new URL(request.url);

		if (url.pathname === "/me") {
			const userId = await getUserId(request);
			if (!userId) return new Response("Unauthorized", { status: 401 });
			return env.UserAgent.getByName(userId).fetch(request);
		}

		return (
			(await routeAgentRequest(request, env)) ??
			new Response("Not found", { status: 404 })
		);
	},
} satisfies ExportedHandler<Env>;

使用 basePath 将客户端连接到该路径。agent 选项仍为必填项,但不会用于构建 URL:

import { useAgent } from "agents/react";

export function Profile() {
	const agent = useAgent({
		agent: "UserAgent",
		basePath: "me",
		onIdentity: (name, agentType) => {
			console.log(`Connected to ${agentType} ${name}`);
		},
	});

	return <p>{agent.identified ? agent.name : "Connecting..."}</p>;
}

WebSockets 能力会在客户端连接时发送身份帧,因此 agent.name 和 onIdentity 会报告服务器选择的名称。如果客户端重新连接时身份发生变化(例如用户以其他身份登录),则会运行 onIdentityChange。

身份帧包含对象名称。如果名称属于私密信息(例如内部用户 ID),请勿发送。请在 WebSockets 上设置 protocol: false,并在 onConnect 中发送自己的帧。更多信息,请参阅按连接控制协议。

通过 RPC 调用对象

Durable Object 上的公共方法是 RPC 方法。你可以在 Worker 中或另一个 Durable Object 中,通过存根调用这些方法:

src/index.jsjs
import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { Scheduler } from "agents/schedules";

export class UserAgent extends DurableObject {
	scheduler = new Scheduler({ callbacks: {} });
	lifecycle = Lifecycle.install(this).use(this.scheduler);

	async pendingReminders(props) {
		await this.lifecycle.start(props);
		return (await this.scheduler.list()).length;
	}
}

export default {
	async fetch(request, env) {
		const agents = env.UserAgent;
		const count = await agents
			.getByName("alice")
			.pendingReminders({ userId: "alice" });
		return Response.json({ count });
	},
};
src/index.tsts
import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { Scheduler } from "agents/schedules";

type Props = { userId: string };

export class UserAgent extends DurableObject<Env> {
	readonly scheduler = new Scheduler({ callbacks: {} });
	readonly lifecycle = Lifecycle.install<Env, Props>(this).use(this.scheduler);

	async pendingReminders(props?: Props): Promise<number> {
		await this.lifecycle.start(props);
		return (await this.scheduler.list()).length;
	}
}

export default {
	async fetch(request: Request, env: Env): Promise<Response> {
		const agents = env.UserAgent as DurableObjectNamespace<UserAgent>;
		const count = await agents
			.getByName("alice")
			.pendingReminders({ userId: "alice" });
		return Response.json({ count });
	},
} satisfies ExportedHandler<Env>;

RPC 调用不会经过 fetch(),因此不会启动生命周期。在每个使用 capability 或使用 onStart() 所设置内容的 RPC 方法开头,调用 await this.lifecycle.start()。

只有在此次调用启动对象时,你传给 start() 的 props 才会生效。更多信息,请参阅启动 Lifecycle。

agents 中的 getAgentByName() 会为你启动对象,但仅适用于 Agent 类。对于普通 Durable Object,请使用 getByName() 和 lifecycle.start()。

这篇文档对您有帮助吗?