占位符
在 Caddy 中,占位符由每个单独的插件根据需要处理;它们不会自动在任何地方工作。
这意味着如果你希望你的插件支持占位符,你必须明确添加对它们的支持。
如果你还不熟悉占位符,请先阅读这里!
占位符概述
占位符是格式为 {foo.bar}
的字符串,用作动态配置值,稍后在运行时进行评估。
Caddyfile 环境变量替换以美元符号开头,如 {$FOO}
,在 Caddyfile 解析时进行评估,不需要由你的插件处理。这些不是占位符,尽管它们共享相同的 { }
语法。
因此,重要的是要理解 {env.HOST}
(一个全局占位符)与 {$HOST}
(一个 Caddyfile 环境变量替换)本质上是不同的。
例如,看以下 Caddyfile:
:8080 {
respond {$HOST} 200
}
:8081 {
respond {env.HOST} 200
}
当你使用 HOST=example caddy adapt
将此 Caddyfile 适配为 JSON 时,你将得到:
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":8080"],
"routes": [
{
"handle": [
{
"body": "example",
"handler": "static_response",
"status_code": 200
}
]
}
]
},
"srv1": {
"listen": [":8081"],
"routes": [
{
"handle": [
{
"body": "{env.HOST}",
"handler": "static_response",
"status_code": 200
}
]
}
]
}
}
}
}
}
特别要注意 srv0
和 srv1
中的 "body"
字段。
由于 srv0
使用了 {$HOST}
(Caddyfile 环境变量替换),该值变成了 example
,因为它在生成 JSON 配置时在 Caddyfile 解析期间被处理。
由于 srv1
使用了 {env.HOST}
(一个全局占位符),它在适配为 JSON 时保持不变。
这确实意味着编写 JSON 配置(不使用 Caddyfile)的用户不能使用 {$ENV}
语法。因此,插件作者在配置被配置时实现替换占位符的支持很重要。这将在下面解释。
实现占位符支持
你不应该在 UnmarshalCaddyfile()
中处理占位符。相反,占位符应该稍后替换,要么在 Provision()
步骤中,要么在你的模块执行期间(例如,HTTP 处理程序的 ServeHTTP()
,匹配器的 Match()
等),使用 caddy.Replacer
。
示例
这里,我们使用新构造的替换器来处理占位符。它可以访问全局占位符,如 {env.HOST}
,但不能访问 HTTP 占位符,如 {http.request.uri}
,因为配置发生在配置加载时,而不是在请求期间。
func (g *Gizmo) Provision(ctx caddy.Context) error {
repl := caddy.NewReplacer()
g.Name = repl.ReplaceAll(g.Name,"")
return nil
}
这里,我们在 ServeHTTP
期间从请求上下文 r.Context()
获取替换器。这个替换器可以访问全局占位符和每个请求的 HTTP 占位符,如 {http.request.uri}
。
func (g *Gizmo) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
_, err := w.Write([]byte(repl.ReplaceAll(g.Name,"")))
if err != nil {
return err
}
return next.ServeHTTP(w, r)
}