Azure 函数 - 使用 appsettings.json

Azure Functions - using appsettings.json

是否可以在 Azure Functions 中使用 appsettings.json 文件?

这里有环境变量的文档..

https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#environment-variables

..但是我们使用 Octopus 进行部署,并且真的很想控制应用程序设置的版本。

我们已经尝试使用

{
  "frameworks": {
    "net46": {
      "dependencies": {
        "Microsoft.Extensions.Configuration": "1.0.0",
        "Microsoft.Extensions.Configuration.Json": "1.0.0"
      }
    }
  }
}

但不断得到

2016-11-23T15:27:03.811 (12,16):错误 CS0012:类型 'Object' 在未引用的程序集中定义。您必须添加对程序集 'System.Runtime, Version=4.0.0.0

的引用

即使能够通过 Octopus supply/update 环境变量也足以满足我们的需求。

请指教

应用程序设置和连接字符串仅支持环境变量。 appsettings.json 不支持。

但是,您可以使用 Azure 资源管理器 (ARM) 模板来配置 Function App 的设置。这里有一个 blog post 更详细地描述了这一点。

对于依赖项,您应该在函数中 use/create project.json。在那里你可以指定你的依赖项。 请检查: https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-csharp#package-management

例如:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.ProjectOxford.Face": "1.1.0"
      }
    }
   }
}

根据您的需要,答案是肯定的! Azure Functions 可以使用 appsettings.json 进行配置。但是当请求函数时,Azure 会执行一些排序顺序。

1º) Azure 将查找您在 .GetEnvironmentVariables("[KEY]" 上使用的 KEYS ) 方法,通过在 Azure Functions 设置中的应用程序设置边栏选项卡上配置的密钥

2º) 如果 Azure 无法通过应用程序设置键找到该配置,那么 Azure 将尝试在您的根文件夹中查找 appsettings.json 文件您正在处理的功能。

3º) 最后,如果 Azure 在应用程序设置和 appsettings.json 文件中都无法找到此密钥,则 Azure 将进行最后一次尝试以查找 web.config对于此文件 appsettings section keys.

感谢您,您可以通过我的 github 存储库中的示例找到这些配置:here and here

希望这些信息对您有所帮助。

根据对配置文件所做的更改,您应该只使用 local.settings.json,因为 appsettings.json 已重命名为 local.settings.json

更改参考: azure-functions-cli

在 Azure Functions 中,设置存储在 local.settings.json 中(如果您的解决方案中不存在,请创建此文件/名称应与提到的完全一致)。

添加设置文件后,您必须按照下面提到的 运行() 方法对其进行配置,

访问设置时,使用下面

IConfigurationRoot config;
config["fromEmail"];

使用以下命令发布设置

func azure functionapp publish *YourAppName* --publish-local-settings -i

配置源自定义从 Azure Functions 主机版本 2.0.14192.0 和 3.0.14191.0 开始可用。

要指定其他配置源,请在函数应用的 StartUp 中覆盖 ConfigureAppConfiguration 方法 class。

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
    public class Startup : FunctionsStartup
    {
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder 
builder)
        {
            FunctionsHostBuilderContext context = builder.GetContext();

            builder.ConfigurationBuilder
                .AddJsonFile(Path.Combine(context.ApplicationRootPath, 
"appsettings.json"), optional: true, reloadOnChange: false)
                .AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings. 
{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
                .AddEnvironmentVariables();
        }
    }
}

//更新csproject中的配置

<None Update="appsettings.json">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>      
</None>

<None Update="appsettings">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>

而不是每次都使用配置,您可以在需要时注入选项 class,如下所示。 在 Startup.Configure 方法内部,您可以使用以下代码将 IConfiguration 实例中的值提取到您的自定义类型中:

builder.Services.AddOptions<MyOptions>()
    .Configure<IConfiguration>((settings, configuration) =>
    {
        configuration.GetSection("MyOptions").Bind(settings);
    });

函数class:

using System;
using Microsoft.Extensions.Options;

public class HttpTrigger
{
    private readonly MyOptions _settings;

    public HttpTrigger(IOptions<MyOptions> options)
    {
        _settings = options.Value;
    }
}

参考:https://docs.microsoft.com/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources

我们可以尝试和测试的方法:

  1. 在您的函数应用程序项目中创建一个 appsettings.json
  2. 您需要在继承自 FunctionsStartup class 的项目中添加 Startup.cs class。这将公开一个用于覆盖的方法 Configure(IFunctionsHostBuilder builder)。
  3. 为了控制自定义级别,我会说扩展 IFunctionsHostBuilder class 假设它是 (IFunctionsHostBuilderExtensions.cs) 并添加一个扩展方法以向其添加新的配置生成器。这给了我们控制我们想要如何在运行时配置 appsettings 设置的好处。
  4. 完成后,您可以通过传递 appsettings 的整个文件路径或仅传递名称来调用新创建的扩展方法。(您可以在扩展方法中为 appsettings 配置基本路径,以避免在代码)。
  5. 构建扩展方法后,您将获得可用于注入的服务,即 IConfiguration,可在代码库中的任何地方使用。
  6. 您还可以为应用程序设置添加多个提供程序,例如 Azure Key Vault、AWS Secret Manager 等。同样,您需要做的就是在 IFunctionsHostBuilderExtensions class 中添加一个扩展方法并调用它们在你的创业公司 class.
  7. 如果你想让事情更整洁,你可以围绕 IConfiguration 服务实现一个包装器来公开一个 GetSettings(string key) 方法,该方法将 return您希望从 IConfiguration 中的提供程序集中集合获得的设置。

