How does a C# Api read the appsettings.json? - c#

I wonder how the startup.cs knows of the appsettings.json file, I can't seem to find a setting in the startup.cs or program.cs of the api.
Standard program.cs:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace WebApplication1
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Standard startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace WebApplication1
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
I tried to add appsettings.json to a console app but then I had to do the injection of the file manually. With this piece of code:
configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
.AddJsonFile("appsettings.json", false)
.Build();

The application knows about these appsettings files thanks to the call to the Host.CreateDefaultBuilder method in program.cs .
As mentioned here in the ASP.NET Core documentation, this method enables some default behavior, such as loading app configuration from:
appsettings.json.
appsettings.{Environment}.json.
User secrets when the app runs in the Development environment.
Environment variables.
Command-line arguments.

Related

How to use startup class inside program file on .NET 6?

I working on an ASP.NET Core 2.2 web application. I have some issues when upgrade my application to .NET 6.
My issue is that there's no startup class in .NET 6.0 and I found program.cs file only.
I add startup class on my web application but I don't know how to use it inside Program.cs.
How to add or use startup class inside my program.cs?
This is the startup.cs file in .NET Core 2.2:
public class Startup
{
private readonly IConfigurationRoot configRoot;
private AppSettings AppSettings { get; set; }
public Startup(IConfiguration configuration)
{
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
Configuration = configuration;
IConfigurationBuilder builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");
configRoot = builder.Build();
AppSettings = new AppSettings();
Configuration.Bind(AppSettings);
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddController();
services.AddDbContext(Configuration, configRoot);
services.AddIdentityService(Configuration);
services.AddAutoMapper();
services.AddScopedServices();
services.AddTransientServices();
services.AddSwaggerOpenAPI();
services.AddMailSetting(Configuration);
services.AddServiceLayer();
services.AddVersion();
services.AddHealthCheck(AppSettings, Configuration);
services.AddFeatureManagement();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory log)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(options =>
options.WithOrigins("http://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod());
app.ConfigureCustomExceptionMiddleware();
log.AddSerilog();
//app.ConfigureHealthCheck();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.ConfigureSwagger();
app.UseHealthChecks("/healthz", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
ResultStatusCodes =
{
[HealthStatus.Healthy] = StatusCodes.Status200OK,
[HealthStatus.Degraded] = StatusCodes.Status500InternalServerError,
[HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable,
},
}).UseHealthChecksUI(setup =>
{
setup.ApiPath = "/healthcheck";
setup.UIPath = "/healthcheck-ui";
//setup.AddCustomStylesheet("Customization/custom.css");
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
And this is my .NET 6 program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
How to use the startup class inside program.cs class ?
Updated Post
every thing is working but configure service not working
because i don't know how to implement ILoggerFactory
on startup
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory log)
{
}
on program.cs
startup.Configure(app, app.Environment,???);
How to add logger factory as third paramter on program.cs
ILoggerFactory is buit in class
Updated it solved using
var app = builder.Build();
startup.Configure(
app,
builder.Environment,
app.Services.GetRequiredService<FooService>(),
app.Services.GetRequiredService<ILoggerFactory>()
);
can you please tell me how to apply swagger ui to check my api
New templates use the so called minimal hosting model but nobody prevents from switching back to the generic hosting one used previously (or via WebHost).
If you want to work with top-level statements you can copy contents of Main method to the Program.cs file and then copy all other methods declared in the old Program class. New Program.cs potentially can look something like this:
await CreateHostBuilder(args)
.Build()
.RunAsync();
// do not forget to copy the rest of the setup if any
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Or just remove the Startup class completely and move configure methods to corresponding parts of new file (maybe extracting some to concise extension methods).
You can manually instantiate the Startup and manually call the method ConfigureServices and Configure :
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
startup.Configure(app, builder.Environment);
In ASP.NET Core 2.*, Startup.Configure accepted injected service :
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IFooService, FooService>();
}
public void Configure(WebApplication app, IWebHostEnvironment env, IFooService fooService, ILoggerFactory loggerFactory)
{
fooService.Init();
...
}
}
Then you can :
var app = builder.Build();
startup.Configure(
app,
builder.Environment,
app.Services.GetRequiredService<FooService>(),
app.Services.GetRequiredService<ILoggerFactory>()
);
When I migrated my APIs, first I consider to reuse the Startup class... but finally I moved the configuration in extension methods.

How does it Access appsettings from this c# Class Library Project?

I saw the sample ASP.NET core Web Api at following website:
https://codewithmukesh.com/blog/dapper-in-aspnet-core/
It uses the dapper for accessing SQL database with 3 class library projects. In the c# class library project named "Dapper.Infrasstructure", the ProductRepository.cs program obtains the SQL connection string from appsettings.cshtml with code shown in CODE SEGMENT 1. It also uses IServiceCollection for registering services in setup.cs (see CODE SEGMENT 2). The library project includes three packages (See CODE SEGMENT 3).
Since there is no appsettings file in any class library. how does it access the settings that is in the main project "Dapper.WebApi"? The program was build with ASP.NET core 3.1. I compiled it using .NET 5. It wors just fine. I also checked the content of its setup.cs file (see CODE SEGMENT 4) and program.cs file (see CODE SEGMENT 5).
I tried to use the similar implementation method in an ASP.NET core MVC program. It could not get the SQL connection string from appsettings contained in the main web app from the C# class library project. What makes the accessing appsettings possible in Web API but not from MVC web app?
CODE SEGMENT 1
public class ProductRepository : IProductRepository
{
private readonly IConfiguration configuration;
public ProductRepository(IConfiguration configuration)
{
this.configuration = configuration;
}
public async Task<int> AddAsync(Product entity)
{
entity.AddedOn = DateTime.Now;
var sql = "Insert into Products (Name,Description,Barcode,Rate,AddedOn) VALUES (#Name,#Description,#Barcode,#Rate,#AddedOn)";
using (var connection = new SqlConnection(configuration.GetConnectionString("DefaultConnection")))
{
connection.Open();
var result = await connection.ExecuteAsync(sql, entity);
return result;
}
}
......
}
CODE SEGMENT 2
using Dapper.Application.Interfaces;
using Dapper.Infrastructure.Repository;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
namespace Dapper.Infrastructure
{
public static class ServiceRegistration
{
public static void AddInfrastructure(this IServiceCollection services)
{
services.AddTransient<IProductRepository, ProductRepository>();
services.AddTransient<IUnitOfWork, UnitOfWork>();
}
}
}
CODE SEGMENT 3
<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.35" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.6" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.1" />
</ItemGroup>
CODE SEGMENT 4
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dapper.Infrastructure;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
namespace Dapper.WebApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddInfrastructure();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.IncludeXmlComments(string.Format(#"{0}\Dapper.WebApi.xml", System.AppDomain.CurrentDomain.BaseDirectory));
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "Dapper - WebApi",
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
#region Swagger
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "CleanArchitecture.WebApi");
});
#endregion
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
CODE SEGMENT 5
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Dapper.WebApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

Application doesn't load azure app config feature flag value but loads config values

I have a Blazor app that I want to use Azure app config.
I've successfully setup getting config values, however it doesn't find my feature flag.
When I inject an IConfiguration and query the value of a config value, like so Configuration["value"] the proper data is retrieved.
But when I try and check if my only feature flag is enabled (and it is) the result is always false, which I imagine is because the app can't find the feature flag, not because it's reading the value wrong (passing the name of a non-existant feature flag causes the method to return "false").
Startup.cs:
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.FeatureManagement;
namespace OreNoAppu
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
services.AddControllersWithViews().AddMicrosoftIdentityUI();
// By default, all incoming requests will be authorized according to the default policy
services.AddAuthorization(options => options.FallbackPolicy = options.DefaultPolicy);
services.AddRazorPages();
services.AddServerSideBlazor().AddMicrosoftIdentityConsentHandler();
services.AddFeatureManagement();
services.AddAzureAppConfiguration();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days.You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAzureAppConfiguration();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
Program.cs:
using Azure.Core;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using BoomiLogReader.Utility;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System;
namespace OreNoAppu
{
public class Program
{
public static void Main(string[] args)
{
InitKeyVault();
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
webBuilder.ConfigureAppConfiguration(config =>
{
var connection = KeyVaultReader.GetSecretValue("AzureAppConfigConnStr");
config.AddAzureAppConfiguration(options =>
options.Connect(connection).UseFeatureFlags());
}).UseStartup<Startup>());
// Initialises KeyVaultReader, which is used to retrieve secrets from Azure Key Vault.
public static void InitKeyVault()
{
// N'importe quoi, access to KeyVault and getting of the conn string works fine.
}
}
}
Code that should be getting the flag:
#using Microsoft.FeatureManagement
#inject IFeatureManager featureManager
#if (featureEnabled)
{
<h1>Yes</h1>
}
#code
{
bool featureEnabled = true;
protected override async Task OnInitializedAsync()
{
featureEnabled = featureManager.IsEnabledAsync("OreNoFlaggu").Result;
}
}
Debugging, I see that the method call "IsEnabledAsync" returns "false", but there is a "OreNoFlaggu" feature flag in my app config, that the application successfully connects and retrieves config values from, and said flag is enabled.
Any idea why it isn't working?
#Tessaract, does your feature flag happen to have a label in your App Config store. If so, please don't forget to pass the label when calling UseFeatureFlags() via FeatureFlagOptions.Label.

.NET Core 3.1 Cors Issue

I have followed the guidelines listed in the Microsoft article: Enable Cross-Origin Requests (CORS) in ASP.NET Core and I am still unable to access the API from the local vue website or PostMan. Any suggestions?
Here is what is defined in AllowedHosts:
"AllowedHosts": "http://localhost;http://localhost:8080"
Here is the startup class:
using App.Core.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace App.Core
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ImportContext>((builder) =>
{
builder.UseSqlServer(Configuration.GetConnectionString("ParsingAppDb"));
});
services.AddDbContext<CodeAdminContext>((builder) =>
{
builder.UseSqlServer(Configuration.GetConnectionString("ParsingAppDb"));
});
services.AddScoped(typeof(IImportContext), typeof(ImportContext));
services.AddScoped(typeof(ICodeAdminContext), typeof(CodeAdminContext));
services.AddTransient(typeof(Logic.IImporter), typeof(Logic.Importer));
services.AddTransient(typeof(Logic.I2964Procssor), typeof(Logic.Processor_2964));
services.AddTransient(typeof(Logic.I2965Procssor), typeof(Logic.Processor_2965));
var allowedHosts = Configuration.GetValue(typeof(string), "AllowedHosts") as string;
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
if (allowedHosts == null || allowedHosts == "*")
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
return;
}
string[] hosts;
if (allowedHosts.Contains(';'))
hosts = allowedHosts.Split(';');
else
{
hosts = new string[1];
hosts[0] = allowedHosts;
}
builder.WithOrigins(hosts)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
}); services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Even using fiddler does not show anything helpful, just the denied call. Conversely if I set it to any origin I can get PostMan to work but the vue website then returns that no Accept-Control was set.
Update: And I already have the Microsoft.AspNetCore.Cors package installed.
I found the problem. It is the configuration value:
"AllowedHosts": "http://localhost;http://localhost:8080"
Apparently this value is used in another way, creating a separate Cors section and placing the allowed cors hosts there and then changing the AllowedHosts value back to * fixed the issue.

Static files don't work when publishing a .NET Core Razor Pages (maybe MVC too) to Mac OSX

After publishing a .net Core Razor Pages project using a self-contained osx-64 profile and then trying to run it on a Macintosh I noticed that all the static files which reside in the wwwroot folder do not work. They simply return a blank page.
There may be same issue when creating an pure MVC (not Razor Pages) site.
Here are the contents of the Startup.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace WebApplication1
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
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.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
}
}
Apparently, when using
app.UseStaticFiles();
on OSX, the location of wwwroot is being looked for elsewhere on the computer instead of in the root of the folder where the app is running.
To solve this I used the following code inside of the
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
function:
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX))
app.UseStaticFiles(new StaticFileOptions
{FileProvider = new PhysicalFileProvider(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "wwwroot")),RequestPath = ""});
else
app.UseStaticFiles();
This will tell OSX to set wwwroot folder in the applications folder as the static files folder. In Windows, the standard app.UseStaticFiles() is used.

Categories