Dot-Net

.Net Core appsettings.json 最佳實踐 - 覆蓋開發設置(反之亦然)?

  • January 26, 2022

尋找一種在 .Net Core 中建構 appsettings.json 文件的合理方法。

是否應該將基本“appsettings.json”文件配置為在開發環境中執行,然後基於環境的覆蓋(例如 appsettings.production.json)覆蓋生產的特定鍵?

或者 appsettings.json 是否應該只包含在所有環境中共享的配置,然後是一個特定的 appsettings.development/staging.json 文件,用於為這些環境顯式設置密鑰?

我擔心的是 - 假設應用程序部署到實時伺服器,但儲存在環境變數中的密鑰(例如覆蓋連接字元串)失去或拼寫錯誤等。在這種情況下,應用程序將回退到基本 appsettings.json連接字元串,這將是實時環境的錯誤數據庫。像這樣的場景聽起來非常災難性,特別是因為這很容易被忽視?

所以問題真的歸結為 - 基本 appsettings.json 文件的內容是否應該是預設的“開發”值(例如開發數據庫、沙盒 API),這些值被生產數據覆蓋,反之亦然?

我認為這有一個無聊的答案;這取決於。但我最喜歡的方法是:

appsetting.json (base settings)
appsettings.development.json (dev with no secrets)
appsettings.production.json (production with no secrets)

Appsettings,其中機密值僅存在於基本設置中,而其他值則寫入相應的 appsettings。

$$ env $$.json。因此範例數據庫連接鍵僅存在於本地數據庫的基本設置中。替換它是環境工作 數據庫連接和日誌記錄範例

應用設置.json

{
"ConnectionStrings": {
 “dbConnection: “data source=localhost” <—— only here
},
“environment”: “local”,
"Logging": {
 "LogLevel": {
   "Default": “Verbose”
 }
},
}

appsettings.development.json

{
“environment”: “development”,
"Logging": {
 "LogLevel": {
   "Default": “Warning”
 }
},
}

appsettings.production.json

{
“environment”: “production”,
"Logging": {
 "LogLevel": {
   "Default": “Information”
 }
},
}

我擔心的是 - 假設應用程序部署到實時伺服器,但儲存在環境變數中的密鑰(例如覆蓋連接字元串)失去或拼寫錯誤等。在這種情況下,應用程序將回退到基本 appsettings.json連接字元串,這將是實時環境的錯誤數據庫。像這樣的場景聽起來非常災難性,特別是因為這很容易被忽視?

你總是可以做到這一點。但是一些健全性測試應該做到這一點。如果您的基礎架構/部署管道允許,請在 ping 數據庫的位置進行簡單的執行狀況檢查。

我已經養成了將我的配置儲存在 AzureAppConfig 和/或 AzureKeyVault 下的習慣。它為我提供了一個中心位置來管理我的開發、登台/測試、生產設置,並且不需要我通過操作 appsettings 文件或將它們儲存在某種部署儲存庫中來使我的部署複雜化。它實際上只在應用程序啟動時從天藍色讀取(我不需要在我的應用程序執行時刷新它們)。話雖如此,這讓本地開發故事變得有點有趣,因為我個人希望操作的順序是appsettings.json, appsettings.{environment}.json, AzureAppConfig, KeyVault, 然後是 finally secrets.json。這樣,無論如何,我都可以用我的本地機密文件覆蓋 azure 中的設置(即使我覆蓋的設置不是

我基本上最終編寫了一些自定義程式碼program.cs來處理從 Azure 載入配置源,然後查找JsonConfigurationSource具有Pathof 的"secrets.json",然後將其作為我的IConfigurationBuilder.Sources.

對我來說,我的文件按如下方式使用

  • appsettings.json- 需要為任何環境設置的通用設置,並且可能永遠不會根據環境而改變。 appsettings.{environment}.json- 主要是一個空的 JSON 文件,基本上只是命名要連接到的AzureAppConfig&資源名稱AzuerKeyVault
  • AzureAppConfig- 基本上對於生產、登台/測試或本地開發之間的任何差異,並且不是敏感資訊。API 端點地址、IP 地址、各種 URL、錯誤日誌資訊,諸如此類。
  • AzureKeyVault- 任何敏感的東西。使用者名、密碼、外部 API 的密鑰(身份驗證、許可證密鑰、連接字元串等)。

問題是,即使您將設置放入appsettings.json,這並不意味著您不能用appsettings.{enviroment}.json其他地方或其他地方覆蓋它。我經常在根設置文件中放置一個值為 的設置NULL,只是為了提醒我這是應用程序中使用的設置。所以一個更好的問題可能是,您是否希望能夠執行您的應用程序(如無錯誤)而只使用基礎appsettings.jsonsecrets.json?或者是否appsettings.{enviroment}.json總是需要成功啟動的內容?

根據您的問題要查看的另一件事是驗證您的配置。更高版本Microsoft.Extensions.Options提供了各種方法來驗證您的選項,以便您可以嘗試擷取某些內容為空/未定義的實例。我通常用數據註釋屬性裝飾我的 POCO Options 類,然後ValidateDataAnnotations()用來驗證它們是否正確設置。

例如

services.AddOptions<MailOptions>().Bind(configuration.GetSection("MailSettings")).ValidateDataAnnotations();

值得注意的是,此驗證僅在您嘗試MailOptions從 DI 請求類似我上面用作範例的內容時執行(因此不在啟動時)因此,我還創建了自己的驗證IStartupFilter來搶先請求我的一個或多個 Options 類在應用程序啟動時從服務提供商處獲取,以便在應用程序開始接受請求之前強制執行相同的驗證。

public class EagerOptionsValidationStartupFilter : IStartupFilter
{
   public readonly ICollection<Type> EagerValidateTypes = new List<Type>();
   private readonly IServiceProvider serviceProvider;

   public EagerOptionsValidationStartupFilter(IServiceProvider serviceProvider)
   {
       this.serviceProvider = serviceProvider;
   }

   public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
   {
       foreach (var eagerType in EagerValidateTypes)
       {
           dynamic test = serviceProvider.GetService(typeof(IOptions<>).MakeGenericType(eagerType));
           _ = test.Value;
       }

       return next;
   }
}

startup.cs

public void ConfigureServices(IServiceCollection services)
{

   services.AddTransient<IStartupFilter>(x =>
       new EagerOptionsValidationStartupFilter(x)
       {
           EagerValidateTypes = {
               typeof(MailOptions),
               typeof(OtherOptions),
               typeof(MoreImportantOptions)
           }
       });
}

引用自:https://stackoverflow.com/questions/62087066