ASP.NET Core 2.2 基础知识(六)【Configuration】

ASP.NET Core 中的应用配置基于配置提供程序建立的键值对。 配置提供程序将配置数据从各种配置源读取到键值对:

  • Azure Key Vault
  • 命令行参数
  • (已安装或已创建的)自定义提供程序
  • 目录文件
  • 环境变量
  • 内存中的 .NET 对象
  • 设置文件

选项模式是本主题中描述的配置概念的扩展。 选项使用类来表示相关设置的组。 有关使用选项模式的详细信息,请参阅 ASP.NET Core 中的选项模式

这三个包均包括在 Microsoft.AspNetCore.App 元包中。

主机与应用配置

在配置并启动应用之前,配置并启动主机。 主机负责应用程序启动和生存期管理。 应用和主机均使用本主题中所述的配置提供程序进行配置。 主机配置键值对成为应用的全局配置的一部分。 有关在构建主机时如何使用配置提供程序以及配置源如何影响主机配置的详细信息,请参阅 ASP.NET Core 中的 Web 主机和通用主机

默认配置

基于 ASP.NET Core dotnet new模板的 Web 应用在生成主机时会调用 CreateDefaultBuilderCreateDefaultBuilder 为应用提供默认配置。

  • 主机配置通过以下方式提供:
    • 使用环境变量配置提供程序,通过前缀为 ASPNETCORE_(例如,ASPNETCORE_ENVIRONMENT)的环境变量提供。
    • 使用命令行配置提供程序,通过命令行参数提供。
  • 应用配置通过以下方式提供(按以下顺序):
    • 使用文件配置提供程序,通过 appsettings.json 提供。
    • 使用文件配置提供程序,通过 appsettings.{Environment}.json 提供。
    • 应用在使用入口程序集的 Development 环境中运行时的机密管理器
    • 使用环境变量配置提供程序,通过环境变量提供。
    • 使用命令行配置提供程序,通过命令行参数提供。

本主题后面将介绍配置提供程序。 有关主机和 CreateDefaultBuilder 的更多信息,请参阅 ASP.NET Core Web 主机

安全性

采用以下最佳实践:

  • 请勿在配置提供程序代码或纯文本配置文件中存储密码或其他敏感数据。
  • 不要在开发或测试环境中使用生产机密。
  • 请在项目外部指定机密,避免将其意外提交到源代码存储库。

详细了解如何使用多个环境和管理使用 Secret Manager 的开发中的应用机密的安全存储(包括使用环境变量存储敏感数据的建议)。 Secret Manager 使用文件配置提供程序将用户机密存储在本地系统上的 JSON 文件中。 本主题后面将介绍文件配置提供程序。

Azure Key Vault 是安全存储应用机密的一种选择。 有关更多信息,请参见在 ASP.NET Core 中的 azure 密钥保管库配置提供程序

分层配置数据

配置 API 能够通过在配置键中使用分隔符来展平分层数据以保持分层配置数据。

在以下 JSON 文件中,两个节的结构化层次结构中存在四个键:

{
  "section0": {
    "key0": "value",
    "key1": "value"
  },
  "section1": {
    "key0": "value",
    "key1": "value"
  }
}

将文件读入配置时,将创建唯一键以保持配置源的原始分层数据结构。 使用冒号 (:) 展平节和键以保持原始结构:

  • section0:key0
  • section0:key1
  • section1:key0
  • section1:key1

GetSectionGetChildren方法可用于隔离各个节和配置数据中某节的子节。 稍后将在GetSection、GetChildren 和 Exists 中介绍这些方法。GetSectionMicrosoft.Extensions.Configuration 包中,后者在 Microsoft.AspNetCore.App 元包中。

约定

在应用启动时,将按照指定的配置提供程序的顺序读取配置源。

应用启动后,在更改基础设置文件时,文件配置提供程序可以重载配置。 本主题后面将介绍文件配置提供程序。

应用的依赖关系注入 (DI)容器中提供了IConfiguration。配置提供程序不能使用 DI,因为主机在设置这些提供程序时 DI 不可用。

配置键采用以下约定:

  • 键不区分大小写。 例如,ConnectionStringconnectionstring 被视为等效键。
  • 如果由相同或不同的配置提供程序设置相同键的值,则键上设置的最后一个值就是所使用的值。
  • 分层键
    • 在配置 API 中,冒号分隔符 (:) 适用于所有平台。
    • 在环境变量中,冒号分隔符可能无法适用于所有平台。 而所有平台均支持采用双下划线 (__),并可以将其转换为冒号。
    • 在 Azure Key Vault 中,分层键使用 --(两个破折号)作为分隔符。 将机密加载到应用的配置中时,必须提供代码以用冒号替换破折号。
  • ConfigurationBinder 支持使用配置键中的数组索引将数组绑定到对象。 数组绑定将在将数组绑定到类部分中进行介绍。

配置值采用以下约定:

  • 值是字符串。
  • NULL 值不能存储在配置中或绑定到对象。

提供程序

下表显示了 ASP.NET Core 应用可用的配置提供程序。

提供程序通过以下对象提供配置…
Azure Key Vault 配置提供程序(安全主题)Azure Key Vault
命令行配置提供程序命令行参数
自定义配置提供程序自定义源
环境变量配置提供程序环境变量
文件配置提供程序文件(INI、JSON、XML)
Key-per-file 配置提供程序目录文件
内存配置提供程序内存中集合
用户机密 (Secret Manager)(安全主题)用户配置文件目录中的文件

