Kestrel Port:0, how to detect autoselected port - c#

I want Kestrel to automatically select a port. My Program.cs looks like this:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
var builder = webBuilder.UseStartup<Startup>();
if (args.Contains("--automatic-port-selection"))
{
builder.UseUrls("http://127.0.0.1:0");
}
});
I looked at https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1 to understand how I can detect the selected port. But I would actually like to obtain the port when the software starts.
I tried with the following:
public class Startup
{
// 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) { }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var serverAddressesFeature =
app.ServerFeatures.Get<IServerAddressesFeature>();
if (serverAddressesFeature != null)
{
foreach (var address in serverAddressesFeature.Addresses)
{
int port = int.Parse(address.Split(':').Last());
Console.Out.WriteLine("Port:" + port);
}
Console.Out.Flush();
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); });
}
}
You can see this basically just a aspnet core application created from the Visual Studio template.
The issue here is that it will always only writes 0. It seems it would only be able to give the correct port number when I read serverAddressesFeature. Addresses` when handling a request. How can I get the used port number when starting up the server?
EDIT
This seems to be relevant: https://github.com/aspnet/Hosting/issues/1390

As far as I know, the reason why you always get the 0 port number is the application doesn't started completely.
When the application started, it will call the startup.cs configure method firstly. This time Kestrel know that its port is 0.
After that it will find the auto free port.
So if you want to get the port which your application is using now. You could write a method which will fired after the application stated completely to log the right port.
More details, you could refer to below codes:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime , IServiceProvider serviceProvider){
//other codes ....
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)
{
int port = int.Parse(address.Split(':').Last());
Console.Out.WriteLine("Port:" + port);
}
}
}
Result:

Related

Running from Visual Studio - Failed to load resource: net::ERR_CONNECTION_REFUSED - Angular, Net(5.0) app, Electron

I am running Visual Studio 2019. I have an angular app that works completely fine when Electron is not running. For example, If I choose IIS Express from the debug list, all works well (ApiControllers run correctly and return data):
However, if I run with Electron.Net App, the site loads and shows the page, but my ApiControllers are not contactable. The following error appears:
Here is my Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IServiceProvider serviceProvider { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// Registers a few services with their interfaces
IOCContainer iocContainer = new IOCContainer();
iocContainer.ConfigureServices(services);
//
// Is this needed?
services.AddCors(options =>
options.AddPolicy("DefaultCorsPolicy", builder => builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()));
// This is very important for some of the services I register in the iocContainer
services.AddHttpClient();
}
// 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();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseCors();
//app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
//endpoints.MapControllerRoute(
// name: "api",
// pattern: "api/{controller}/{action}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
ElectronBootstrap();
}
public async void ElectronBootstrap()
{
BrowserWindowOptions options = new BrowserWindowOptions
{
Show = false,
//WebPreferences = new WebPreferences() {
// AllowRunningInsecureContent = true,
// ContextIsolation = false
//}
};
BrowserWindow mainWindow = await Electron.WindowManager.CreateWindowAsync();
mainWindow.OnReadyToShow += () =>
{
mainWindow.Show();
};
mainWindow.SetTitle("App Name here");
mainWindow.WebContents.OpenDevTools();
MenuItem[] menu = new MenuItem[]
{
new MenuItem
{
Label = "File",
Submenu=new MenuItem[]
{
new MenuItem
{
Label ="Exit",
Click =()=>{Electron.App.Exit();}
}
}
},
new MenuItem
{
Label = "Info",
Click = async ()=>
{
await Electron.Dialog.ShowMessageBoxAsync("Welcome to App");
}
}
};
Electron.Menu.SetApplicationMenu(menu);
}
}
and here is my Program.cs
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>();
webBuilder.UseElectron(args);
//webBuilder.UseSetting("https_port", "8080");
});
//public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
// WebHost.CreateDefaultBuilder(args)
//.UseStartup<Startup>()
//
}
Any thoughts on what I might be doing wrong? I assume the IIS server is not running, but that would defy the point of having a self contained desktop application running on Electron. What options do I have?
My goal:
To have a self contained desktop application that others can use.
It will read and write files from local file system.
It will make requests out to APIs
It will have an Angular front end and a C# back end.
Additional info:
This may be relevant:
Be careful with the default application set up in Visual Studio for Angular apps.
There is a "getBaseUrl" constant in the Angular/Typescript code module. If you use it in injection of your services, it will always route you through the full URL which may or may not be correct after packaging up your application. Instead, you can blank it out or simply not use it.

Inject a service in Startup.cs in ASP.NET Core 3.1

I am working on a .NET Core 3.1 application. I have a requirement where i have to inject a service in Startup.cs. My code is:
Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices(servicesCollection =>
{
servicesCollection.AddScoped<IUnauthorizedAccessService, UnauthorizedAccessService>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs:
public Startup(IConfiguration configuration, IUnauthorizedAccessService unauthorizedAccessService)
{
Configuration = configuration;
_unauthorizedAccessService = unauthorizedAccessService;
}
public IConfiguration Configuration { get; }
public IUnauthorizedAccessService _unauthorizedAccessService { get; set; }
When i run the code, i get the following exception:
Unable to resolve service for type 'Interface.Service.IUnauthorizedAccessService' while attempting to activate 'Website.Startup'.'
How can i inject the service in Startup.cs ? I have even tried it getting in Configure method. But then, i get the exception at repository level. Code:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IUnauthorizedAccessService unauthorizedAccessService)
{
_unauthorizedAccessService = unauthorizedAccessService;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/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.UseSession();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCookiePolicy(new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Strict,
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=User}/{action=Index}/{id?}");
});
}
I have a method RegisterDatabase which is being called from ConfigureServices. Code:
private void RegisterDatabase(IServiceCollection services)
{
services.AddDbContext<TrainingContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
Service code is:
public class UnauthorizedAccessService : IUnauthorizedAccessService
{
private readonly IEventLogRepository _eventLogRepository;
public UnauthorizedAccessService(IEventLogRepository eventLogRepository)
{
_eventLogRepository = eventLogRepository;
}
public async Task<BaseResponse> LogUnauthorizedAccessInDB(string user, string url, string sessionId)
{
try
{
EventLog eventLog = new EventLog();
eventLog.Httpsession = sessionId;
eventLog.AppUserName = user;
eventLog.EventDateTime = DateTime.Now;
eventLog.MessageLevel = 3;
eventLog.Message = url;
await _eventLogRepository.Add(eventLog);
}
catch(Exception ex)
{
}
return HelperService.Response(null, null);
}
}
When Adding the object, i get the exception
Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'TrainingContext'.
All of my other repositories are working but, getting exception only at this point. What can be the possible issue ? Any help would be much appreciated.
Basically, what i am trying to achieve is that i want to log unauthorized access to my site in Database. Code is:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o =>
{
o.AccessDeniedPath = "/Home/Error";
o.LoginPath = "/Login";
o.SlidingExpiration = false;
o.Events = new CookieAuthenticationEvents
{
//OnRedirectToAccessDenied = new Func<RedirectContext<CookieAuthenticationOptions>, Task>(context =>
OnRedirectToAccessDenied = new Func<RedirectContext<CookieAuthenticationOptions>, Task>(test)
};
});
test method is:
private async Task<Task> test (RedirectContext<CookieAuthenticationOptions> context)
{
string user = context.HttpContext.User.Identity.Name;
string url = "/" + context.Request.Host.Value + "/" + context.Request.RouteValues["controller"] + "/" + context.Request.RouteValues["action"];
string sessionId = context.HttpContext.Session.Id;
await _unauthorizedAccessService.LogUnauthorizedAccessInDB(user, url, sessionId);
context.Response.Redirect("/Home/Error");
return context.Response.CompleteAsync();
}
You need to create a scoped object that implements CookieAuthenticationEvents. For example:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Threading.Tasks;
namespace MyApplication.Services
{
public class MyCookieAuthenticationEvents : CookieAuthenticationEvents
{
private readonly IUnauthorizedAccessService _unauthorizedAccessService;
public MyCookieAuthenticationEvents(
IUnauthorizedAccessService unauthorizedAccessService)
{
_unauthorizedAccessService = unauthorizedAccessService;
}
public override Task RedirectToAccessDenied(
RedirectContext<CookieAuthenticationOptions> context)
{
// TODO: you can use _unauthorizedAccessService here
return base.RedirectToAccessDenied(context);
}
}
}
To inject this, you'd do it as so:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.EventsType = typeof(MyCookieAuthenticationEvents);
});
services.AddScoped<MyCookieAuthenticationEvents>();
services.AddScoped<IUnauthorizedAccessService, UnauthorizedAccessService>();
Make sure you remove that IUnauthorizedAccessService from your program.cs. You don't inject there. You inject in your Configure method.
This is how you do proper dependency injection. You don't do what the accepted answer is doing. That is probably one of the most unorthodox things I have ever seen in a long time.
Startup.cs is designed for configuring own services and pipeline configuration. You can not inject your custom services in constructor just because they are not configured yet.
Docs:
The host provides services that are available to the Startup class
constructor. The app adds additional services via ConfigureServices.
Both the host and app services are available in Configure and
throughout the app.

Hangfire not working with ASP.NET Core 3.1 as expected

Not sure what I'm doing wrong here.
I've setup Hangfire.
In startup.cs I've added
public void ConfigureServices(IServiceCollection services)
{
....
// Add Hangfire services.
services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(_configuration.GetConnectionString(Constants.AppSettingNames.DefaultConnectionStringName), new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
}));
// Add the processing server as IHostedService
services.AddHangfireServer();
services.AddMvc()
....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IBackgroundJobClient backgroundJobs)
{
app.UseHangfireDashboard();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapControllerRoute("default", "{controller}/{action=Index}/{id?}");
endpoints.MapHangfireDashboard();
});
}
Then I have a service that I call from a controller
public class PublisherService
{
public Publisher(IBackgroundJobClient backgroundJobs)
{
_backgroundJobs = backgroundJobs;
}
public void Publish()
{
// This works
_backgroundJobs.Enqueue(() => Console.WriteLine("Rocky"));
// This doesn't work
_backgroundJobs.Enqueue(() => TestPublish());
}
private static void TestPublish()
{
Console.WriteLine("Yo Adrian");
}
}
This code doesn't seem to execute my custom method TestPublish().
In my controller I call
publisherService.Publish();
This works correctly:
_backgroundJobs.Enqueue(() => Console.WriteLine("Rocky"))
but the following does not??
_backgroundJobs.Enqueue(() => TestPublish());
Any ideas why not?
Thanks
Figured it out, the custom method needed to be public
so
private static void TestPublish()
needed to be
public static void TestPublish()
The Enqueue method uses an expression and not a delegate, so it doesn't get execute in the calling context. It get's executed at some point later so that needs to be publicly accessible.
Also, I hadn't got Hangfire logging setup correctly so I couldn't see the error message.
Hopefully this will be useful to somebody else

Singleton ImplementationType: Unable to resolve service for type 'Nest.IElasticClient' while attempting to activate

I have a default web api template project using .NET Core 3.1 and I have registered Elastic Search NEST on my startup.cs. But when I load it, it hit error at
Singleton ImplementationType: Unable to resolve service for type 'Nest.IElasticClient' while attempting to activate in program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
and here is my startup.cs
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();
services.AddSingleton<IProductService, ESProductService>();
services.Configure<ProductSettings>(Configuration.GetSection("product"));
services.AddElasticsearch(Configuration);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
and below is the ElasticsearchExtensions extension class
public static class ElasticsearchExtensions
{
public static void AddElasticsearch(this IServiceCollection services, IConfiguration configuration)
{
var url = configuration["elasticsearch:url"];
var defaultIndex = configuration["elasticsearch:index"];
var settings = new ConnectionSettings(new Uri(url))
.DefaultIndex(defaultIndex);
AddDefaultMappings(settings);
var client = new ElasticClient(settings);
services.AddSingleton(client);
CreateIndex(client, defaultIndex);
}
private static void AddDefaultMappings(ConnectionSettings settings)
{
settings
.DefaultMappingFor<Product>(m => m
.Ignore(p => p.Price)
.Ignore(p => p.Quantity)
.Ignore(p => p.Rating)
);
}
private static void CreateIndex(IElasticClient client, string indexName)
{
var createIndexResponse = client.Indices.Create(indexName,
index => index.Map<Product>(x => x.AutoMap())
);
}
}
Problem solved. The AddSingleton miss out the interface. services.AddSingleton<IElasticClient>(client);
I also got the same error. To fix the issue, I used IElasticClientService instead of IElasticClient. You can use below code for dependency Injection.
builder.Services.AddScoped<IElasticClientService, ElasticClientService>();
ElasticClientService has a property called "elasticClient" which can be used to do all sort of operations. Hope this helps.

How to enable logging for gRPC in .NET Core 3.0

I have an ASP.NET Core 3.0 (3.0.0-preview8-28405-07) server application which is using gRPC (0.1.22) with protocol buffers. I'd like to enable logging to a file or console.
Below is an example of a .Startup file:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc(options =>
{
options.EnableDetailedErrors = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>();
});
}
}
And the GreeterService:
public class GreeterService : Greeter.GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply
{
Message = "Hello " + request.Name
});
}
}
gRPC has a logging class called Grpc.Core.Logging.ILogger. How can I set this up so it is logging to a file or console?
Logging with GRPC in ASP.NET core 3 is handled the same way for any other ASP.NET app.
You can enable logging by running
hostBuilder.ConfigureLogging(logging =>
{
logging.AddConsole();
})
in your program.cs entry point file or by running
serviceCollection.AddLogging(logging =>
{
logging.AddConsole();
});
in your ConfigureServices method in startup.cs

Categories