本课学习目标
- 理解“应用配置”与“用户偏好”的区别。
- 理解为什么不应该把所有可变值硬编码在程序中。
- 建立 appsettings.json。
- 安装并使用 Microsoft.Extensions.Configuration.Json。
- 使用 ConfigurationBuilder 读取 JSON 配置。
- 使用 IConfiguration 读取分层配置值。
- 理解 CopyToOutputDirectory。
- 建立 UserPreferences 类。
- 把用户偏好序列化成 JSON 文件。
- 使用 Environment.SpecialFolder.LocalApplicationData 保存每个用户自己的设置。
- 理解为什么秘密信息不能直接放进 appsettings.json 并提交到公开 GitHub。
34.1 什么叫“硬编码”?
如果一个可能会改变的值直接写死在程序代码里,每次修改都必须重新编辑代码、编译并发布。
例如:
Dim apiUrl As String =
"https://api.example.com/students"
或者:
lblTitle.Text =
"Student Manager 2026"
值和程序逻辑混在一起;修改配置往往需要重新编译。
把环境或部署相关值放在代码外,程序启动时读取。
34.2 应用配置与用户偏好
应用配置
API URL、应用标题、分页大小、功能开关等。
用户偏好
最后选择的课程、窗体大小、是否记住选择等。
秘密资料
API Key、密码、Token 不应与普通配置同等处理。
34.3 建立 appsettings.json
在项目中加入:
appsettings.json
内容:
{
"Application": {
"Name": "Student Settings Manager 2026",
"DefaultCourse": "Visual Basic 2026",
"PageSize": 20
},
"Api": {
"BaseUrl": "https://jsonplaceholder.typicode.com"
},
"Features": {
"EnableApiDemo": true
}
}
34.4 JSON 配置可以分层
例如:
Application
├─ Name
├─ DefaultCourse
└─ PageSize
Api
└─ BaseUrl
Features
└─ EnableApiDemo
读取时使用冒号分隔:
Application:Name
Application:DefaultCourse
Api:BaseUrl
Features:EnableApiDemo
34.5 安装 Configuration NuGet 包
右键项目:
Manage NuGet Packages
→ Browse
安装:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json
34.6 确保 appsettings.json 被复制到输出目录
项目运行时,程序通常从输出目录执行。因此配置文件也必须出现在那里。
可以在项目文件中加入:
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
这样构建项目时,appsettings.json 会复制到应用输出目录。
34.7 建立 AppConfiguration.vb
创建:
Configuration\AppConfiguration.vb
Imports Microsoft.Extensions.Configuration
Public NotInheritable Class AppConfiguration
Private Shared ReadOnly configuration As IConfigurationRoot =
New ConfigurationBuilder().
SetBasePath(
AppContext.BaseDirectory).
AddJsonFile(
"appsettings.json",
False,
True).
Build()
Private Sub New()
End Sub
Public Shared ReadOnly Property ApplicationName As String
Get
Return configuration(
"Application:Name")
End Get
End Property
Public Shared ReadOnly Property DefaultCourse As String
Get
Return configuration(
"Application:DefaultCourse")
End Get
End Property
Public Shared ReadOnly Property ApiBaseUrl As String
Get
Return configuration(
"Api:BaseUrl")
End Get
End Property
End Class
34.8 ConfigurationBuilder 在做什么?
34.9 为什么设置 Base Path?
SetBasePath(
AppContext.BaseDirectory)
这样 appsettings.json 的相对路径会从应用实际运行目录开始解析,比依赖当前工作目录更清楚。
34.10 在 Form 中读取配置
Private Sub Form1_Load(
sender As Object,
e As EventArgs) Handles MyBase.Load
Me.Text =
AppConfiguration.ApplicationName
lblApiUrl.Text =
AppConfiguration.ApiBaseUrl
cmbCourse.Text =
AppConfiguration.DefaultCourse
End Sub
34.11 配置值可能缺失
如果 JSON 中没有某个 key,读取结果可能是 Nothing。因此可以提供默认值:
Dim applicationName As String =
AppConfiguration.ApplicationName
If String.IsNullOrWhiteSpace(
applicationName) Then
applicationName =
"Student Manager"
End If
34.12 建立 UserPreferences 类
用户可以修改的设置不要直接覆盖 appsettings.json。本课另外建立用户偏好文件。
创建:
Models\UserPreferences.vb
Public Class UserPreferences
Public Property RememberLastCourse As Boolean = True
Public Property LastCourse As String = ""
Public Property WindowWidth As Integer = 1000
Public Property WindowHeight As Integer = 700
End Class
34.13 用户设置保存在哪里?
取得当前用户本机应用数据目录:
Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData)
然后建立本应用自己的子目录:
VBTutor
└─ StudentSettingsManager2026
└─ user-settings.json
34.14 建立 SettingsService
创建:
Services\SettingsService.vb
Imports System.IO
Imports System.Text.Json
Public Class SettingsService
Private ReadOnly settingsFolder As String
Private ReadOnly settingsFile As String
Public Sub New()
Dim localData As String =
Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData)
settingsFolder =
Path.Combine(
localData,
"VBTutor",
"StudentSettingsManager2026")
settingsFile =
Path.Combine(
settingsFolder,
"user-settings.json")
End Sub
34.15 保存用户偏好
Public Sub Save(
settings As UserPreferences)
Directory.CreateDirectory(
settingsFolder)
Dim options As New JsonSerializerOptions With {
.WriteIndented = True
}
Dim json As String =
JsonSerializer.Serialize(
settings,
options)
File.WriteAllText(
settingsFile,
json)
End Sub
第一次保存时,如果目录不存在:
Directory.CreateDirectory()
会先建立它。
34.16 读取用户偏好
Public Function Load() As UserPreferences
If Not File.Exists(
settingsFile) Then
Return New UserPreferences()
End If
Dim json As String =
File.ReadAllText(
settingsFile)
Dim settings As UserPreferences =
JsonSerializer.Deserialize(
Of UserPreferences)(
json)
If settings Is Nothing Then
Return New UserPreferences()
End If
Return settings
End Function
End Class
34.17 保存后的 user-settings.json
{
"RememberLastCourse": true,
"LastCourse": "Visual Basic 2026",
"WindowWidth": 1100,
"WindowHeight": 760
}
这就是 Lesson 33 所学 JSON 在真实桌面应用中的另一个用途:保存用户偏好。
34.18 实作:Student Settings Manager 2026
建立:
StudentSettingsManager2026
建议项目结构:
StudentSettingsManager2026
├─ Configuration
│ └─ AppConfiguration.vb
├─ Models
│ └─ UserPreferences.vb
├─ Services
│ └─ SettingsService.vb
├─ appsettings.json
└─ Form1.vb
34.19 Form1 保存 SettingsService
Public Class Form1
Private ReadOnly settingsService As New SettingsService()
Private currentSettings As UserPreferences
34.20 Form.Load 同时读取应用配置和用户偏好
Private Sub Form1_Load(
sender As Object,
e As EventArgs) Handles MyBase.Load
Me.Text =
AppConfiguration.ApplicationName
lblApiUrl.Text =
AppConfiguration.ApiBaseUrl
Try
currentSettings =
settingsService.Load()
Catch ex As Exception
currentSettings =
New UserPreferences()
MessageBox.Show(
"无法读取用户设置,将使用默认值。" &
Environment.NewLine &
ex.Message)
End Try
chkRememberCourse.Checked =
currentSettings.RememberLastCourse
If currentSettings.RememberLastCourse AndAlso
Not String.IsNullOrWhiteSpace(
currentSettings.LastCourse) Then
cmbCourse.Text =
currentSettings.LastCourse
Else
cmbCourse.Text =
AppConfiguration.DefaultCourse
End If
Me.Width =
Math.Max(
600,
currentSettings.WindowWidth)
Me.Height =
Math.Max(
450,
currentSettings.WindowHeight)
End Sub
34.21 保存偏好按钮
Private Sub btnSaveSettings_Click(
sender As Object,
e As EventArgs) Handles btnSaveSettings.Click
Dim settings As New UserPreferences With {
.RememberLastCourse =
chkRememberCourse.Checked,
.LastCourse =
cmbCourse.Text.Trim(),
.WindowWidth =
Me.Width,
.WindowHeight =
Me.Height
}
Try
settingsService.Save(
settings)
currentSettings =
settings
lblStatus.Text =
"用户偏好已保存。"
Catch ex As IOException
MessageBox.Show(
ex.Message,
"保存设置失败",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End Try
End Sub
34.22 重新读取设置
Private Sub btnReload_Click(
sender As Object,
e As EventArgs) Handles btnReload.Click
Try
currentSettings =
settingsService.Load()
chkRememberCourse.Checked =
currentSettings.RememberLastCourse
cmbCourse.Text =
currentSettings.LastCourse
lblStatus.Text =
"用户偏好已重新读取。"
Catch ex As Exception
MessageBox.Show(
ex.Message,
"读取设置失败",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End Try
End Sub
34.23 恢复默认值
Private Sub btnReset_Click(
sender As Object,
e As EventArgs) Handles btnReset.Click
currentSettings =
New UserPreferences()
chkRememberCourse.Checked =
currentSettings.RememberLastCourse
cmbCourse.Text =
AppConfiguration.DefaultCourse
Me.Width =
currentSettings.WindowWidth
Me.Height =
currentSettings.WindowHeight
lblStatus.Text =
"已经恢复默认设置。"
End Sub
34.24 关闭窗体时自动记住设置
如果希望关闭时保存:
Private Sub Form1_FormClosing(
sender As Object,
e As FormClosingEventArgs) Handles MyBase.FormClosing
Dim settings As New UserPreferences With {
.RememberLastCourse =
chkRememberCourse.Checked,
.LastCourse =
cmbCourse.Text.Trim(),
.WindowWidth =
Me.Width,
.WindowHeight =
Me.Height
}
Try
settingsService.Save(
settings)
Catch ex As IOException
' 关闭程序时不阻止退出。
' 正式应用可以写入日志。
End Try
End Sub
34.25 为什么不直接修改 appsettings.json?
随应用一起部署,适合程序级、环境级或管理员设定的非秘密配置。
属于当前用户,可以在运行时修改,并保存在用户自己的应用数据目录。
34.26 配置文件不等于秘密保险箱
不要因为使用 appsettings.json 就把秘密直接放进去:
{
"OpenAI": {
"ApiKey": "真实秘密密钥"
}
}
34.27 环境变量概念
程序可以读取环境变量:
Dim apiKey As String =
Environment.GetEnvironmentVariable(
"MY_API_KEY")
如果没有设置:
If String.IsNullOrWhiteSpace(
apiKey) Then
MessageBox.Show(
"尚未配置 API Key。")
End If
这样源代码和公开配置文件中都不需要出现真正密钥。
34.28 配置值也要验证
例如 PageSize:
Dim pageSizeText As String =
configuration(
"Application:PageSize")
Dim pageSize As Integer
If Not Integer.TryParse(
pageSizeText,
pageSize) OrElse
pageSize <= 0 Then
pageSize =
20
End If
即使值来自自己写的配置文件,也应该考虑配置被删掉、拼错或改成无效内容的情况。
34.29 本课设置架构
34.30 初学者常见错误
appsettings.json 没复制到输出目录
代码正确但运行时找不到文件。检查 CopyToOutputDirectory。
把用户偏好写到安装目录
安装目录可能不适合普通用户写入;用户偏好应放到用户数据目录。
相信配置永远有效
配置可能缺失或格式错误,重要值需要默认值与验证。
把 API Key 当普通配置
秘密信息需要更安全的存储与部署方式。
34.31 小练习
- 在 appsettings.json 加入 Application:SupportEmail,在 About 对话框显示。
- 加入 Features:EnableApiDemo,False 时禁用 API 按钮。
- 在 UserPreferences 加入 DarkMode Boolean。
- 加入“记住上一次 API URL”的用户偏好。
- 故意删除 appsettings.json,观察程序启动错误,并加入更友好的错误处理。
- 把 PageSize 改成无效文字,加入默认值 20。
- 使用环境变量读取一个假的教学 API Key,而不是把值写进代码。
本课复习
- 什么是硬编码?
- 应用配置与用户偏好有什么不同?
- appsettings.json 适合保存哪一类设置?
- ConfigurationBuilder 的作用是什么?
- AddJsonFile() 做什么?
- 为什么需要 CopyToOutputDirectory?
- 为什么用户偏好适合放在 LocalApplicationData?
- 怎样把 UserPreferences 保存成 JSON?
- 为什么配置值也需要验证和默认值?
- 为什么 API Key 不应该直接放在公开 appsettings.json 或 GitHub?