占位符
在 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
。
示例
在这里,我们使用新构建的 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()
中获取 replacer。此 replacer 可以访问全局占位符和每个请求的 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)
}