按照启动时指定的配置提供程序的顺序读取配置源。 本主题中所述的配置提供程序按字母顺序进行介绍,而不是按代码排列顺序进行介绍。 代码中的配置提供程序应以特定顺序排列以符合基础配置源的优先级。

配置提供程序的典型顺序为:

  1. 文件(appsettings.json、appsettings.{Environment}.json,其中 {Environment} 是应用的当前托管环境)
  2. Azure 密钥保管库
  3. 用户机密 (Secret Manager)(仅限开发环境中)
  4. 环境变量
  5. 命令行参数

通常的做法是将命令行配置提供程序置于一系列提供程序的末尾,以允许命令行参数替代由其他提供程序设置的配置。

在使用 CreateDefaultBuilder初始化新的 WebHostBuilder时,将使用此提供程序序列。 有关详细信息,请参阅Web 主机:设置主机

ConfigureAppConfiguration

构建主机时调用 ConfigureAppConfiguration 以指定应用的配置提供程序以及 CreateDefaultBuilder自动添加的配置提供程序:

public class Program
{
    public static Dictionary<string, string> arrayDict = new Dictionary<string, string>
        {
            {"array:entries:0", "value0"},
            {"array:entries:1", "value1"},
            {"array:entries:2", "value2"},
            {"array:entries:4", "value4"},
            {"array:entries:5", "value5"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddInMemoryCollection(arrayDict);
                config.AddJsonFile("json_array.json", optional: false, reloadOnChange: false);
                config.AddJsonFile("starship.json", optional: false, reloadOnChange: false);
                config.AddXmlFile("tvshow.xml", optional: false, reloadOnChange: false);
                config.AddEFConfiguration(options => options.UseInMemoryDatabase("InMemoryDb"));
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

命令行配置提供程序

CommandLineConfigurationProvider在运行时从命令行参数键值对加载配置。

要激活命令行配置,请在 ConfigurationBuilder的实例上调用 AddCommandLine扩展方法。

使用 CreateDefaultBuilder初始化新的WebHostBuilder时会自动调用 AddCommandLine。 有关详细信息,请参阅Web 主机:设置主机

此外,CreateDefaultBuilder 也会加载:

  • appsettings.jsonappsettings.{Environment}.json 的可选配置。
  • 用户机密 (Secret Manager)(在开发环境中)。
  • 环境变量。

CreateDefaultBuilder 最后添加命令行配置提供程序。 在运行时传递的命令行参数会替代由其他提供程序设置的配置。

CreateDefaultBuilder 在构造主机时起作用。 因此,CreateDefaultBuilder 激活的命令行配置可能会影响主机的配置方式。

构建主机时调用 ConfigureAppConfiguration 以指定应用的配置。

CreateDefaultBuilder 已经调用了 AddCommandLine。 如果需要提供应用配置并仍然能够使用命令行参数覆盖该配置,请在ConfigureAppConfiguration 中调用应用的其他提供程序并最后调用 AddCommandLine

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                // Call other providers here and call AddCommandLine last.
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

直接创建 WebHostBuilder时,请使用以下配置调用UseConfiguration

var config = new ConfigurationBuilder()
    // Call additional providers here as needed.
    // Call AddCommandLine last to allow arguments to override other configuration.
    .AddCommandLine(args)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

示例

2.x 示例应用利用静态便捷方法 CreateDefaultBuilder 来构建主机,其中包括对 AddCommandLine的调用。

  1. 在项目的目录中打开命令提示符。
  2. dotnet run 命令提供命令行参数 dotnet run CommandLineKey=CommandLineValue
  3. 应用运行后,在 http://localhost:5000 打开应用的浏览器。
  4. 观察输出是否包含提供给 dotnet run 的配置命令行参数的键值对。

自变量

该值必须后跟一个等号 (=),否则当值后跟一个空格时,键必须具有前缀(--/)。 如果使用等号(例如,CommandLineKey=),则该值可以为 null

键前缀示例
无前缀CommandLineKey1=value1
双划线 (--)--CommandLineKey2=value2--CommandLineKey2 value2
正斜杠 (/)/CommandLineKey3=value3/CommandLineKey3 value3

在同一命令中,不要将使用等号的命令行参数键值对与使用空格的键值对混合使用。

示例命令:

dotnet run CommandLineKey1=value1 --CommandLineKey2=value2 /CommandLineKey3=value3
dotnet run --CommandLineKey1 value1 /CommandLineKey2 value2
dotnet run CommandLineKey1= CommandLineKey2=value2

交换映射

交换映射支持键名替换逻辑。 使用ConfigurationBuilder手动构建配置时,可以为 AddCommandLine 方法提供交换替换字典。

当使用交换映射字典时,会检查字典中是否有与命令行参数提供的键匹配的键。 如果在字典中找到命令行键,则传回字典值(键替换)以将键值对设置为应用的配置。 对任何具有单划线 (-) 前缀的命令行键而言,交换映射都是必需的。

交换映射字典键规则:

  • 交换必须以单划线 (-) 或双划线 (--) 开头。
  • 交换映射字典不得包含重复键。

构建主机时调用 ConfigureAppConfiguration以指定应用的配置:

public class Program
{
    public static readonly Dictionary<string, string> _switchMappings = 
        new Dictionary<string, string>
        {
            { "-CLKey1", "CommandLineKey1" },
            { "-CLKey2", "CommandLineKey2" }
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    // Do not pass the args to CreateDefaultBuilder
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder()
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddCommandLine(args, _switchMappings);
            })
            .UseStartup<Startup>();
}

如前面的示例所示,当使用交换映射时,对 CreateDefaultBuilder 的调用不应传递参数。 CreateDefaultBuilder 方法的 AddCommandLine 调用不包括映射的交换,并且无法将交换映射字典传递给 CreateDefaultBuilder。 如果参数包含映射的交换并传递给 CreateDefaultBuilder,则其 AddCommandLine 提供程序无法使用 FormatException 进行初始化。 解决方案不是将参数传递给 CreateDefaultBuilder,而是允许 ConfigurationBuilder 方法的 AddCommandLine 方法处理参数和交换映射字典。

创建交换映射字典后,它将包含下表所示的数据。

-CLKey1CommandLineKey1
-CLKey2CommandLineKey2

如果在启动应用时使用了交换映射的键,则配置将接收字典提供的密钥上的配置值:

dotnet run -CLKey1=value1 -CLKey2=value2

运行上述命令后,配置包含下表中显示的值。

CommandLineKey1value1
CommandLineKey2value2

环境变量配置提供程序

EnvironmentVariablesConfigurationProvider在运行时从环境变量键值对加载配置。

要激活环境变量配置,请在 ConfigurationBuilder 的实例上调用AddEnvironmentVariables扩展方法。

在环境变量中使用分层键时,冒号分隔符 (:) 可能无法适用于所有平台。 所有平台均支持采用双下划线 (__),并可以用冒号替换。

借助 Azure 应用服务,用户可以在 Azure 门户中设置使用环境变量配置提供程序替代应用配置的环境变量。 有关详细信息,请参阅Azure 应用:使用 Azure 门户替代应用配置

初始化一个新的 WebHostBuilder时,对于前缀为 ASPNETCORE_ 的环境变量,会自动调用 AddEnvironmentVariables。 有关详细信息,请参阅 Web 主机:设置主机

此外,CreateDefaultBuilder 也会加载:

  • 来自没有前缀的环境变量的应用配置,方法是通过调用不带前缀的 AddEnvironmentVariables
  • appsettings.json 和 appsettings.{Environment}.json 的可选配置。
  • 用户机密 (Secret Manager)(在开发环境中)。
  • 命令行参数。

环境变量配置提供程序是在配置已根据用户机密和 appsettings 文件建立后调用。 在此位置调用提供程序允许在运行时读取的环境变量替代由用户机密和 appsettings 文件设置的配置。

构建主机时调用ConfigureAppConfiguration以指定应用的配置。

如果需要从其他环境变量提供应用配置,请在ConfigureAppConfiguration中调用应用的其他提供程序,并使用前缀调用 AddEnvironmentVariables

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                // Call additional providers here as needed.
                // Call AddEnvironmentVariables last if you need to allow environment
                // variables to override values from other providers.
                config.AddEnvironmentVariables(prefix: "PREFIX_");
            })
            .UseStartup<Startup>();
}

