占位符
在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环境变量替换),因此在生成JSON时已经变成example。srv1使用的是{env.HOST}(全局占位符),因此在adapt到JSON时会保持原样。
这也意味着:直接写JSON配置(不使用Caddyfile)的用户不能使用{$ENV}语法。因此,插件作者应在配置Provision阶段实现占位符替换支持,下面会说明。
实现占位符支持
你不应在UnmarshalCaddyfile()中处理占位符。占位符应在更晚阶段替换:要么在Provision()阶段,要么在模块执行阶段(例如HTTP处理器的ServeHTTP()、匹配器的Match()等),并使用caddy.Replacer。
示例
下面示例使用新建的replacer来处理占位符。它能访问全局占位符(如{env.HOST}),但不能访问HTTP占位符(如{http.request.uri}),因为provision发生在配置加载阶段,而不是请求阶段。
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) }