ASP.NET Core (Blazor) Loading customer specific configuration on startup by using Url or Virtual Path - asp.net-core

Our application (Server-side Blazor in .NET Core 3.1) runs within IIS on a Windows Server. We have multiple sites in IIS running the same application but with different URL's for different customers.
At startup in (ConfigureServices) we want to load customer configuration for the application from a config file. That way we can have multiple instances of the application running with different configs. Loading this information from the database is not an option because the config contains the details to connect to the database.
In ASP.NET Framework we would have access to the virtual path or (sub)domain name in the Global and then load the configuration based on that information.
We need the same access in our ASP.Net Core applications or another work around.
Is there any to achieve the same result?

A better way to distinguish sites is by URL. The domain name and port bound to each site in IIS will not be repeated.
You can refer to these code to get URL in startup.cs.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IHostApplicationLifetime lifetime,IServiceProvider serviceProvider)
{
...other code...
lifetime.ApplicationStarted.Register(
() => logAddresses(app.ServerFeatures));
}
static void logAddresses(IFeatureCollection features)
{
var addressFeature = features.Get<IServerAddressesFeature>();
if (addressFeature != null)
{
foreach(var address in addressFeature.Addresses)
{
Console.Out.WriteLine(address);
}
}
}

I was able to grab the Application Pool ID from the Environment and then load the config section as the application config:
var appPool = Environment.GetEnvironmentVariable( "APP_POOL_ID", EnvironmentVariableTarget.Process )
van sectionname = SomeMagicToParseAppPool( appPool );
var config = Configuration.GetSection( sectionName );
if ( config == null ) throw new ApplicationException($" Cannot find config section for {host}");
services.Configure<ApplicationSettings>( config );
This allows me to load a different config for a different site. The only downside is that each application requires their own Application Pool. But that was already a requirement due to a .NET Core ASP.NET app having to run unmanaged in the Application Pool.

Related

What is the correct way to host .well-known/openid-configuration?

I have a Blazor Server Project based on ASP.NET Core 5. I want to host my own openid-configuration discovery file. Since this file is served while running the OIDC workflow I want to verify what is the correct way to host this file. So far I have tried the following and only option 2 works.
using wwwroot/.well-known
This involves hosting the openid-configuration file statically in the wwwroot folder of my blazor server project.
After this if I run he project and try to access the file using localhost:44382/.well-known/openid-configuration, the file is not served.
Using Controllers
For this I just added a simple controller to my blazor project and specified .well-known/openid-configuration as a route for my anonymous controller HTTPGET action.
public class OidcConfigurationController : Controller
{
[HttpGet(".well-known/openid-configuration")]
public JsonResult OpenIdConfiguration()
{
return Json(new Storage.Storables.Security.OIDC.Configuration());
}
}
Now if I run the project with Option 2 and try to reach the localhost:44382/.well-known/openid-configuration the configuration JSON is served correctly.
Is option 2 the correct way to serve the OpenId-Configuration using ASP.NET Core and Blazor server project ? Will it cause any issues if I publish the server (for e.g. to Azure)
The reason why your first method is not working is that you don't serve a static file in a way the static file extensions assume you do. You missing a file ending, otherwise, the request isn't recognized as a file.
That said, you can write your own middleware. Give the file a proper ending like .json. If the resources /.well-known/openid-configuration/ is requested, you change the requested path to /.well-known/openid-configuration.json and let the static file extension handle the rest.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.Use(async (context, next) =>
{
if (context.Request.Path == "/.well-known/openid-configuration")
{
context.Request.Path = "/.well-known/openid-configuration.json";
}
await next();
});
app.UseStaticFiles();
...
}
For more information about writing a middleware have a look at the documentation https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write
However, you ran into the problem - I guess - because mostly this document is generated on the fly based on the configuration of your open id connect server like IdentityServer. So, maybe there is away around the static file?

UrlHelper returning http links on Azure App Service

I have a service that when deployed on Azure App Services returns http links instead of https links when using UrlHelper. When testing on my development machine it returns https links as expected, and the service is available and accessed through https requests.
An example of the type of route from my startup I'm trying to use is:
routes.MapRoute(
"FooBar",
"api/Foo/{Id}/Bar");
The link is then constructed using:
IUrlHelper _urlHelper = // Injected into class via service registration
int id = 42; // Arbitrary value for example
_urlHelper.Link("FooBar", new {Id = id});
When running on my local machine using Docker on Windows from Visual Studio I get a link of https://localhost:1234/api/Foo/42/Bar, but on my deployed Linux Container App Service on Azure I get http://my-app-name.azurewebsites.net/api/Foo/42/Bar.
I don't know what I'm doing wrong to get an http link instead of an https link, and would appreciate any advice/pointing in the right direction.
So I found the solution was with the configuration of the ASP.Net Core app itself. I performed the following modifications and then everything worked correctly:
Added app.UseForwardedHeaders(); to the request pipeline.
Added the following snippet to service container registration:
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
The KnownNetworks and KnownProxies need to be cleared as they default to assuming an IIS hosting environment. For extra security you can add the known proxy/network IPs instead of clearing them here.

Getting the Base URL of the application inside of the ASP.NET Core HostedService