直接创建 WebHostBuilder 时,请使用以下配置调用UseConfiguration

var config = new ConfigurationBuilder()
    .AddEnvironmentVariables()
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

示例
2.x 示例应用利用静态便捷方法 CreateDefaultBuilder 来构建主机,其中包括对 AddEnvironmentVariables 的调用。

  1. 运行示例应用。 在 http://localhost:5000 打开应用的浏览器。
  2. 观察输出是否包含环境变量 ENVIRONMENT 的键值对。 该值反映了应用运行的环境,在本地运行时通常为 Development

为了使应用呈现的环境变量列表简短,应用将环境变量筛选为以下列内容开头的变量:

  • ASPNETCORE_
  • urls
  • 日志记录
  • ENVIRONMENT
  • contentRoot
  • AllowedHosts
  • applicationName
  • CommandLine

如果希望公开应用可用的所有环境变量,请将 Pages/Index.cshtml.cs 中的 FilteredConfiguration 更改为以下内容:

FilteredConfiguration = _config.AsEnumerable();

前缀

AddEnvironmentVariables 方法提供前缀时,将筛选加载到应用的配置中的环境变量。 例如,要筛选前缀 CUSTOM_ 上的环境变量,请将前缀提供给配置提供程序:

var config = new ConfigurationBuilder()
    .AddEnvironmentVariables("CUSTOM_")
    .Build();

创建配置键值对时,将去除前缀。

静态便捷方法 CreateDefaultBuilder 创建一个WebHostBuilder以建立应用的主机。 创建 WebHostBuilder 时,它会在前缀为 ASPNETCORE_ 的环境变量中找到其主机配置。

连接字符串前缀
针对为应用环境配置 Azure 连接字符串所涉及的四个连接字符串环境变量,配置 API 具有特殊的处理规则。 如果没有向 AddEnvironmentVariables 提供前缀,则具有表中所示前缀的环境变量将加载到应用中。

连接字符串前缀提供程序
CUSTOMCONNSTR_自定义提供程序
MYSQLCONNSTR_MySQL
SQLAZURECONNSTR_Azure SQL 数据库
SQLCONNSTR_SQL Server

当发现环境变量并使用表中所示的四个前缀中的任何一个加载到配置中时:

  • 通过删除环境变量前缀并添加配置键节 (ConnectionStrings) 来创建配置键。
  • 创建一个新的配置键值对,表示数据库连接提供程序(CUSTOMCONNSTR_ 除外,它没有声明的提供程序)。
