文件
a project

Placeholder 佔位符

在 Caddy 中,Placeholder 佔位符會根據個別外掛程式的需求進行處理;它們不會自動在所有地方都運作。

這表示如果您希望您的外掛程式支援 Placeholder 佔位符,您必須明確地新增對它們的支援。

如果您還不熟悉 Placeholder 佔位符,請從這裡開始閱讀!

Placeholder 佔位符概觀

Placeholder 佔位符是以 {foo.bar} 格式呈現的字串,用作動態配置值,並在執行時進行評估。

Caddyfile 環境變數替換(以美元符號開頭,例如 {$FOO})在 Caddyfile 解析時進行評估,並且不需要由您的外掛程式處理。 這些並非 Placeholder 佔位符,儘管它們共用相同的 { } 語法。

因此,務必理解 {env.HOST}(全域 Placeholder 佔位符)與 {$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}(全域 Placeholder 佔位符),因此在轉換為 JSON 時它保持不變。

這確實意味著撰寫 JSON 配置(不使用 Caddyfile)的使用者無法使用 {$ENV} 語法。 因此,對於外掛程式作者來說,實作 Placeholder 佔位符替換支援非常重要,尤其是在配置被佈建時。 這將在下面解釋。

實作 Placeholder 佔位符支援

您不應在 UnmarshalCaddyfile() 中處理 Placeholder 佔位符。 相反地,Placeholder 佔位符應稍後替換,無論是在 Provision() 步驟中,還是在模組執行期間(例如,HTTP 處理器的 ServeHTTP(),匹配器的 Match() 等),使用 caddy.Replacer

範例

在這裡,我們使用新建立的替換器來處理 Placeholder 佔位符。 它有權存取全域 Placeholder 佔位符,例如 {env.HOST},但沒有 HTTP Placeholder 佔位符,例如 {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() 中獲取替換器。 此替換器有權存取全域 Placeholder 佔位符每個請求的 HTTP Placeholder 佔位符,例如 {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)
}