I need to obtain the base URL of the ASP.NET Core application inside of one of the HostedServices.
I need this because it does a request to the same ASP.NET core application in which it is hosted (the purpose is warming up, to improve the first call performance to the User).
For now my solution is to keep the base URL in the config file or just in the hosted service private variable.
https://github.com/BBGONE/JRIApp.Core/blob/master/DEMOS/RIAppDemoMVC/RIAppDemo/Utils/WarmUpService.cs
But i think, there's a way to obtain it from the startup code, but i don't know where it is hidden.
Anybody know how it can be obtained?
P.S. - there are solutions to obtain it from the request information, but the HostedService is started before any request have been done. So it's not suitable in this case.
I have found how to obtain the address of the appllication.
public void Configure(IApplicationBuilder application)
{
var addresses = application.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
}
Although it has an issue https://github.com/aspnet/Hosting/issues/811 and can not be used if the application is hosted in the IIS or IIS Express.
They say:
That's not going to work for IIS or IIS Express. IIS is running as a
reverse proxy. It picks a random port for your process to listen on
and does not flow the public address information to you. The only way
to get the public address information is from incoming requests.
The ASP.NET Core Module generates a dynamic port to assign to the backend process. CreateDefaultBuilder calls the UseIISIntegration method. UseIISIntegration configures Kestrel to listen on the dynamic port at the localhost IP address (127.0.0.1). If the dynamic port is 1234, Kestrel listens at 127.0.0.1:1234. This configuration replaces other URL configurations provided by.
But if you get the feature from the WebHost after it was built, then this can be used to get the local address for warm up.
I tried this way:
public static void Main(string[] args)
{
var builder = CreateWebHostBuilder(args);
var webHost = builder.Build();
var addresses = webHost.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
var address = addresses.FirstOrDefault();
AppDomain.CurrentDomain.SetData("BaseUrl", address?? "");
webHost.Run();
}
and got the local Kestrel address in the WarmUpService like this:
string baseUrl = AppDomain.CurrentDomain.GetData("BaseUrl").ToString();

Asp.Net Core signalR is not working if publishing inside virtual directory

I have developed an application in asp.net core and used signalR. When i publish it in root directory it works fine, but if i publish it inside virtual directory it doesn't work. My signalR hub is always pointing to the root directory.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ... other middleware ...
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chat");
});
}
And i have initialized it in client side like this,
chat.js
var connection = new signalR.HubConnection("/chat");
After publishing it inside virtual directory(/development/chatapp/source) it points like this,
http://localhost:100/chat?userId=1
But actually it has to point like this, so that it will work
http://localhost:100/development/chatapp/source/chat?userId=1
I have used asp.net core 2.0 and this signalR version(1.0.0-preview1-final).
Someone please suggest me to resolve this issue.
With the help of my friend i have found the root cause for this issue and i fixed it. Actually javascript doesn't know whether the application is hosted in root folder or sub folder(in virtual directory). It always points the root folder.
So when we are initializing it in js like this(new signalR.HubConnection("/chat")), it points the root directory as below,
http://localhost:100/chat?userId=1
Since javascript doesn't aware of IIS hosting, we need to tell the relative path from c#. So i get my application's base path as follows in controller,
ViewData["PathBase"] = Request.PathBase.ToString();
And if it has some value i just prepend it to '/chat', otherwise i just initialize it as '/chat'.
var connection = new signalR.HubConnection("/development/chatapp/source/chat");
This solves my problem :-)
Kailash P : With the help of my friend i have found the root ...
OK, thanks for your sharing.
I'm publish the SignalR Chat sample to IIS but NOT WORKS --X
SignalRChat :
 Tutorial: Get started with ASP.NET Core SignalR
 https://learn.microsoft.com/zh-tw/aspnet/core/tutorials/signalr?view=aspnetcore-2.2&tabs=visual-studio
*With Visual Studio debugging, the sample are works but NOT WORKS publish to IIS.
In page load, the send message button not enabled because there have some errors :
Failed to load resource: the server responded with a status of 500 (Internal Server Error)  chatHub/negotiate:1
[2019-11-18T06:40:26.977Z] Error: Failed to complete negotiation with the server: Error: Internal Server Error  Utils.ts:179
--
After add the vitural directory the SignalR Chat sample WORKS :
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build();
->
var connection = new signalR.HubConnectionBuilder().withUrl("/chat1/chatHub").build();
Don't know why there have no any official note about this problem (SignalR publishing).
[NET Core 2.2]
[Visual Studio 2019]

WCF Client Configuration in a Sharepoint Webpart

I have a Sharepoint 2010 webpart that calls a WCF service.
I've created a service proxy and manually coded the endpoint, see below.
In a conventional WCF client I'd use the config files for the configuration and use transforms when I was buiding for deployment to different environments.
How would I achieve the same through a Sharepoint webpart? I want to put the configuration somewhere that it can be changed for different build configurations.
ie. For a local deployment during testing, then a test server, production. We're trying to automate this as much as possible.
Thanks,
Tim
UPDATE:
I'm aware that you need to put config data in the web.config file in sharepoint. I'm looking for a way to put these config settings into source control and have them automatically populate / deploy for different builds and environments.
namespace CombinedPortal.WcfClient {
public class FrameworkServiceProxy : IFrameworkService
{
private IFrameworkService _proxy;
public FrameworkServiceProxy()
{
var endpoint = new EndpointAddress("http://server:1234/FrameworkService.svc");
var binding = new WSHttpBinding(SecurityMode.None);
_proxy = new ChannelFactory<IFrameworkService>(binding, endpoint).CreateChannel();
}
public Framework GetCurrentFramework(double uniqueLearnerNumber)
{
var fw = _proxy.GetCurrentFramework(uniqueLearnerNumber);
return fw;
}
} }
Your code is C# code which executes on the server.
When then user presses a button on a web part there is a POST back to the Sharepoint web server, where the C# code executes.
It is therefore the web.config of your SharePoint site which is used.