环境变量键转换的配置键提供程序配置条目
CUSTOMCONNSTR_<KEY>ConnectionStrings:<KEY>配置条目未创建。
MYSQLCONNSTR_<KEY>ConnectionStrings:<KEY>键:ConnectionStrings:<KEY>_ProviderName
值:MySql.Data.MySqlClient
SQLAZURECONNSTR_<KEY>ConnectionStrings:<KEY>键:ConnectionStrings:<KEY>_ProviderName
值:System.Data.SqlClient
SQLCONNSTR_<KEY>ConnectionStrings:<KEY>键:ConnectionStrings:<KEY>_ProviderName
值:System.Data.SqlClient

文件配置提供程序

FileConfigurationProvider是从文件系统加载配置的基类。 以下配置提供程序专用于特定文件类型:

  • INI 配置提供程序
  • JSON 配置提供程序
  • XML 配置提供程序

INI 配置提供程序

IniConfigurationProvider 在运行时从 INI 文件键值对加载配置。

若要激活 INI 文件配置,请在 ConfigurationBuilder的实例上调用AddIniFile扩展方法。

冒号可用作 INI 文件配置中的节分隔符。

重载允许指定:

  • 文件是否可选。
  • 如果文件更改,是否重载配置。
  • IFileProvider用于访问该文件。

构建主机时调用 ConfigureAppConfiguration以指定应用的配置:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddIniFile("config.ini", optional: true, reloadOnChange: true);
            })
            .UseStartup<Startup>();
}

基路径使用 SetBasePath设置。 SetBasePathMicrosoft.Extensions.Configuration.FileExtensions 包中,后者在 Microsoft.AspNetCore.App 元包中。

直接创建 WebHostBuilder 时,请使用以下配置调用 UseConfiguration

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddIniFile("config.ini", optional: true, reloadOnChange: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

基路径使用 SetBasePath设置。 SetBasePathMicrosoft.Extensions.Configuration.FileExtensions 包中,后者在 Microsoft.AspNetCore.App 元包中。

INI 配置文件的通用示例:

[section0]
key0=value
key1=value

[section1]
subsection:key=value

[section2:subsection0]
key=value

[section2:subsection1]
key=value

以前的配置文件使用 value 加载以下键:

  • section0:key0
  • section0:key1
  • section1:subsection:key
  • section2:subsection0:key
  • section2:subsection1:key

JSON 配置提供程序

JsonConfigurationProvider在运行时期间从 JSON 文件键值对加载配置。

若要激活 JSON 文件配置,请在ConfigurationBuilder的实例上调用AddJsonFile扩展方法。

重载允许指定:

  • 文件是否可选。
  • 如果文件更改,是否重载配置。
  • IFileProvider用于访问该文件。

使用 CreateDefaultBuilder 初始化新的 WebHostBuilder 时,会自动调用 AddJsonFile 两次。 调用该方法来从以下文件加载配置:

  • appsettings.json – 首先读取此文件。 该文件的环境版本可以替代 appsettings.json 文件提供的值。
  • appsettings.{Environment}.json– 根据 IHostingEnvironment.EnvironmentName加载文件的环境版本。

有关详细信息,请参阅 Web 主机:设置主机

此外,CreateDefaultBuilder 也会加载:

  • 环境变量。
  • 用户机密 (Secret Manager)(在开发环境中)。
  • 命令行参数。

首先建立 JSON 配置提供程序。 因此,用户机密、环境变量和命令行参数会替代由 appsettings 文件设置的配置。

构建主机时调用 ConfigureAppConfiguration以指定除 appsettings.jsonappsettings.{Environment}.json 以外的文件的应用配置:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddJsonFile("config.json", optional: true, reloadOnChange: true);
            })
            .UseStartup<Startup>();
}

基路径使用 SetBasePath设置。 SetBasePathMicrosoft.Extensions.Configuration.FileExtensions 包中,后者在Microsoft.AspNetCore.App 元包中。

直接创建 WebHostBuilder时,请使用以下配置调用 UseConfiguration

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("config.json", optional: true, reloadOnChange: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

基路径使用 SetBasePath设置。 SetBasePathMicrosoft.Extensions.Configuration.FileExtensions 包中,后者在 Microsoft.AspNetCore.App 元包中。

示例
2.x 示例应用利用静态便捷方法 CreateDefaultBuilder 来构建主机,其中包括对 AddJsonFile 的两次调用。 配置从 appsettings.jsonappsettings.{Environment}.json 进行加载。

  1. 运行示例应用。 在 http://localhost:5000 打开应用的浏览器。
  2. 观察输出是否包含表中所示的配置的键值对,具体取决于环境。 记录配置键使用冒号 (:) 作为分层分隔符。
开发值生产值
Logging:LogLevel:System信息信息
Logging:LogLevel:Microsoft信息信息
Logging:LogLevel:Default调试Error
AllowedHosts**

XML 配置提供程序

XmlConfigurationProvider在运行时从 XML 文件键值对加载配置。

若要激活 XML 文件配置,请在 ConfigurationBuilder的实例上调用 AddXmlFile扩展方法。

重载允许指定:

  • 文件是否可选。
  • 如果文件更改,是否重载配置。
  • IFileProvider用于访问该文件。

创建配置键值对时,将忽略配置文件的根节点。 不要在文件中指定文档类型定义 (DTD) 或命名空间。

构建主机时调用 ConfigureAppConfiguration以指定应用的配置:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddXmlFile("config.xml", optional: true, reloadOnChange: true);
            })
            .UseStartup<Startup>();
}

基路径使用 SetBasePath 设置。 SetBasePathMicrosoft.Extensions.Configuration.FileExtensions 包中,后者在Microsoft.AspNetCore.App 元包中。

