文档
一个 项目

占位符

在 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
                }
              ]
            }
          ]
        }
      }
    }
  }
}

特别要注意 srv0srv1 中的 "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)
}