简体   繁体   中英

ASP.NET Core web service does not load appsettings.json into configuration

I have an ASP.NET Core 2.1 Web Application with Razor Pages which has AAD authentication information defined in the appsettings.json file (courtesy of the default application template - see below on how I got there). However, when trying to configure the authentication in Startup.cs the configuration does not have any of the config values from my appsettings.json . If I inspect the IConfiguration object in the debugger then it appears to only have the environment variable configurations:

在此处输入图片说明

Here's the Startup.ConfigureServices method where the issue lies:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        .AddAzureAD(options =>
        {
            // This is from the default template. It should work, but the relevant settings aren't there so options isn't populated.
            this.Configuration.Bind("AzureAd", options);

            // This of course works fine
            options.Instance = "MyInstance";
            options.Domain = "MyDomain";
            options.TenantId = "MyTenantId";
            options.ClientId = "MyClientId";
            options.CallbackPath = "MyCallbackPath";
        });

    services.AddMvc(options =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        options.Filters.Add(new AuthorizeFilter(policy));
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

And the service configuration in case it's important (note that this is being built on top of a service fabric stateless service):

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

                return new WebHostBuilder()
                            .UseKestrel(opt =>
                            {
                                int port = serviceContext.CodePackageActivationContext.GetEndpoint("ServiceEndpoint").Port;
                                opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
                                {
                                    listenOptions.UseHttps(GetCertificateFromStore());
                                    listenOptions.NoDelay = true;
                                });
                            })
                            .ConfigureServices(
                                services => services
                                    .AddSingleton<StatelessServiceContext>(serviceContext))
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseStartup<Startup>()
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                            .UseUrls(url)
                            .Build();
            }))
    };
}

To create this service, I used the wizard in VS2017. I selected an existing service fabric project ( .sfproj ) and chose Services > Add > New Service Fabric Service and chose Stateless ASP.NET Core [for .NET Framework] , then on the next page I chose Web Application (the one with Razor Pages, not MVC) and clicked Change Authentication where I chose Work or School Accounts and entered my AAD info. The only changes I have made to this template were adding the code inside the call to AddAzureAD in Startup.ConfigureServices and setting the appsettings.json files to always be copied to the output directory.

Why doesn't the appsettings.json file get loaded into the configuration? As I understand, this is supposed to happen by default, but something seems to be missing...

WebHostBuilder doesn't load appsettings.json by default, you need to manually call AddJsonFile . For example:

return new WebHostBuilder()
            .UseKestrel(opt =>
            {
                //snip
            })
            .ConfigureAppConfiguration((builderContext, config) =>
            {
                config.AddJsonFile("appsettings.json", optional: false);
            })
            .ConfigureServices(
                services => services
                    .AddSingleton<StatelessServiceContext>(serviceContext))
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
            .UseUrls(url)
            .Build();

Alternatively you can use WebHost.CreateDefaultBuilder which will load more defaults.

Another approach, would be to manually create the configuration via ConfigurationBuilder then use the UseConfiguration method.

var configuration = new ConfigurationBuilder()
     .SetBasePath(Directory.GetCurrentDirectory())
     .AddJsonFile("appsettings.json", false, true)
     .Build();

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

The primary intent is core to provide a bit of flexibility when implementing, they often error on less is more. You have to explicitly say what you would like, that way the pipeline remains relatively small.

As mentioned before WebHostBuilder do not execute this default behavior CreateDefaultBuilder needs to be called instead.

I prefer following implementation:

public static void Main(string[] args)
{
     var host = CreateHostBuilder(args).Build();
     var logger = host.Services.GetRequiredService<ILogger<Program>>();

     try
     {
         logger.LogInformation("Starting up");
                
         host.Run();
      }
      catch (Exception ex)
      {
         logger.LogCritical(ex, "Application start-up failed");
      }
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

For others like me who find this issue:

It might be that you're not copying the appsettings.json file during build.

The OP does say he's doing this, but it's kind of a small print thing - and was what I was failing to do.

The more you know ...

Below mentioned steps worked for me

  1. Go to Appsettings.json
  2. Right click and goto properties
  3. select the build action from the drop down to none if its content
  4. Make the copy to Output directory as Copy Always

Below mentioned steps worked for me

  1. Go to Appsettings.json
  2. Right click and goto properties
  3. select the build action from the drop down to none if its content
  4. Make the copy to Output directory as Copy Always

Make sure your file name is all lower case.

My mistake was to name the file appSettings.json instead of appsettings.json . When running within a Linux container, the camel-cased file was not loaded.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM