Kestrel address binding errors in azure app service - asp.net-core

I have an aspnet core 2.2 app which looks like this:
return WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((builderContext, config) =>
{
var env = builderContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>()
.UseKestrel(options =>
{
options.ConfigureHttpsDefaults(opts =>
{
opts.ServerCertificate = GetCertificate();
opts.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
opts.ClientCertificateValidation = CertificateValidator.DisableChannelValidation;
});
});
Everything works great locally. However, when deployed to an azure app service I get the following:
Unhandled Exception: System.IO.IOException: Failed to bind to address http://127.0.0.1:5000: address already in use
Is there anything special I need to do differently here? I don't particularly care about using Kestrel vs anything else as long as I can perform client certificate authentication (which works locally in my current implementation).

The issue turned out to be that the application was already running but was not taking requests due to trying to use kestrel in the azure app service. I had to use .UseIIS() to get it to work. Perhaps I was doing something wrong.

If you already know what port your app. will consistenly use, it may make sense to just code this into your app with UseUrls:
.UseUrls(urls: "http://localhost:10000")
Or running dotnet run --urls="http://localhost:10000" like this issue.

Related

Migration to Minimal API - Test Settings Json not overriding Program

Thanks to this answer: Integration test and hosting ASP.NET Core 6.0 without Startup class
I have been able to perform integration tests with API.
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
});
});
HttpClient? client = app.CreateClient();
This has worked using the appsettings.json from the API project. Am now trying to use integrationtestsettings.json instead using:
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(ProjectDirectoryLocator.GetProjectDirectory())
.AddJsonFile("integrationtestsettings.json")
.Build();
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
builder.ConfigureServices(services =>
{
});
});
_httpClient = app.CreateClient();
I have inspected the configuration variable and can see the properties loaded from my integrartiontestsettings.json file. However, the host is still running using the appsettings.json from the server project.
Previously, in .Net5, I was using WebHostBuilder and the settings were overridden by test settings.
WebHostBuilder webHostBuilder = new();
webHostBuilder.UseStartup<Startup>();
webHostBuilder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(_configuration));
But cannot get the test settings to apply using the WebApplicationFactory.
It seems the method has changed.
Changing:
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
To:
builder.UseConfiguraton(configuration);
has done the trick.
builder.ConfigureAppConfiguration, now it's configuring the app (after your WebApplicationBuilder.Build() is called) and your WebApplication is created.
You need to "inject" your configurations before the .Build() is done. This is why you need to call UseConfiguraton instead of ConfigureAppConfiguration.

URL Rewrite exceptions for Blazor WebAssembly Hosted deployment

During development, i have used Swagger on the server side of my Blazor WebAssembly App. Always launching (debug) using kestrel instead of IIS Express.
Routing worked as expected, all my component routed properly and if i manually typed /swagger, i got to the swagger page. All good.
We have deployed under IIS on our pre-prod servers, the Server side and Blazor WebAssembly App (client) work as expected and are usable, however, my /swagger url gets rewritten (I assume) to go somewhere in my App instead of letting it go to Swagger, obviously there isn't any component that answers to /swagger.
My only guess is that, when hosted on IIS, the aspnet core app takes care of telling IIS what to rewrite and how (similar to the configs that could be provided thru a web.config for a "Standalone" deployment.)
I can't find how to specify exceptions, I've been following the doc at
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/webassembly?view=aspnetcore-3.1#iis
Any idea how i could add an exception for /swagger ?
EDIT:
Turns out it works without issues in Chrome, only Firefox has the unwanted behavior. If i clear my cache, or use Incognito mode, the issue does not happen in Firefox. So, it seems that Firefox caches some stuff and tries to send my URL input to the Blazor Wasm instead of going thru to the server. I will debug some more with the dev tools and fiddler open to try and figure it out, will report back.
Turns out there this is part of the service-worker.js file that is published. It is different in dev than what gets published (which makes sense).
During my debugging i was able to reproduce the issue on all browsers (Edge, Chrome and Firefox), regardless of being in Incognito/Private mode or not.
Once the service-worker is running, it handles serving requests from cache/index.html of the Blazor WebAssembly app.
If you go into your Blazor WebAssembly Client "wwwroot" folder, you'll find a service-worker.js and a service-worker.published.js. In the service-worker.published.js, you will find a function that looks like this :
async function onFetch(event) {
let cachedResponse = null;
if (event.request.method === 'GET') {
// For all navigation requests, try to serve index.html from cache
// If you need some URLs to be server-rendered, edit the following check to exclude those URLs
const shouldServeIndexHtml = event.request.mode === 'navigate'
&& !event.request.url.includes('/connect/')
&& !event.request.url.includes('/Identity/');
const request = shouldServeIndexHtml ? 'index.html' : event.request;
const cache = await caches.open(cacheName);
cachedResponse = await cache.match(request);
}
return cachedResponse || fetch(event.request);
}
Simply following the instructions found in the code comments is gonna fix the issue. So we ended up adding an exclusion for "/swagger" like so :
&& !event.request.url.includes('/swagger')
Hopefully this post is useful for people who are gonna want to serve things outside of the service worker, not only Swagger.
Do you have UseSwagger first in your Startup.Configure method?
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
c.SwaggerEndpoint("/swagger/v1/swagger.json", "YourAppName V1")
);
In Startup.ConfigureServices I have the Swagger code last.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSwaggerGen(c =>
c.SwaggerDoc(
name: "v1",
info: new OpenApiInfo
{
Title = "YourAppName",
Version = "V1",
}));
}
This is working just fine for us.
Note: You must navigate to https://yourdomain/swagger/index.html

