一个真实项目通常至少在两个环境运行:开发环境和生产环境,它们的配置往往不同。 同一套代码,不同环境用不同的配置。 比如数据库连接字符串,开发时连的是你本机的测试库,上线后连的是云端生产库。 又比如日志级别,开发时要详细输出方便调试,生产时只记录严重错误。 如果每次切换环境都要手动改 appsettings.json,又麻烦又容易出错。所以 .NET 提供了一套机制来帮你自动处理这件事。 .NET 默认通过环境变量 ASPNETCORE_ENVIRONMENT 来识别当前是什么环境。 程序启动时,先读这个环境变量,值是什么它就认为自己在什么环境。 常见的值有三个: Development — 开发环境 Staging — 预发布/测试环境 Production — 生产环境 注意,环境不止这三种,可以自定义很多 你在自己的开发机上跑项目,Visual Studio 会自动把这个变量设为 Development。 部署到服务器上时,运维人员会在系统环境变量里把它设成 Production。 程序不用改,自动适配。 在代码中,可以通过 builder.Environment.EnvironmentName 来获取当前环境名。 var env = builder.Environment.EnvironmentName; // 比如 "Development" 还可以用内置的扩展方法做判断: if (builder.Environment.IsDevelopment()) { // 开发环境才做的事情 } if (builder.Environment.IsProduction()) { // 生产环境才做的事情 } 这些方法本质就是把 EnvironmentName 和 "Development"、"Production" 做比较。 多环境配置的核心文件是 appsettings.{环境名}.json 比如 appsettings.Development.json。 每个环境一个 JSON 文件,系统会自动挑对应名字的那个加载。 项目里通常有三个文件: appsettings.json — 公共配置,所有环境都读 appsettings.Development.json — 只在开发环境读 appsettings.Production.json — 只在生产环境读 加载顺序是:先读 appsettings.json,再读对应环境的那个文件。环境文件里的同名键会覆盖公共文件里的值。 举例: appsettings.json: { "Database": { "ConnectionString": "默认值", "MaxRetry": 3 } } appsettings.Development.json: { "Database": { "ConnectionString": "开发库地址" } } 开发环境最终读到的效果:ConnectionString 是“开发库地址”,MaxRetry 还是 3(公共配置的值保留,没被覆盖的继续生效)。 接下来看一个小练习,看看是否理解了 假设你有以下两个文件: appsettings.json: { "AppName": "MyApp", "LogLevel": "Warning", "MaxUsers": 100 } appsettings.Production.json: { "LogLevel": "Error", "MaxUsers": 200 } 问题: 程序跑在 Production 环境下,最终读到的 AppName、LogLevel、MaxUsers 分别是什么值? 答案: AppName是MyApp LogLevel是Error MaxUsers是200 轻松答对,对吧,根本没有难度嘛~ 好我们继续说 敏感信息(如数据库密码、API 密钥)绝对不能写在 appsettings.json 里,更不能提交到 Git。 JSON 文件里只放非敏感配置,秘密用另外的安全方式管理。 appsettings.json 通常会提交到代码仓库,团队所有人都能看到。如果把数据库密码写进去,等于把密码公开了。 解决方法分环境来看: 开发环境:用 User Secrets(存在本机的一个独立 JSON 文件,不跟项目代码放一起,不会误提交到 Git) 生产环境:用环境变量,或者云密钥管理服务(如 Azure Key Vault) 这两种方式的数据,配置系统会自动合并进来,和 JSON 里的配置用起来一模一样。 开发环境中使用 User Secrets,通过命令行 dotnet user-secrets 来管理。 第一步,在项目目录下初始化(只需做一次): dotnet user-secrets init 第二步,存值: dotnet user-secrets set "Database:Password" "123456" 第三步,代码里照常读取,不用做任何特殊处理: var password = builder.Configuration["Database:Password"]; User Secrets 的物理文件存在你电脑的用户目录下,不在项目文件夹里,Git 碰不到它。 生产环境中,敏感信息通常通过环境变量传入,配置系统会自动读取。 服务器上设好环境变量,代码里照常取值,不用改一行代码。 还记得我们第一节说的吗?WebApplication.CreateBuilder 默认就加载了环境变量配置源。所以在服务器上设一个环境变量: 数据库密码 = "生产环境密码" 代码里直接用同样的键去取: var password = builder.Configuration["Database:Password"]; 配置系统会把环境变量和 JSON 合并在一起,你感知不到来源的差异。 没看懂?来看例子 现在你的程序要连数据库,需要密码。密码不能写在 JSON 里。 生产环境的做法: 第一步,运维人员在服务器上设置一个环境变量,键名叫 Database:Password,值是真实密码。 第二步,你的代码不用改,还是原来那一行: var password = builder.Configuration["Database:Password"]; 为什么这行代码能取到环境变量的值? 因为程序启动时,ConfigurationBuilder 做了两件事: 1. 先加载 appsettings.json,里面可能有 Database 段但没有密码,或者密码是假的 2. 接着加载环境变量,发现里面有个 Database:Password,就用这个值覆盖掉 JSON 里同名的值 最后合并出来的结果里,Database:Password 的值就是环境变量里设的那个真实密码。 环境变量里无法直接表示 JSON 的层级结构,所以用双下划线 __ 或冒号 : 来表示层级。 JSON 里是嵌套的: { "Database": { "Password": "xxx" } } 环境变量只能写平的一行,就写成: Database__Password = "xxx" // 双下划线 __ 等价于 JSON 里的层级 或者在某些平台(比如 Linux)可以用冒号: Database:Password = "xxx" 配置系统识别这两种写法,会自动还原成 Database 段下的 Password 键。 好,来看小练习 User Secrets 文件存在项目文件夹下的一个隐藏目录里,所以只要不提交这个目录到 Git 就行。 对还是错? 答案: ✖ User Secrets 文件根本不在项目文件夹里,所以问题里的前提就是错的。 它的实际位置在你电脑的用户目录下,一般类似这个路径: C:\Users\你的用户名\AppData\Roaming\Microsoft\UserSecrets\<随机ID>\secrets.json 这个目录和你的项目文件夹完全没有关系,所以不存在“要手动排除它不提交 Git”这回事。它本来就不在项目里,Git 永远看不到它。 这就是 User Secrets 安全的核心:物理隔离,而不是靠 .gitignore 来防。 如果是项目文件夹里的隐藏目录,你忘加 .gitignore 就可能泄露。User Secrets 从设计上杜绝了这个风险。 理解了吗,好的,恭喜你走到了本节的末尾 我们简单总结一下这一节我们学了点啥 多环境: 同一套代码,开发/生产配置不同 环境识别: ASPNETCORE_ENVIRONMENT 环境变量,三个推荐值,可自定义 环境判断: IsDevelopment()、IsProduction()、IsEnvironment("自定义") 环境文件: appsettings.{环境}.json,覆盖同名键,保留不同名键 敏感信息原则: 密码密钥不写 JSON,不提交 Git 开发环境方案: User Secrets,存在用户目录,物理隔离 生产环境方案: 环境变量,用 __ 或 : 表示层 回顾完毕,来看最终练习 1.appsettings.Development.json 里的配置会完全替换掉 appsettings.json 里的所有配置。对吗?为什么? 2.你在开发机上想存一个 API 密钥,不想提交到 Git。应该用什么方案?命令怎么写(假设键是 ApiKey,值是 sk-123456)? 3.环境变量中表示 Logging:LogLevel:Default 这个层级键,在 Linux 上应该写成 ________,在 Windows 上推荐写成 ________。 答案: 1.✖ appsettings.Development.json 不是完全替换,而是只覆盖和 appsettings.json 同名的键。 appsettings.json 里有而 Development 文件里没有的键,继续保留生效 2. 在项目目录下执行 dotnet user-secrets set "ApiKey" "sk-123456" 代码取值 var apiKey = builder.Configuration["ApiKey"]; 3. Linux Logging:LogLevel:Default Windows Logging__LogLevel__Default 答对了吗,如果是的话,那太好了,你已经把基础的部分毕业了,已经可以着手一些小项目。 回顾一下这三节,都学了什么 怎么读取配置: ["键"] 或 GetSection 配置太多一个个取麻烦 定义类,Get<T>() 或 Options 绑定 怎么让整个项目都能用配置: Configure<T>() 注册,构造函数注入 option三兄弟 开发和生产配置不同: appsettings.{环境}.json 自动覆盖 密钥怎么存: 开发用 User Secrets,生产用环境变量 配置检查: ValidateOnStart() 启动校验 接下来就是进阶了,不要着急,先把基础看明白吃透,再去考虑进阶内容。
(三)多环境管理与安全实践
以下为完整正文内容。
正文
搜索结果
请输入关键词开始搜索。