文档
a 项目

占位符

在 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

示例

在这里,我们使用新构建的 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)
}