Using Steeltoe Discovery Client on .NET Core console application (Spring Cloud Config)

In this thread I was able to setup my simple console application using ASP.NET CORE's configuration system.
The code is as simple as:
static void Main(string[] args)
{
string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: false)
.AddEnvironmentVariables();
config = configuration.Build();
var serviceProvider = new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddDiscoveryClient(config)
.BuildServiceProvider();
Console.WriteLine(config["Test"]);
Console.Read();
}
However, since the application does not use IApplicationBuilder, I cannot invoke the .UseDiscoveryClient() method. I end up receiving an error on .AddDiscoveryClient(config):
"Discovery client type UNKNOWN, check configuration"
Is there a work around this? We would like to experiment using console applications with our Spring Cloud Config server. If there is no way to do it with Steeltoe, feel free to inform with other libraries that do.
The extension methods AddDiscoveryClient and UseDiscoveryClient are for use with Steeltoe service discovery. The error message you're seeing is due to Steeltoe not knowing which type of service registry your app should be a client of (ie: "client type UNKNOWN").
You only wish to access Spring Cloud Config server, so you don't need either of those methods. You can add the ConfigServerConfigurationProvider to you your config builder with .AddConfigServer.
you should add "appName" and "hostname" in the appsetting.json under instance

ASP.NET Core configuration reloadOnChange with IOptionsSnapshot still not responsive

I'm using ASP.NET Core 2.0 and I have configuration code like this in the Main method:
public static void Main(string[] args)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment ?? "Production"}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddCommandLine(args)
.Build();
}
I have the reloadOnChange set to true, and in my controller I am using IOptionsSnapshot;
public HomeController(ILogger<HomeController> logger, IOptionsSnapshot<AppSettings> options)
But when I modify the values in my appsettings.json, I have to restart my app every time or the changes are not being picked up just by refreshing the browser. What am I doing wrong?
I've tried to run the app both with console and IIS Express; I've also tried IOptionsMonitor, same thing. What is the difference between IOptionsMonitor and IOptionsSnapshot?
As mentioned in the documentation, just enabling reloadOnChange and then injecting IOptionsSnapshot<T> instead of IOptions<T> will be enough. That requires you to have properly configured that type T though. Usually a configuration registration will look like this:
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
However, looking closer at your code, it does not seem that you are using the new ASP.NET Core 2.0 way of configuring your program. The configuration is now part of dependency injection, so you will set it up as part of the WebHostBuilder, using ConfigureAppConfiguration. That could for example look like this:
public static IWebHost BuildWebHost()
=> new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((builderContext, config) =>
{
IHostingEnvironment env = builderContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>()
.Build();
If you are using the default builder using WebHost.CreateDefaultBuilder(), then you don’t even need to do this, as the configuration is then automatically set up like that with reloadOnChange activated.
The difference between IOptionsSnapshot and IOptionsMonitor is that the IOptionsSnapshot will just give you a snapshot of the options at the time the IOptionsSnapshot<T> object is being constructed.
That’s why the usage is exactly the same as with IOptions<T>: You inject it in the constructor and then store the options.Value in the instance to access the options later. At that point, that object is fixed and will never change. It’s just that the IOptionsSnapshot<T> is registered as a scoped dependency instead of a singleton dependency like IOptions<T>, so it gets the chance to get the current configuration values on every request instead of just once.
The IOptionsMonitor<T> however is a singleton service that allows you to retrieve the current configuration value at any given time. So it is especially useful for singleton services that need to get the current configuration whenever they need it. In addition, the options monitor offers a push mechanism to get notified of configuration changes by the configuration sources. That way, you can explicitly handle configuration changes.
Options snapshots are designed to be used for transient or scoped dependencies, so you will be fine just using those most of the time. Only in rare occasions when you have to use a singleton service that also needs to have the most updated configuration, you should have the need to use an options monitor. In those cases, note that just switching from snapshots to a monitor will not be enough. Usually, you will have to handle changed configuration in some way (for example clean up state, clear caches etc.). So you should always think about whether you actually need reloadable configuration for everything or if just restarting the application isn’t a viable alternative.

.Net Core web api needs to consume web service from configuration file

In my .Net Core application I am consuming Web Service using
Add -> Connected services -> WCF Service Preview(nuget package) and added the web service and used the service methods.
However, now clients moved the Web Service to internal Web Servers and I do not have access to the service from my development environment. So I am not able to access the service methods and build my solution and publish.
Is there any way I can pass the Service URL from Configuration file?
Example:
For Dev Environment - http://dev.svc
For Prod Environment - http://prod.svc
Yes, you can. I suggest you to read whole article about the configuration in ASP.NET Core, as there are lots of things you may find useful. In general, you can get the config files for each of environments with code like this:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
// note that here we do override the values by specific file for an emvironment
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
// and this line will get the environment variables from server machine
.AddEnvironmentVariables();
Configuration = builder.Build();
}
}
JSON files could be something like this:
appsettings.json
{
"serviceUrl": "",
}
appsettings.Development.json
{
"serviceUrl": "http://dev.svc",
}
appsettings.Production.json
{
"serviceUrl": "http://prod.svc",
}
Also you may find useful the Working with multiple environments article.