ASP.NET core mapping subdomains to areas - c#

I am trying to map the subdomains to areas, So far all the answers I found were for pervious versions of .NET and not .NET core. the best and most relevant answer I found was from This page. however i am having a problem implementing it as it appears to be the walkthrough for a pervious version of .NET core and i am getting the 'MvcRouteHandler' does not contain a constructor that takes 0 arguments Error.
Here is the code which i get the error from:
public class AreaRouter : MvcRouteHandler, IRouter //the error happens in this line, visual studio underlines the AreaRoute word
{
public new async Task RouteAsync(RouteContext context)
{
string url = context.HttpContext.Request.Headers["HOST"];
string firstDomain = url.Split('.')[0];
string subDomain = char.ToUpper(firstDomain[0]) + firstDomain.Substring(1);
string area = subDomain;
context.RouteData.Values.Add("area", subDomain);
await base.RouteAsync(context);
}
}
so anyway, i am looking for another way to map subdomains to areas or find a way to fix this error.

Edit: Complete tutorial available here.
Managed to create working version of your file.
1. Code for Router is available here. Specify your own subdomains in _allowedSubdomains array on top of file.
2. After adding router, change your code in Startup.cs:
app.UseMvc(routes =>
{
routes.DefaultHandler = areaRouter;
areaRouter should be passed as described by #mepsi.
3. Finally, allow the use of sudomains as described here.
Everything should be working now. The complete code is available on Github. I will write complete tutorial with all explanations later.

I'm struggling with the same issue and I managed to get forward by adding my router on ConfigureServices with:
services.AddSingleton<AreaRouter>();
Then inject it into the Configure method with:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, AreaRouter areaRouter)
And finally set it in place:
routes.DefaultHandler = areaRouter;
Fixing this problem got me forward, but unfortunetaly I still couldn't get the subdomain routing to work as intended. It seems like routing decision is already made at this point.
Note: I would have only added comment but I can't do that yet.

If you look at the definition of MvcRouteHandler you will see it does not have a parameterless constructor, the ones availables are:
public MvcRouteHandler(IActionInvokerFactory actionInvokerFactory, IActionSelector actionSelector, DiagnosticSource diagnosticSource, ILoggerFactory loggerFactory);
public MvcRouteHandler(IActionInvokerFactory actionInvokerFactory, IActionSelector actionSelector, DiagnosticSource diagnosticSource, ILoggerFactory loggerFactory, IActionContextAccessor actionContextAccessor);
As you're inheriting from it, you must call the base constructor, so usually you would create a constructor with the same parameters and pass it to the base as you don't require any extra parameter:
public AreaRouter (IActionInvokerFactory actionInvokerFactory,
IActionSelector actionSelector, DiagnosticSource diagnosticSource,
ILoggerFactory loggerFactory, IActionContextAccessor actionContextAccessor)
: base(actionInvokerFactory, actionSelector, diagnosticSource,
loggerFactory, actionContextAccessor) { }

Related

.Net Code asp.net Lambda: duplicate AWS cloudwatch logs, one not formatted

This is happening with a .Net Core ASP.Net Lambda application using Microsoft.Extensions.Logging and AWS.Logger.AspNetCore. After (re)deploying/publishing the lambda, the initial call (using Postman) 2 logs show up in CW. E.g.:
2019/02/10T01.29.56 - 7f017ed5-857d-41af-8a6d-624ddc2be024
2019/02/10/[$LATEST]24ab4b53f17d4be3a2507331e968bbf7
The first contains a dozen or so entries, both internal Microsoft MVC messages and my explicit Log calls. For example:
[2/10/19 1:29:58 AM] Debug: Incoming POST requests to /api/carrierprogram/LaunchSendOffer/57e30620-571b-4b68-a86b-2ef4cb53f1a8/28
[2/10/19 1:29:58 AM] Debug: ASP.NET Core Request PathBase: /Stage, Path: /api/carrierprogram/LaunchSendOffer/57e30620-571b-4b68-a86b-2ef4cb53f1a8/28
The second log has slightly different content, and the messages are not formatted:
[40m[32minfo[39m[22m[49m: Controllers.CarrierProgramController[0]
LaunchSendOffer(0) for programId '57e30620-571b-4b68-a86b-2ef4cb53f1a8', offerId '28'
A second request posted to the Lambda returns successfully, but the same explicit log calls recorded on the first call do not show up at all in the first log.
Here's a snippet from Startup.cs that populates the DI services repository
public void ConfigureServices(IServiceCollection services)
{
...
var awsConfig = Log.CompleteAwsConfiguration(Configuration.GetAWSLoggingConfigSection());
var loggerFactory = new LoggerFactory()
.AddAWSProvider(awsConfig,
formatter: (logLevel, message, exception) => $"[{DateTime.UtcNow}] {logLevel}: {message}")
.AddConsole()
.AddDebug();
services.AddSingleton(loggerFactory);
...
}
And how the logger is referenced in a controller class
private static ILogger<CarrierProgramController> _log;
public CarrierProgramController(..., ILogger<CarrierProgramController> logger)
{
_log = logger;
}
and a sample call:
_log.LogInformation($"LaunchSendOffer(0) for programId '{programId}', offerId '{offerId}'");
I suspect this is due to how the application is coded (DI C#), but this is mostly a black box and it's hard to troubleshoot. My questions
Why at there 2 logs generated?
Why are the entries in the second log unformatted (no log level prefix)?
Why do subsequent calls to the Lambda not add to the log(s)?
I replaced the reference to AWS.Logger.AspNetCore with Amazon.Lambda.Logging.AspNetCore. This post shed some light on the behavior: https://github.com/aws/aws-logging-dotnet/issues/52
After replacing the package, there were a few additional changes. I removed the section in the Configuration method in the 7 line snippet above. And in the Configure() method in Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
Added this line:
loggerFactory.AddLambdaLogger(Configuration.GetLambdaLoggerOptions());
All the other changes, e.g. constructors in the controller and service classes were the same. Meaning I didn't have to add the logger/factory back to the services collection for DI.
So to answer my 3 questions - they all seem related to the use of the incorrect package, and switching to Amazon.Lambda.Logging.AspNetCore returned to expected behavior.

How to fix obsolete ILoggerFactory methods?

I upgraded my project to .NET Core 2.2.x and got an obsolete warning regarding the following code - both lines:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
The suggestion to fix is The recommended alternative is AddConsole(this ILoggingBuilder builder). I thought that is what I am using.
What am I missing here?
I had the same issue today.
Remove your logging configuration from Startup.cs and go to your Program.cs file and add something like:
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.Build();
This used the 'builder' because the variable 'logging' is an IloggingBuilder (whereas your code is still using ILoggerFactory)
UPDATE: The other method I just tried is to stay inside Startup.cs but move the logging stuff from the 'Configure' method to 'ConfigureServices' like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddConfiguration(Configuration.GetSection("Logging"));
loggingBuilder.AddConsole();
loggingBuilder.AddDebug();
});
}
Perhaps keeps the Program.cs less polluted...
The documentation's recommendation to use AddConsole(this ILoggingBuilder builder) is correct, but for that to work you need to add a reference to the NuGet package Microsoft.Extensions.Logging.Console.
I got the same warning when I was updating logging code from .Net Core 2.1 to 3.0. The recommended way to do the upgrade is documented on MSDN.
In my case, I was trying to get an instance of LoggerFactory for console which is pretty straightforward in .Net Core 3.0:
using (var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()))
{
// use loggerFactory
}
Don't worry about it - this is the dumbest thing ever!
Note
The following code sample uses a ConsoleLoggerProvider constructor
that has been obsoleted in version 2.2. Proper replacements for
obsolete logging APIs will be available in version 3.0. In the
meantime, it is safe to ignore and suppress the warnings.
In case you thought you forgot what Obsolete meant - you hadn't! Don't worry about it and just ignore it for now - or suppress the warning (sorry I don't have the code for that to hand).
(Wish they'd put a better explanation for why this was done - that's what I mean by dumb.)
According to the issue opened on GitHub for this, the replacement methods are already being called if you use CreateDefaultBuilder() method in your Program.cs.
https://github.com/aspnet/Docs/issues/9829
The only issue I have is that I only turned these on for non-Production environments.. and don't see a way to do so going forward.
If you don't have access to the LoggerFactory.Create(), use can still use the ILoggerFactory with the AddProvider() method giving it a ConsoleLoggerProvider() but it is a bit of a pain if you want to do something simple. The problem is, ConsoleLoggerProvider() requires a IOptionsMonitor<ConsoleLoggerOptions> as a parameter and the easiest thing to do if you
you don't have access to the options mechanism in your code base (my problem), or
the real options mechanisms in your existing code base don't match up with IOptionsMonitor<>, or
you have other reasons not to use the ASP.Net options facilities
is to create a dummy class:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
class DummyConsoleLoggerOptionsMonitor : IOptionsMonitor<ConsoleLoggerOptions>
{
private readonly ConsoleLoggerOptions option = new ConsoleLoggerOptions();
public DummyConsoleLoggerOptionsMonitor(LogLevel level)
{
option.LogToStandardErrorThreshold = level;
}
public ConsoleLoggerOptions Get(string name)
{
return this.option;
}
public IDisposable OnChange(Action<ConsoleLoggerOptions, string> listener)
{
return new DummyDisposable();
}
public ConsoleLoggerOptions CurrentValue => this.option;
private sealed class DummyDisposable : IDisposable
{
public void Dispose()
{
}
}
}
You can then use your ILoggerFactory like:
factory.AddProvider(
new ConsoleLoggerProvider(
new DummyConsoleLoggerOptionsMonitor(LogLevel.Debug)));

ASP Zero :System.IO.FileNotFoundException: Could not find file 'c:\windows\system32\inetsrv\log4net.config'

I have a web project which still working properly until I ve updated some packages
then I faced a run time error with this line of code
//Log4Net configuration
AbpBootstrapper.IocManager.IocContainer
.AddFacility<LoggingFacility>(f => f.UseAbpLog4Net()
.WithConfig(Server.MapPath("log4net.config"))
);
the error message said that "Could not find file" even though the file is exist.
Any answers will be appreciated.
Thanks in advance.
For AspNet Core, you can use HostingEnvironment.ContentRootPath
//Log4Net configuration
AbpBootstrapper
.IocManager
.IocContainer
.AddFacility<LoggingFacility>(f => f.UseAbpLog4Net()
.WithConfig(Path.Combine(_hostingEnvironment.ContentRootPath, "log4net.config")));
PS: You need to inject IHostingEnvironment
private readonly IHostingEnvironment _hostingEnvironment;
public Startup(IHostingEnvironment env)
{
_hostingEnvironment = env;
}
Update-1 : for AspNet MVC
Take a look at the HttpRuntime.AppDomainAppPath
Update-2 : for AspNet MVC - Use tilda for MapPath
Server.MapPath("~\log4net.config")
See https://www.roelvanlisdonk.nl/2009/05/25/asp-net-could-not-find-a-part-of-the-path-cwindowssystem32inetsrv/

ASP.NET Core WebAPI 500 Internal error in IIS 7.5

I'm struggling to get this WebAPI to work. Well, work with IIS. Everything works fine in IIS express, but when I publish it, specifically 1 api request doesn't work. I'm trying to access a url of API/[Controller]/{date}/{integer}. I keep getting the 500 server error. My other route of API/[Controller]/{date} works.
Here's my API Controller:
[Route("api/[controller]")]
public class PostingsController : Controller
{
// GET: api/Postings/5
[HttpGet("{date}")]
public string Get(string date)
{
return date;
}
// GET api/Postings/20160407/2
[HttpGet("{date}/{modeID}")]
public List<TruckPosting> Get(string date, int modeID)
{
TruckPosting tp = new TruckPosting();
List<TruckPosting> truckPostings = tp.GetTruckPostings(date, modeID);
return truckPostings;
}
}
Could the reason be that I'm trying to return a List<>? I'm stumped considering it works fine in VS's IIS Express.
Edit
Here's my startup.cs page:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure1(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseIISPlatformHandler();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseFileServer(true);
app.UseMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.Map("/appRoot", (app1) => this.Configure1(app1, env, loggerFactory));
}
That's a good thought that it might that fact that you're returning a List. We have working Core Web API methods and all of them return Task<IEnumerable<Foo>>. Try changing the return type List<TruckPosting> to Task<IEnumerable<TruckPosting>>
EDIT: To view the details for 500 (internal server) errors you will need to put the following line of code at the beginning of your Configure (or Configure1) method:
app.UseDeveloperExceptionPage();
And obviously this is not something you want in a production environment.
EDIT2: When running in VS you can use the code below to show exception details as long as the Hosting:Environment variable in the Debug section of Properties is set to "Development". After publishing you will need to create a System Environment variable named ASPNET_ENV and set its value to "Development" or the code will not call the UseDeveloperExceptionPage() method.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
I posted an ASP.NET Core custom exception handler on GitHub today. I thought it might be helpful to you since you're building an SPA and are probably calling a lot Web API methods.
It's a middleware project that returns error messages for Web API calls and redirects to an error page for non Web API calls. It can also log the exceptions in a SQL Server database. That's done in a separate thread to help with performance.
The solution includes a demo application that shows how to use the exception handler for both Web API exceptions and non Web API exceptions.
Here's the link.
https://github.com/ClintBailiff/CustomExceptionHandler
If you end up checking it out and have some suggestions or comments, I like to hear them.

Using Startup class in ASP.NET5 Console Application

Is it possible for an ASP.NET 5-beta4 console application (built from the ASP.NET Console project template in VS2015) to use the Startup class to handle registering services and setting up configuration details?
I've tried to create a typical Startup class, but it never seems to be called when running the console application via dnx . run or inside Visual Studio 2015.
Startup.cs is pretty much:
public class Startup
{
public Startup(IHostingEnvironment env)
{
Configuration configuration = new Configuration();
configuration.AddJsonFile("config.json");
configuration.AddJsonFile("config.{env.EnvironmentName.ToLower()}.json", optional: true);
configuration.AddEnvironmentVariables();
this.Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Settings>(Configuration.GetSubKey("Settings"));
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationContext>(options => options.UseSqlServer(this.Configuration["Data:DefaultConnection:ConnectionString"]));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(minLevel: LogLevel.Warning);
}
}
I've tried to manually create the Startup class in my Main method, but this doesn't seem like the right solution and hasn't so far allowed me to configure the services.
I'm assuming there's some way for me to create a HostingContext that doesn't start up a web server but will keep the console application alive. Something along the lines of:
HostingContext context = new HostingContext()
{
ApplicationName = "AppName"
};
using (new HostingEngine().Start(context))
{
// console code
}
However so far the only way I can get this to work is if I set the HostingContext.ServerFactoryLocation to Microsoft.AspNet.Server.WebListener, which starts up the web server.
What you're looking for is the right idea, but I think you'll need to back up a moment.
Firstly, you may have noticed that your default Program class isn't using static methods anymore; this is because the constructor actually gets some dependency injection love all on its own!
public class Program
{
public Program(IApplicationEnvironment env)
{
}
public void Main(string[] args)
{
}
}
Unfortunately, there aren't as many of the services you're used to from an ASP.NET 5 hosting environment registered; thanks to this article and the IServiceManifest you can see that there's only a few services available:
Microsoft.Framework.Runtime.IAssemblyLoaderContainer
Microsoft.Framework.Runtime.IAssemblyLoadContextAccessor
Microsoft.Framework.Runtime.IApplicationEnvironment
Microsoft.Framework.Runtime.IFileMonitor
Microsoft.Framework.Runtime.IFileWatcher
Microsoft.Framework.Runtime.ILibraryManager
Microsoft.Framework.Runtime.ICompilerOptionsProvider
Microsoft.Framework.Runtime.IApplicationShutdown
This means you'll get the joy of creating your own service provider, too, since we can't get the one provided by the framework.
private readonly IServiceProvider serviceProvider;
public Program(IApplicationEnvironment env, IServiceManifest serviceManifest)
{
var services = new ServiceCollection();
ConfigureServices(services);
serviceProvider = services.BuildServiceProvider();
}
private void ConfigureServices(IServiceCollection services)
{
}
This takes away a lot of the magic that you see in the standard ASP.NET 5 projects, and now you have the service provider you wanted available to you in your Main.
There's a few more "gotchas" in here, so I might as well list them out:
If you ask for an IHostingEnvironment, it'll be null. That's because a hosting environment comes from, well, ASP.Net 5 hosting.
Since you don't have one of those, you'll be left without your IHostingEnvironment.EnvironmentName - you'll need to collect it from the environment variables yourself. Which, since you're already loading it into your Configuration object, shouldn't be a problem. (It's name is "ASPNET_ENV", which you can add in the Debug tab of your project settings; this is not set for you by default for console applications. You'll probably want to rename that, anyway, since you're not really talking about an ASPNET environment anymore.)

Categories