直接创建 WebHostBuilder时,请使用以下配置调用UseConfiguration

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddXmlFile("config.xml", optional: true, reloadOnChange: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

基路径使用 SetBasePath设置。 SetBasePathMicrosoft.Extensions.Configuration.FileExtensions 包中,后者在 Microsoft.AspNetCore.App 元包中。

XML 配置文件可以为重复节使用不同的元素名称:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <section0>
    <key0>value</key0>
    <key1>value</key1>
  </section0>
  <section1>
    <key0>value</key0>
    <key1>value</key1>
  </section1>
</configuration>

以前的配置文件使用 value 加载以下键:

  • section0:key0
  • section0:key1
  • section1:key0
  • section1:key1

如果使用 name 属性来区分元素,则使用相同元素名称的重复元素可以正常工作:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <section name="section0">
    <key name="key0">value</key>
    <key name="key1">value</key>
  </section>
  <section name="section1">
    <key name="key0">value</key>
    <key name="key1">value</key>
  </section>
</configuration>

以前的配置文件使用 value 加载以下键:

  • section:section0?key0
  • section:section0?key1
  • section:section1?key0
  • section:section1?key1

属性可用于提供值:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <key attribute="value" />
  <section>
    <key attribute="value" />
  </section>
</configuration>

以前的配置文件使用 value 加载以下键:

  • key:attribute
  • section?attribute

Key-per-file 配置提供程序

KeyPerFileConfigurationProvider使用目录的文件作为配置键值对。 该键是文件名。 该值包含文件的内容。 Key-per-file 配置提供程序用于 Docker 托管方案。

若要激活 Key-per-file 配置,请在 ConfigurationBuilder 的实例上调用 AddKeyPerFile扩展方法。 文件的 directoryPath 必须是绝对路径。

重载允许指定:

  • 配置源的 Action<KeyPerFileConfigurationSource> 委托。
  • 目录是否可选以及目录的路径。

双下划线字符 (__) 用作文件名中的配置键分隔符。 例如,文件名 Logging__LogLevel__System 生成配置键 Logging:LogLevel:System

构建主机时调用 ConfigureAppConfiguration以指定应用的配置:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                var path = Path.Combine(Directory.GetCurrentDirectory(), "path/to/files");
                config.AddKeyPerFile(directoryPath: path, optional: true);
            })
            .UseStartup<Startup>();
}

基路径使用 SetBasePath设置。 SetBasePathMicrosoft.Extensions.Configuration.FileExtensions 包中,后者在 Microsoft.AspNetCore.App 元包中。

直接创建 WebHostBuilder时,请使用以下配置调用 UseConfiguration

var path = Path.Combine(Directory.GetCurrentDirectory(), "path/to/files");
var config = new ConfigurationBuilder()
    .AddKeyPerFile(directoryPath: path, optional: true)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

内存配置提供程序

MemoryConfigurationProvider 使用内存中集合作为配置键值对。

若要激活内存中集合配置,请在 ConfigurationBuilder的实例上调用 AddInMemoryCollection扩展方法。

可以使用 IEnumerable<KeyValuePair<String,String>> 初始化配置提供程序。

构建主机时调用 ConfigureAppConfiguration以指定应用的配置:

public class Program
{
    public static readonly Dictionary<string, string> _dict = 
        new Dictionary<string, string>
        {
            {"MemoryCollectionKey1", "value1"},
            {"MemoryCollectionKey2", "value2"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddInMemoryCollection(_dict);
            })
            .UseStartup<Startup>();
}

直接创建 WebHostBuilder 时,请使用以下配置调用 UseConfiguration

var dict = new Dictionary<string, string>
    {
        {"MemoryCollectionKey1", "value1"},
        {"MemoryCollectionKey2", "value2"}
    };

var config = new ConfigurationBuilder()
    .AddInMemoryCollection(dict)
    .Build();

var host = new WebHostBuilder()
    .UseConfiguration(config)
    .UseKestrel()
    .UseStartup<Startup>();

GetValue

ConfigurationBinder.GetValue<T>从具有指定键的配置中提取一个值,并将其转换为指定类型。 如果未找到该键,则过载允许你提供默认值。

以下示例使用键 NumberKey 从配置中提取字符串值,键入该值作为 int,并将值存储在变量 intValue 中。 如果在配置键中找不到 NumberKey,则 intValue 会接收 99 的默认值:

var intValue = config.GetValue<int>("NumberKey", 99);

GetSection、GetChildrenExists

对于下面的示例,请考虑以下 JSON 文件。 在两个节中找到四个键,其中一个包含一对子节:

{
  "section0": {
    "key0": "value",
    "key1": "value"
  },
  "section1": {
    "key0": "value",
    "key1": "value"
  },
  "section2": {
    "subsection0" : {
      "key0": "value",
      "key1": "value"
    },
    "subsection1" : {
      "key0": "value",
      "key1": "value"
    }
  }
}

将文件读入配置时,会创建以下唯一的分层键来保存配置值:

  • section0:key0
  • section0:key1
  • section1:key0
  • section1:key1
  • section2:subsection0:key0
  • section2:subsection0:key1
  • section2:subsection1:key0
  • section2:subsection1:key1

GetSection

IConfiguration.GetSection使用指定的子节键提取配置子节。 GetSectionMicrosoft.Extensions.Configuration 包中,后者在 Microsoft.AspNetCore.App 元包中。

若要返回仅包含 section1 中键值对的 IConfigurationSection,请调用 GetSection 并提供节名称:

var configSection = _config.GetSection("section1");

configSection 不具有值,只有密钥和路径。

