.Net Core appsettings.json 最佳實踐 - 覆蓋開發設置(反之亦然)?
尋找一種在 .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, 然後是 finallysecrets.json。這樣,無論如何,我都可以用我的本地機密文件覆蓋 azure 中的設置(即使我覆蓋的設置不是我基本上最終編寫了一些自定義程式碼
program.cs來處理從 Azure 載入配置源,然後查找JsonConfigurationSource具有Pathof 的"secrets.json",然後將其作為我的IConfigurationBuilder.Sources.對我來說,我的文件按如下方式使用
appsettings.json- 需要為任何環境設置的通用設置,並且可能永遠不會根據環境而改變。appsettings.{environment}.json- 主要是一個空的 JSON 文件,基本上只是命名要連接到的AzureAppConfig&資源名稱AzuerKeyVaultAzureAppConfig- 基本上對於生產、登台/測試或本地開發之間的任何差異,並且不是敏感資訊。API 端點地址、IP 地址、各種 URL、錯誤日誌資訊,諸如此類。AzureKeyVault- 任何敏感的東西。使用者名、密碼、外部 API 的密鑰(身份驗證、許可證密鑰、連接字元串等)。問題是,即使您將設置放入
appsettings.json,這並不意味著您不能用appsettings.{enviroment}.json其他地方或其他地方覆蓋它。我經常在根設置文件中放置一個值為 的設置NULL,只是為了提醒我這是應用程序中使用的設置。所以一個更好的問題可能是,您是否希望能夠執行您的應用程序(如無錯誤)而只使用基礎appsettings.json和secrets.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.cspublic void ConfigureServices(IServiceCollection services) { services.AddTransient<IStartupFilter>(x => new EagerOptionsValidationStartupFilter(x) { EagerValidateTypes = { typeof(MailOptions), typeof(OtherOptions), typeof(MoreImportantOptions) } }); }