下面是一些代码片段:

/// <summary>
///     Represents the startup class of the function app.
/// </summary>
public class Startup : FunctionsStartup
{
    private const string LocalSettingFileGenericName = "appsettings";
    private const string LocalSettingFileExtension = "json";

    /// <summary>
    ///     Configures the host builder for the function app.
    /// </summary>
    /// <param name="builder">The function app host builder.</param>
    public override void Configure(IFunctionsHostBuilder builder)
    {
        try
        {
            builder.AddConfiguration((configurationBuilder) =>
            {
                var configuration = typeof(Startup).Assembly.GetCustomAttribute<AssemblyConfigurationAttribute>().Configuration;

                var configurationFileName = !string.Equals(configuration, "Release")
                    ? $"{LocalSettingFileGenericName}.{configuration.ToLowerInvariant()}.{LocalSettingFileExtension}"
                    : $"{LocalSettingFileGenericName}.{LocalSettingFileExtension}";

                var configurationSource = configurationBuilder
                    .AddJsonFile(configurationFileName, false, true)
                    .AddEnvironmentVariables();

                var partialConfigurationBuilder = configurationSource.Build();

                var keyVaultName = partialConfigurationBuilder.GetSection(ConfigurationKeys.KeyvaultName)?.Value;

                return configurationSource
                       .AddKeyVaultWithManagedIdentity(keyVaultName)
                       .Build();
            });

IFunctionBuilderExtensions.cs

 internal static class IFunctionsHostBuilderConfigurationsExtensions
{
    private const string keyVaultGenericUri = "https://{0}.vault.azure.net/";

    /// <summary>
    ///     Provides an extension method to add configuration provider.
    /// </summary>
    /// <param name="builder">The function app host builder.</param>
    /// <param name="configBuilderFunc">The delegate to pointing to configuration builder.</param>
    /// <returns>The function app host builder</returns>
    public static IFunctionsHostBuilder AddConfiguration(
        this IFunctionsHostBuilder builder,
        Func<IConfigurationBuilder, IConfiguration> configBuilderFunc)
    {
        var configurationBuilder = builder.GetBaseConfigurationBuilder();

        var configuration = configBuilderFunc(configurationBuilder);

        builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));

        return builder;
    }

    /// <summary>
    ///     Provides an extension method to add Azure Key Vault as a configuration provider.
    /// </summary>
    /// <param name="builder">The configuration builder.</param>
    /// <param name="keyvaultName">The azure key vault name.</param>
    /// <param name="authenticationClientId">The AAD application clientId.</param>
    /// <param name="authenticationClientSecret">The AAD application clientSecret.</param>
    /// <returns>The configuration builder.</returns>
    public static IConfigurationBuilder AddKeyVaultWithManagedIdentity(
        this IConfigurationBuilder builder,
        string keyvaultName)
    {
        if (string.IsNullOrWhiteSpace(keyvaultName))
        {
            return builder;
        }

        var serviceTokenProvider = new AzureServiceTokenProvider();
        var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(serviceTokenProvider.KeyVaultTokenCallback));

        var keyVaultUri = string.Format(keyVaultGenericUri, keyvaultName);

        builder.AddAzureKeyVault(
            keyVaultUri,
            keyVaultClient,
            new DefaultKeyVaultSecretManager());

        return builder;
    }

    private static IConfigurationBuilder GetBaseConfigurationBuilder(this IFunctionsHostBuilder builder)
    {
        var configurationBuilder = new ConfigurationBuilder();

        var descriptor = builder.Services.FirstOrDefault(
            service => service.ServiceType == typeof(IConfiguration));

        if (descriptor?.ImplementationInstance is IConfiguration configRoot)
        {
            configurationBuilder.AddConfiguration(configRoot);
        }

        var rootConfigurationBuilder = configurationBuilder.SetBasePath(GetCurrentDirectory());

        return rootConfigurationBuilder;
    }

    private static string GetCurrentDirectory()
    {
        var currentDirectory = Path.GetDirectoryName(
            Assembly.GetExecutingAssembly().Location);

        return currentDirectory.Replace("bin", "{Your settings directory}");
    }

包装器实现示例:

  /// <summary>
///     Represents the configuration settings provider class.
/// </summary>
public class ConfigurationSettings : IConfigurationSettings
{
    private readonly IConfiguration configurationSource;

    /// <summary>
    ///     Initializes the class of type <see cref="ConfigurationSettings"/>.
    /// </summary>
    /// <param name="configurationSource">The configuration source.</param>
    public ConfigurationSettings(
        IConfiguration configurationSource)
    {
        this.configurationSource = configurationSource;
    }

    ///<inheritdoc/>
    public T GetSetting<T>(string key)
    {
        try
        {
            if (!configurationSource.GetSection(key).Exists())
            {
                throw new ConfigurationDoesNotExistException(
                    $"The configuration with key {key} does not exist in appsetting or key vault.");
            }

            return (T)Convert.ChangeType(configurationSource.GetSection(key)?.Value, typeof(T));
        }
        catch (InvalidCastException)
        {
            throw;
        }
        catch (Exception)
        {
            throw;
        }
    }
}