同样,若要获取 section2:subsection0 中键的值,请调用 GetSection 并提供节路径:

var configSection = _config.GetSection("section2:subsection0");

GetSection 永远不会返回 null。 如果找不到匹配的节,则返回空 IConfigurationSection

GetSection 返回匹配的部分时,Value未填充。 存在该部分时,返回一个 KeyPath部分。

GetChildren

section2 上调用 IConfiguration.GetChildren 会获得 IEnumerable<IConfigurationSection>,其中包括:

  • subsection0
  • subsection1
var configSection = _config.GetSection("section2");

var children = configSection.GetChildren();

存在

使用 ConfigurationExtensions.Exists确定配置节是否存在:

var sectionExists = _config.GetSection("section2:subsection2").Exists();

给定示例数据,sectionExistsfalse,因为配置数据中没有 section2:subsection2 节。

绑定至类

可以使用选项模式将配置绑定到表示相关设置组的类。 有关更多信息,请参见ASP.NET Core 中的选项模式

配置值作为字符串返回,但调用 Bind可以构造 POCO 对象。 BindMicrosoft.Extensions.Configuration.Binder 包中,后者在 Microsoft.AspNetCore.App 元包中。

示例应用包含 Starship 模型 (Models/Starship.cs):

public class Starship
{
    public string Name { get; set; }
    public string Registry { get; set; }
    public string Class { get; set; }
    public decimal Length { get; set; }
    public bool Commissioned { get; set; }
}

当示例应用使用 JSON 配置提供程序加载配置时,starship.json 文件的 starship 节会创建配置:

{
  "starship": {
    "name": "USS Enterprise",
    "registry": "NCC-1701",
    "class": "Constitution",
    "length": 304.8,
    "commissioned": false
  },
  "trademark": "Paramount Pictures Corp. http://www.paramount.com"
}

创建以下配置键值对:

starship:nameUSS Enterprise
starship:registryNCC-1701
starship:classConstitution
starship:length304.8
starship:commissionedFalse
trademarkParamount Pictures Corp. http://www.paramount.com

示例应用使用 starship 键调用 GetSectionstarship 键值对是独立的。 在子节传入 Starship 类的实例时调用 Bind 方法。 绑定实例值后,将实例分配给用于呈现的属性:

var starship = new Starship();
_config.GetSection("starship").Bind(starship);
Starship = starship;

GetSectionMicrosoft.Extensions.Configuration 包中,后者在Microsoft.AspNetCore.App 元包中。

绑定至对象图

Bind能够绑定整个 POCO 对象图。 BindMicrosoft.Extensions.Configuration.Binder 包中,后者在Microsoft.AspNetCore.App 元包中。

该示例包含 TvShow 模型,其对象图包含 MetadataActors 类 (Models/TvShow.cs):

public class TvShow
{
    public Metadata Metadata { get; set; }
    public Actors Actors { get; set; }
    public string Legal { get; set; }
}

public class Metadata
{
    public string Series { get; set; }
    public string Title { get; set; }
    public DateTime AirDate { get; set; }
    public int Episodes { get; set; }
}

public class Actors
{
    public string Names { get; set; }
}

示例应用有一个包含配置数据的 tvshow.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <tvshow>
    <metadata>
      <series>Dr. Who</series>
      <title>The Sun Makers</title>
      <airdate>11/26/1977</airdate>
      <episodes>4</episodes>
    </metadata>
    <actors>
      <names>Tom Baker, Louise Jameson, John Leeson</names>
    </actors>
    <legal>(c)1977 BBC https://www.bbc.co.uk/programmes/b006q2x0</legal>
  </tvshow>
</configuration>

使用 Bind 方法将配置绑定到整个 TvShow 对象图。 将绑定实例分配给用于呈现的属性:

var tvShow = new TvShow();
_config.GetSection("tvshow").Bind(tvShow);
TvShow = tvShow;

ConfigurationBinder.Get<T> 绑定并返回指定类型。 Get<T> 比使用 Bind 更方便。 以下代码显示如何将 Get<T> 与前面的示例一起使用,该示例允许将绑定实例直接分配给用于呈现的属性:

TvShow = _config.GetSection("tvshow").Get<TvShow>();

GetMicrosoft.Extensions.Configuration.Binder 包中,后者在Microsoft.AspNetCore.App 元包中。 ASP.NET Core 1.1 或更高版本中提供了 Get<T>GetSectionMicrosoft.Extensions.Configuration 包中,后者在Microsoft.AspNetCore.App 元包中。

将数组绑定至类

示例应用演示了本部分中介绍的概念。

Bind支持使用配置键中的数组索引将数组绑定到对象。 公开数字键段(:0::1:、… :{n}:)的任何数组格式都能够与 POCO 类数组进行数组绑定。 BindMicrosoft.Extensions.Configuration.Binder 包中,后者在 Microsoft.AspNetCore.App 元包中。

备注
绑定是按约定提供的。 不需要自定义配置提供程序实现数组绑定。

内存中数组处理

请考虑下表中所示的配置键和值。

array:entries:0value0
array:entries:1value1
array:entries:2value2
array:entries:4value4
array:entries:5value5

使用内存配置提供程序在示例应用中加载这些键和值:

public class Program
{
    public static Dictionary<string, string> arrayDict = new Dictionary<string, string>
        {
            {"array:entries:0", "value0"},
            {"array:entries:1", "value1"},
            {"array:entries:2", "value2"},
            {"array:entries:4", "value4"},
            {"array:entries:5", "value5"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddInMemoryCollection(arrayDict);
                config.AddJsonFile("json_array.json", optional: false, reloadOnChange: false);
                config.AddJsonFile("starship.json", optional: false, reloadOnChange: false);
                config.AddXmlFile("tvshow.xml", optional: false, reloadOnChange: false);
                config.AddEFConfiguration(options => options.UseInMemoryDatabase("InMemoryDb"));
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

该数组跳过索引 #3 的值。 配置绑定程序无法绑定 null 值,也无法在绑定对象中创建 null 条目,这在演示将此数组绑定到对象的结果时变得清晰。

在示例应用中,POCO 类可用于保存绑定的配置数据:

public class ArrayExample
{
    public string[] Entries { get; set; }
}

将配置数据绑定至对象:

var arrayExample = new ArrayExample();
_config.GetSection("array").Bind(arrayExample);

GetSectionMicrosoft.Extensions.Configuration 包中,后者在Microsoft.AspNetCore.App 元包中。

还可以使用 ConfigurationBinder.Get<T> 语法,从而产生更精简的代码:

ArrayExample = _config.GetSection("array").Get<ArrayExample>();

绑定对象(ArrayExample 的实例)从配置接收数组数据。

ArrayExample.Entries 索引ArrayExample.Entries
0value0
1value1
2value2
3value4
4value5

绑定对象中的索引 #3 保留 array:4 配置键的配置数据及其值 value4。 当绑定包含数组的配置数据时,配置键中的数组索引仅用于在创建对象时迭代配置数据。 无法在配置数据中保留 null 值,并且当配置键中的数组跳过一个或多个索引时,不会在绑定对象中创建 null 值条目。

可以在由任何在配置中生成正确键值对的配置提供程序绑定到 ArrayExample 实例之前提供索引 #3 的缺失配置项。 如果示例包含具有缺失键值对的其他 JSON 配置提供程序,则 ArrayExample.Entries 与完整配置数组相匹配:

missing_value.json:

{
  "array:entries:3": "value3"
}

ConfigureAppConfiguration中:

config.AddJsonFile("missing_value.json", optional: false, reloadOnChange: false);

将表中所示的键值对加载到配置中。

array:entries:3value3

如果在 JSON 配置提供程序包含索引 #3 的条目之后绑定 ArrayExample 类实例,则 ArrayExample.Entries 数组包含该值。

ArrayExample.Entries 索引ArrayExample.Entries
0value0
1value1
2value2
3value3
4value4
5value5

JSON 数组处理

如果 JSON 文件包含数组,则会为具有从零开始的节索引的数组元素创建配置键。 在以下配置文件中,subsection 是一个数组:

{
  "json_array": {
    "key": "valueA",
    "subsection": [
      "valueB",
      "valueC",
      "valueD"
    ]
  }
}

JSON 配置提供程序将配置数据读入以下键值对:

json_array:keyvalueA
json_array:subsection:0valueB
json_array:subsection:1valueC
json_array:subsection:2valueD

在示例应用中,以下 POCO 类可用于绑定配置键值对:

public class JsonArrayExample
{
    public string Key { get; set; }
    public string[] Subsection { get; set; }
}

绑定后,JsonArrayExample.Key 保存值 valueA。 子节值存储在 POCO 数组属性 Subsection 中。

JsonArrayExample.Subsection 索引JsonArrayExample.Subsection
0valueB
1valueC
2valueD

自定义配置提供程序

该示例应用演示了如何使用实体框架 (EF)创建从数据库读取配置键值对的基本配置提供程序。

提供程序具有以下特征:

  • EF 内存中数据库用于演示目的。 若要使用需要连接字符串的数据库,请实现辅助 ConfigurationBuilder 以从另一个配置提供程序提供连接字符串。
  • 提供程序在启动时将数据库表读入配置。 提供程序不会基于每个键查询数据库。
  • 未实现更改时重载,因此在应用启动后更新数据库对应用的配置没有任何影响。

定义用于在数据库中存储配置值的 EFConfigurationValue 实体。

Models/EFConfigurationValue.cs

public class EFConfigurationValue
{
    public string Id { get; set; }
    public string Value { get; set; }
}

添加 EFConfigurationContext 以存储和访问配置的值。

EFConfigurationProvider/EFConfigurationContext.cs

public class EFConfigurationContext : DbContext
{
    public EFConfigurationContext(DbContextOptions options) : base(options)
    {
    }

    public DbSet<EFConfigurationValue> Values { get; set; }
}

创建用于实现 xref:Microsoft.Extensions.Configuration.IConfigurationSource 的类。

EFConfigurationProvider/EFConfigurationSource.cs

public class EFConfigurationSource : IConfigurationSource
{
    private readonly Action<DbContextOptionsBuilder> _optionsAction;

    public EFConfigurationSource(Action<DbContextOptionsBuilder> optionsAction)
    {
        _optionsAction = optionsAction;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new EFConfigurationProvider(_optionsAction);
    }
}

通过从 ConfigurationProvider继承来创建自定义配置提供程序。 当数据库为空时,配置提供程序将对其进行初始化。

EFConfigurationProvider/EFConfigurationProvider.cs

public class EFConfigurationProvider : ConfigurationProvider
{
    public EFConfigurationProvider(Action<DbContextOptionsBuilder> optionsAction)
    {
        OptionsAction = optionsAction;
    }

    Action<DbContextOptionsBuilder> OptionsAction { get; }

    // Load config data from EF DB.
    public override void Load()
    {
        var builder = new DbContextOptionsBuilder<EFConfigurationContext>();

        OptionsAction(builder);

        using (var dbContext = new EFConfigurationContext(builder.Options))
        {
            dbContext.Database.EnsureCreated();

            Data = !dbContext.Values.Any()
                ? CreateAndSaveDefaultValues(dbContext)
                : dbContext.Values.ToDictionary(c => c.Id, c => c.Value);
        }
    }

    private static IDictionary<string, string> CreateAndSaveDefaultValues(
        EFConfigurationContext dbContext)
    {
        // Quotes (c)2005 Universal Pictures: Serenity
        // https://www.uphe.com/movies/serenity
        var configValues = new Dictionary<string, string>
            {
                { "quote1", "I aim to misbehave." },
                { "quote2", "I swallowed a bug." },
                { "quote3", "You can't stop the signal, Mal." }
            };

        dbContext.Values.AddRange(configValues
            .Select(kvp => new EFConfigurationValue 
                {
                    Id = kvp.Key,
                    Value = kvp.Value
                })
            .ToArray());

        dbContext.SaveChanges();

        return configValues;
    }
}

可以使用 AddEFConfiguration 扩展方法将配置源添加到 ConfigurationBuilder

Extensions/EntityFrameworkExtensions.cs:

public class EFConfigurationSource : IConfigurationSource
{
    private readonly Action<DbContextOptionsBuilder> _optionsAction;

    public EFConfigurationSource(Action<DbContextOptionsBuilder> optionsAction)
    {
        _optionsAction = optionsAction;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new EFConfigurationProvider(_optionsAction);
    }
}

下面的代码演示如何在 Program.cs 中使用自定义的 EFConfigurationProvider

public class Program
{
    public static Dictionary<string, string> arrayDict = new Dictionary<string, string>
        {
            {"array:entries:0", "value0"},
            {"array:entries:1", "value1"},
            {"array:entries:2", "value2"},
            {"array:entries:4", "value4"},
            {"array:entries:5", "value5"}
        };

    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.SetBasePath(Directory.GetCurrentDirectory());
                config.AddInMemoryCollection(arrayDict);
                config.AddJsonFile("json_array.json", optional: false, reloadOnChange: false);
                config.AddJsonFile("starship.json", optional: false, reloadOnChange: false);
                config.AddXmlFile("tvshow.xml", optional: false, reloadOnChange: false);
                config.AddEFConfiguration(options => options.UseInMemoryDatabase("InMemoryDb"));
                config.AddCommandLine(args);
            })
            .UseStartup<Startup>();
}

在启动期间访问配置

IConfiguration 注入 Startup 构造函数以访问 Startup.ConfigureServices 中的配置值。 若要访问 Startup.Configure 中的配置,请将 IConfiguration 直接注入方法或使用构造函数中的实例:

public class Startup
{
    private readonly IConfiguration _config;

