一个常见的模式是使用 Static Assets 提供静态前端应用程序(例如,React、Vue、Svelte),然后将后端请求传递给容器化的后端应用程序。
在本示例中,我们将展示一个使用作为静态资产提供的简单 index.html 文件的示例,但您可以从许多前端框架中选择一种。有关更多信息,请参阅我们的 Workers framework examples。
有关完整示例,请参阅 Static Frontend + Container Backend Template ↗。
{
"name": "cron-container",
"main": "src/index.ts",
"assets": {
"directory": "./dist",
"binding": "ASSETS"
},
"containers": [
{
"class_name": "Backend",
"image": "./Dockerfile",
"max_instances": 3
}
],
"durable_objects": {
"bindings": [
{
"class_name": "Backend",
"name": "BACKEND"
}
]
},
"migrations": [
{
"new_sqlite_classes": [
"Backend"
],
"tag": "v1"
}
]
}name = "cron-container"
main = "src/index.ts"
[assets]
directory = "./dist"
binding = "ASSETS"
[[containers]]
class_name = "Backend"
image = "./Dockerfile"
max_instances = 3
[[durable_objects.bindings]]
class_name = "Backend"
name = "BACKEND"
[[migrations]]
new_sqlite_classes = [ "Backend" ]
tag = "v1"在 ./dist 目录中创建一个简单的 index.html 文件。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Widgets</title>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.13.3/cdn.min.js"></script>
</head>
<body>
<div x-data="widgets()" x-init="fetchWidgets()">
<h1>Widgets</h1>
<div x-show="loading">Loading...</div>
<div x-show="error" x-text="error" style="color: red;"></div>
<ul x-show="!loading && !error">
<template x-for="widget in widgets" :key="widget.id">
<li>
<span x-text="widget.name"></span> - (ID: <span x-text="widget.id"></span>)
</li>
</template>
</ul>
<div x-show="!loading && !error && widgets.length === 0">
No widgets found.
</div>
</div>
<script>
function widgets() {
return {
widgets: [],
loading: false,
error: null,
async fetchWidgets() {
this.loading = true;
this.error = null;
try {
const response = await fetch('/api/widgets');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
this.widgets = await response.json();
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
}
}
}
</script>
</body>
</html>在本示例中,我们使用 Alpine.js ↗ 从 /api/widgets 获取小组件列表。
这是一个非常简单的示例,但您可以实现更复杂的逻辑。有关更多信息,请参阅 examples of Workers integrating with frontend frameworks。
您的 Worker 需要能够同时提供静态资产并将请求路由到容器化的后端。
在这种情况下,如果路由以 /api 开头,我们将请求传递给三个容器实例之一,而所有其他请求将作为静态资产提供。
import { Container, getRandom } from "@cloudflare/containers";
const INSTANCE_COUNT = 3;
class Backend extends Container {
defaultPort = 8080; // pass requests to port 8080 in the container
sleepAfter = "2h"; // only sleep a container if it hasn't gotten requests in 2 hours
}
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname.startsWith("/api")) {
const containerInstance = await getRandom(env.BACKEND, INSTANCE_COUNT);
return containerInstance.fetch(request);
}
return env.ASSETS.fetch(request);
},
};您的容器应能够处理对 /api/widgets 的请求。
在本例中,我们将使用一个简单的 Golang 后端,该后端返回硬编码的小组件列表。
server.go
package main
import (
"encoding/json"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r \*http.Request) {
widgets := []map[string]interface{}{
{"id": 1, "name": "Widget A"},
{"id": 2, "name": "Sprocket B"},
{"id": 3, "name": "Gear C"},
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
json.NewEncoder(w).Encode(widgets)
}
func main() {
http.HandleFunc("/api/widgets", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}