Getting the Base URL of the application inside of the ASP.NET Core HostedService - asp.net-core

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();

Related

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

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.

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.

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]

How to retrieve headers on AppHarbor hosted WCF-applications

Currently I have a WCF-based service deployed on AppHarbor. I'm having a issue with a GET operation defined like this:
[WebGet(UriTemplate = "feedcallback")]
Stream HandleMessageGet();
And implemented like this:
public Stream HandleMessageGet()
{
var value = WebOperationContext.Current.IncomingRequest.Headers["header.name"];
//Do stuff with header value
return ms;
}
Whenever I run this WCF application on localhost for debugging etc. it works fine; I can retrieve the header value. But whenever I deploy the project to AppHarbor, the get request doesn't function properly anymore because it can't retrieve the header from the WebOperationContext.
What could be causing this issue and how could this be solved?
In the end it seems to be an issue with the AppHarbor loadbalancer not forwarding headers with dots in them.
See: http://support.appharbor.com/discussions/problems/37218-header-not-being-forwarded

How to host Web API in Windows Service

I have several resources that I'd like to expose using the WCF Web API. I've investigated the Web API using a Web host but our services all run as Windows Services in production so it's time for me to put the tests aside and verify that everything will work as we need it. I've looked as the sample app here: http://webapicontrib.codeplex.com/SourceControl/changeset/view/2d771a4d6f6f#Samples%2fSelfHosted%2fserver%2fProgram.cs but this does not work with the current version (preview 5) because the HttpConfigurableServiceHost class is not accessible from our code.
One of the most appealing aspects of the Web API is the simple startup using MapServiceRoute and the new WebApiConfiguration. I don't see, however, a way to define the base url and port for the services. Obviously, hosting the service in IIS eliminates this because we configure this information in IIS. How can I accomplish this when hosting in a Windows Service?
It's actually pretty simple. In a nutshell you need to instantiate HttpSelfHostServer and HttpSelfHostConfiguration and then call server.OpenAsync().
public void Start()
{
_server.OpenAsync();
}
public void Stop()
{
_server.CloseAsync().Wait();
_server.Dispose();
}
For an example on how to do this using Windows service project template and/or Topshelf library see my blog post: http://www.piotrwalat.net/hosting-web-api-in-windows-service/
The latest version just uses HttpServiceHost. http://webapicontrib.codeplex.com/SourceControl/changeset/view/ddc499585751#Samples%2fSelfHosted%2fserver%2fProgram.cs
Ping me on twitter if you continue to have problems.
This is the basic code using a console app. A Windows Service uses the same basic approach except you use the start and stop methods to start and stop the service and don't need to block.
static void Main(string[] args)
{
var host = new HttpServiceHost(typeof(PeopleService), "http://localhost:8080/people");
host.Open();
foreach (var ep in host.Description.Endpoints)
{
Console.WriteLine("Using {0} at {1}", ep.Binding.Name, ep.Address);
}
Console.ReadLine();
host.Close();
}
See this blog post.