    public Startup(IConfiguration config)
    {
        _config = config;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        var value = _config["key"];
    }

    public void Configure(IApplicationBuilder app, IConfiguration config)
    {
        var value = config["key"];
    }
}

有关使用启动便捷方法访问配置的示例,请参阅应用启动:便捷方法

在 Razor Pages 页或 MVC 视图中访问配置

若要访问 Razor Pages 页或 MVC 视图中的配置设置,请为Microsoft.Extensions.Configuration 命名空间添加using 指令C# 参考:using 指令)并将 IConfiguration注入页面或视图。

在 Razor 页面页中:

@page
@model IndexModel
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index Page</title>
</head>
<body>
    <h1>Access configuration in a Razor Pages page</h1>
    <p>Configuration value for 'key': @Configuration["key"]</p>
</body>
</html>

在 MVC 视图中:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index View</title>
</head>
<body>
    <h1>Access configuration in an MVC view</h1>
    <p>Configuration value for 'key': @Configuration["key"]</p>
</body>
</html>

从外部程序集添加配置

通过 IHostingStartup 实现,可在启动时从应用 Startup 类之外的外部程序集向应用添加增强功能。 有关更多信息,请参见在 ASP.NET Core 中使用承载启动程序集

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ASP.NET Core是一个跨平台的开源框架,用于构建Web应用程序。它是ASP.NET的下一代版本,具有更高的性能、更好的可测试性和更大的灵活性。 ASP.NET Core具有以下基础知识: 1. 架构:ASP.NET Core采用了模块化的架构,称为中间件(Middleware)。中间件可以按照需求进行配置和组合,实现各种功能,如路由、身份验证、日志记录等。 2. 跨平台:与传统的ASP.NET不同,ASP.NET Core可以在Windows、Linux和macOS等多个平台上运行。 3. 环境:ASP.NET Core引入了环境概念,可以根据不同的环境(开发、生产等)进行配置和部署。 4. 依赖注入:ASP.NET Core内置了依赖注入容器,可以轻松地进行依赖注入,提高代码的可测试性和可维护性。 5. Razor Pages:Razor Pages是一种新的页面模型,使得开发者可以将HTML标记和后端逻辑组合在一起,简化了开发过程。 6. Web API:ASP.NET Core提供了强大的Web API支持,可以轻松地构建RESTful风格的API。 7. 身份验证和授权:ASP.NET Core提供了丰富的身份验证和授权机制,包括基于Cookie的身份验证、JWT身份验证等。 8. 数据访问:ASP.NET Core可以与各种数据库进行交互,支持多种数据访问技术,如Entity Framework Core、Dapper等。 9. 部署和托管:ASP.NET Core应用程序可以以自包含的方式进行部署,可以通过IIS、Docker等进行托管。 以上是ASP.NET Core基础知识,希望对你有所帮助!如果你有更多问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值