In a previous article we talked about using CSRF Tokens to protect against CSRF attacks. But their main usage was in using the Razor helpers to build a web application in ASP.net Core. But what if you are building a SPA using something like AngularJS? Your forms will no longer have access to the Razor helpers, but you still want to protect against CSRF exploits.

Luckily AngularJS actually has a handy “helper” that will add CSRF tokens as a header automatically as long as it can find a particular cookie. And in actual fact, even though Angular does most of the leg work for us and makes it easy, you can use any flavor of javascript frameworks using this pattern. At the end of the article we even show how you can get up and running using something as plain as jQuery. Let’s jump into it!

Anti Forgery Setup

Later on we will delve into how AngularJS works with CSRF Tokens, but for now what you need to know is that Angular will be sending the token in a header called “X-XSRF-TOKEN”. We need to let our API know this and expect it.

Inside your startup.cs inside your ConfigureServices method, you will need a call to “AddAntiforgery” and we will set the headername inside there.

Setting The Cookie On The Initial Request

Now we need to return a cookie to our front end. There are 2 different ways to do this. You create an endpoint that does nothing more than return a response code of 200 and returns the cookie in the response, or you make it so when your index file is returned, the cookie is returned along with it.

Let’s go with the latter for now.

If you are returning your index file as a C# view, then you will need to inject the IAntiforgery service into your controller, generate a new token, and set it as a cookie. It looks like this :

Now importantly, the cookie name is XSRF-TOKEN and not X-XSRF-TOKEN. The cookie is missing the X on purpose (This catches people out!). And the other important thing is that HttpOnly is set to false on the cookie meaning that javascript is able to read the cookie. Generally speaking, if the cookie holds any sensitive info (Such as authentication cookies), you do not want to do this, but here it’s required.

If you are using a static index.html file that is not returned as a view. You will need to use middleware instead. In your startup.cs file you will have a method called “Configure”. On this method you need to add a parameter called IAntiforgery. Don’t worry too much about the order as this is all worked out using the servicecollection anyway. From there you will want to check if the user is requesting the root file (index.html) and if they are add a cookie onto the response.

Important! This middleware needs to come before the call to UseStaticFiles. The StaticFiles middleware short circuits the pipeline and returns the static file immediately meaning your cookie won’t be set.

Setting The Cookie On Demand

A common scenario is going to be that the API and the front end are actually two seperate websites probably hosted on different boxes. With this in mind you won’t be serving any of the front end code from the API box, so you can’t set the cookie when the index.html file is requested.

In this scenario, you will need to setup an endpoint that will return the token for you. This could actually be after a user logs on, or it could be an endpoint that does nothing more than return a 200 and sets the cookie (Possibly with expiration). It will look something like this :

The front end can call this endpoint at any point to generate a new CSRF token.

Using AngularJS

So what is there left to do in AngularJS? Well… actually nothing.

When $http is used inside Angular, it will now go ahead and look through your browser cookies for something named “XSRF-TOKEN”. If it finds it, it will then add a header to every request called “X-XSRF-TOKEN” to every request. You don’t have to set anything special up inside AngularJS… Phew.

Using jQuery

So jQuery isn’t going to be quite as easy as AngularJS, but the process will still be the same.

We are going to use the handy JS Cookie library that allows us to easily grab out values from Cookies from the front end. Then we are going to take that token and send it as a header with the name X-XSRF-TOKEN.

CSRF or Cross Site Request Forgery is a type of web attack that uses a users own browser to post a form from one site to another. It works like so :

  • User logs into www.mybankaccount.com and receives a cookie.
  • Sometime later the user goes to www.malicioussite.com and is shown a completely innocuous form on the surface. Something like :
  • If a user clicks the submit button. Because they are still logged in and have a cookie, the POST request is accepted

Unsurprisingly Wikipedia actually has a pretty good article on the subject and explains it a little better than I do : https://en.wikipedia.org/wiki/Cross-site_request_forgery

The most common way to protect against CSRF attacks is by using a token that is returned on a GET request for a form, and must be present for a POST request to complete. The token must be unique to the user (Cannot be shared), and can either be per “session” or per “request” based. As always, .net core has done most of the leg work for us.

Generating Tokens

If you build a form using ASP.net core helpers then a CSRF token is automatically generated for you without any extra code required.

So both of these :

and

End up generating a form with a request verification token built in :

If you are building forms without using the ASP.net helpers, then you can generate a token manually :

Validating Tokens

What good is generating tokens if you don’t validate them! There are two filters in ASP.net core to validate tokens as part of the request pipeline :

  • ValidateAntiForgeryToken – This filter validates the request token on each and every method it’s placed on regardless of the HTTP verb (So it validates even on GET requests).
  • AutoValidateAntiforgeryToken – This filter is almost the same, but it doesn’t validate tokens on GET, HEAD, OPTIONS and TRACE requests.

Typically you are going to use the second option. Very rarely are you going to want to validate GET requests, but it’s there if you need it.

You can apply the filters on an entire controller or action by using it as an attribute :

Or you can apply the filter globally if you prefer. In your startup.cs file, find your ConfigureServices method. You should already have a call to AddMvc but you can add a global filter like so :

Adding A Passthrough

You may end up with an endpoint that you actually don’t want to validate CSRF tokens. It may legitimately be receiving POST requests from an outside source. If you have already applied the validate attributes to your controller or globally but need to allow an exception through, you can use the IgnoreAntiforgeryToken attribute on both an Action or a Controller.

 

ASP.net core comes with two ways to do authentication out of the box. The first is using ASP.net cores “identity” service that wraps a MSSQL set of tables and allows you to get every piece of functionality out of the box, but does require you to have a very rigid structure and do things the “Microsoft” way. It also requires you to use a database adapter that has been pre-built (Typically this means using Microsoft SQL). If you are using something like a NoSQL database store to your users, or you authenticate your users using something other than a username password combination, then using the full identity service can be a bit of a pain.

The alternative is to use ASP.net Core’s “Cookie Authentication” which allows you to use cookie based claims for a user, but authenticate the users yourself using your own data store, however that may work. That’s what this article is going to focus on.

Setup

If you are creating a brand new project using Visual Studio for this tutorial, ensure that when creating your project you select the “authentication type” to be none. We essentially want to start with a clean slate.

In your nuget package manager window, install the following package :

In your startup.cs file, find your configure method. You should add the following code to your pipeline but you must ensure this is before your call to “AddMvc”. This is extremely important. Your pipeline runs in the order you have added it so you need to obviously have authorization run before MVC kicks in.

A little bit about the options we have set here.

AuthenticationScheme is just the name we have given this instance of authentication. You can actually have multiple authentication middleware in your application. The name isn’t too important and can be anything if you are only having the single login.

LoginPath is the path to your login page

AccessDeniedPath is the path to a “denied” page

AutomaticAuthenticate means that the middleware runs automatically on every request and tries to authenticate the user

AutomaticChallenge means that a user should automatically be routed to the LoginPage if the user is not authorized.

Registering A User

Using Cookie Authentication as a simple middleware and not using the full identity service means you need to organize registering users and creating them in your data store yourself. There is nothing in the middleware to help you out there.

Logging In A User

For logging in, we will create a simple post model to accept our username and password combination.

I’ve created a controller called “AccountController”. Inside I’ve created a GET and POST endpoint for our model. The GET endpoint returns a view that has a simple form for logging in, the POST endpoint is where we do our work.

Let’s walk through that code a little.

First we validate the user. In our example we just validate every user but obviously in real life this would go away to our data store and validate the user/password combination.

Next we create a Claims list. These are things we know about the user that we wish to store on the cookie. In this example I’ve just stored the Username as the “Name” claim. This one is slightly important as we see later as there is a shortcut to get this name, but you can actually store any key/value combination inside the claims if you don’t want to have to go back to the database each time to get info on the logged in user.

We then build an identity, a principal, and then set the cookie using the SignInAsync method. There is a bit of cruft going on here in terms of building the identity and principal, but the important part is building claims and calling signin.

Authorizing Controllers

Next we obviously need to learn how to “lockdown” certain areas of our website to force a user to have to login. We can do this simply by adding an “Authorize” attribute to any controller.

And that’s all there is to it! Any user that navigates to this controller will be redirected to your “Logon” url.

Logging Our A User

Logging out a user is very simple.

That’s really all there is too it! Remember the Authentication Scheme you use here must match the one you set in the middleware setup.

Viewing The Logged In User

Obviously at some point we will want to view the logged in user, either to get further details about the user from the database or set onscreen elements in a view. Here we have some example code on how we can get both the username we set earlier, and any other claim we may have set.

The Name on the Identity object is just a fancy way of getting the “Name” claim that we set earlier, but for any other claim you can work with the claims list and fetch our data. Remember this will only be set after logging in so null checks should be done along the way.

In ASP.net core there is this concept of “environments” where you set at the machine level (Or sometimes at the code level) what “environment” you are in. Environments may include development, staging, production or actually any name you want. While you have access to this name via code, the main uses are actually to do configuration swaps at runtime. In full framework you might have previously used web.config transforms to transform the configuration at build time, ASP.net core instead determines it’s configuration at runtime.

It should be noted that there are two uses of the word “environment variables” being used here. The first is that “Environment Variables” are used to describe the actual definition of an environment variable on your machine. The second is that you can add an “environment” to that environment variables list to describe what ASP.net Core environment you are using… That may sound confusing but it will all come together at the end!

Setting Environment Variable In Visual Studio

When developing using Visual Studio, any debugging is done inside an IIS Express instance. You can set Environment variables for debugging by right clicking your project, selecting properties, and then selecting “Debug” on the left hand menu. By default you should see that a variable has been added for you called “ASPNETCORE_ENVIRONMENT” and it’s value should be Development.

You can set this to another value (like “Staging”), when you want to debug using another configuration set. It should be noted that editing this value actually edits your “launchSettings.json” and sets the variable in there. It ends up looking like this :

Setting Environment Variable Machine Wide

You will need to set the ASP.net core environment machine wide if…

  • You are not using Visual Studio/IIS Express
  • You are running the website locally but using full IIS
  • You are running the website on a remote machine for staging/production

To set your entire machine to an environment you should do the following :

For Windows in a Powershell window

For Mac/Linux edit your .bashrc or .bash_profile file and add

It should be noted that setting this up machine wide means that your entire machine (And every project within it) is going to run using that environment variable. This is extremely important if you are currently running multiple websites on a single box for QA/Staging purposes and these each have their own configurations. Setting the environment machine wide may cause issues because each site will think they are in the same environment, if this is you, read the next section!

Setting Environment Variable via Code

You can go through mountains of official ASP.net core documentation and miss the fact that you can set the ASP.net Core Environment through code. For some reason it’s a little known feature that makes the use case of multiple websites with different environments live on the same machine.

In your ASP.net core site, you should have a program.cs. Opening this you will see that you have a WebHostBuilder object that gets built up. You can actually add a little known command called “UseEnvironment” like so :

Obviously as a constant string it’s probably not that helpful, but this is regular old C# code so how you find that string is up to you. You could load a text file for example and from there decide which environment to use. Any logic is possible!

Accessing Environment via Code

To access the environment in your code (Such as a controller), you just have to inject IHostingEnvironment into your controller. The interface is already setup by calling “AddMvc” in your ConfigureServices method so you don’t need to add the interface to your service collection manually.

Here is some example code that returns the environment I am currently in :

Swapping Config Based Off Environment

In your startup.cs there is a method called “Startup” that builds your configuration. By default it adds a json file called “appsettings.json” but it can be used to add a file called “appsettings.{environment}.json where environment is swapped out for the environment you are currently running in. The code looks like this :

Any settings in your base appsettings.json are overwritten using your environment specific file.

Configuration itself is a massive topic and Microsoft has already done an incredible job of it’s documentation so head over and read it here.

Naming Conventions Per Environment

Again, another little known “feature” of using environments is that naming conventions can be used to completely change how your application starts. By default, you should have a file called startup.cs that contains how your app is configured, how services are built etc. But you can actually create a file/class called “startup{environmentname}.cs” and it will be run instead of your default if it matches the environment name your app is currently running in.

Alternatively, if you stick with a single startup.cs file, the methods Configure and ConfigureServices can be changed to Configure{EnvironmentName} and ConfigureServices{EnvironmentName} to write completely different pipelines or services. It’s not going to be that handy on larger projects because it doesn’t call both the default and the environment specific methods, it only calls one which could lead to a lot of copy and pasting. But it’s handy to know!

In 99% of cases, static files you want served for your website will be able to live in the wwwroot folder. But in that rare 1% use case that you need to have static files littered throughout your project (Or you dislike having a wwwroot folder nonstop!), what can you do?

First off, you will need the StaticFiles nuget package to be able to serve files at all. So run the following from your nuget package manager :

In your startup.cs, you would typically see something similar to the following where the “UseStaticFiles” call allows you to serve files from your wwwroot folder :

But you can actually add multiple calls to StaticFiles each with it’s own file location :

We leave the original StaticFiles call in there so that the wwwroot is still used to serve static files if we want (Although if you are intending to never use it, you can remove that call completely), but we can add subsequent calls to the middleware for each additional static content folder.

The FileProvider parameter tells the middleware where it can find the physical files, and the RequestPath is the actual URL that a browser should go to to reach the static files. In this example if we we went to something like www.mysite.com/content/js/site.js, we would be served static files.

Modifying the RequestPath can come in handy, consider the following :

In this example our physical folder hasn’t changed, but the request path is now simply /js rather than /content/js. You can use this to essentially rewrite public urls for your static content (Which can come in handy if you end up moving around the actual physical folder).

If you’re up to date on your dotnet tooling, then you are probably using the very latest Dotnet Nuget command (dotnet add package). And then on top of that you are wondering where the hell are all your packages, and where is your packages.config? I know atleast for me it was extremely confusing adding a nuget package and not seeing a packages folder fill up. So let’s run through some of the changes.

What Is The Dotnet CLI Command “dotnet add package” Actually Doing?

So when you run the command “dotnet add package”, what is it doing under the hood? Well actually, when it boils down to it, it’s actually just forwarding your command to the “nuget.exe” anyway. If you dig into the source code eventually you end up at a class called “NugetForwardingApp” (Source) that takes your dotnet command and transforms it to the original nuget command anyway. This makes sense as nuget will still be used with the .net full framework so it’s pointless for the .net core team to completely rebuild a package manager from scratch right?

As a side note, you’ll often find this with dotnet CLI commands. Often they are just a wrapper or a skin over things like nuget or msbuild to make your life a little easier (Because who hasn’t wasted a day trying to get a finicky msbuild command to work!).

Where Is My packages.config?

Previously when you added a nuget package, you would end up with a packages.config file in the root of your project directory that told nuget what dependencies you had. You’ll quickly notice that this is not the case anymore, so where are your dependencies defined? In the .csproj of course. Let’s say I create a new project with a reference to EntityFramework, my csproj would look a bit like the following :

The csproj format that is used for .net core projects is now very very clean, so it makes viewing your dependencies as easy as viewing your packages.json. The other benefit is that you have all your dependencies in a single file. When you add project references (Or hardcoded DLL references), it’s all contained within the csproj.

Where Is My Packages Folder?

You are probably used to having a packages folder in your solution that holds the nuget downloads. This is now gone and been replaced by a global packages folder. By default this is located at “%userprofile%\.nuget\packages”. Opening this folder the first time might give you a heart attack with how much is in here, and I suspect that “cleaning” this folder in the future is going to be the fix for various bugs.

It certainly makes sense to have a global folder on some levels though as for many projects you will be using the exact same version of the same library across many projects. With ASP.net core itself being inside nuget, it also makes sense to not have to download this over and over for each project.

It’s also important to note that the location of “%userprofile%\.nuget\packages” is just the default, you are able to edit your nuget.config either on the global level or per project level to specify another location. But be aware if you do this on the project level and give each project it’s own packages folder, you will download all of ASP.net core into that packages folder all over again.

What Happens When I Publish?

When it comes to publishing, there is really nothing to worry about. When you publish, .net core works out what packages you need and moves them into your publish destination folder for a stand alone solution. You don’t for example need this packages folder on your server, it’s only for development purposes.

Editing Nuget.config

As discussed earlier, you can edit your nuget.config file to change your local global cache location, add nuget feeds, and configure other settings such as HTTP Proxies, default package versions etc. The Microsoft documentation for editing your nuget.config can be found here. A handy tip to generating a nuget file at the project level is the following command from a command line/bash/terminal.

Be aware that adding a nuget file at the project level (even a completely empty one) can have bad side effects. I’ve found that settings don’t exactly “fall through”, e.g. If a setting is missing from the project level nuget.config, it doesn’t then go to the global config, it just treats it as false/missing etc.

While .net core ships with the service collection dependency injection framework as a first class citizen, some developers may still prefer to stick with their third party DI framework they used in full framework .net. And for good reason, the .net core DI is rather basic when it comes to options for injecting and auto binding. Things like auto binding all interfaces, or being able to inject different services based on parameter names, class names etc are all lost.

Luckily Autofac, LightInject, DryIOC and StructureMap do have the ability to be used in .net core (With others slowly making the move), but there are a couple of gotchas that you need to go through. Namely you have to remember that things like IOptions, ILogger, HttpContext and other framework classes need to be available in the DI framework by default. On top of that, libraries that already use the ServiceCollection of .net core need to be able to be dropped in and used as per normal.

Almost all third party DI frameworks have adopted the same pattern for .net core. The general steps for any third party DI library are :

  • ConfigureServices in your startup.cs file should return IServiceProvider not void
  • You should register framework services as you normally would within the .net core service collection
  • Build your IOC container and register services as normal
  • Push the .net core services collection into the IOC container
  • Build your container and return the service provider

Sound complicated? Don’t worry, sample code will be provided for each DI framework as well as any other gotchas you have.

Autofac In ASP.net Core

First install the following Nuget package :

Here is the code for your startup.cs :

DryIOC In ASP.net Core

First install the following Nuget package :

DryIOC prefers to keep it’s registrations out of the startup.cs file (Which is probably a good idea). So create a class called “CompositionRoot”, that has a constructor that takes IRegistrator, and register your services inside there.

Then your startup.cs should look like the following :

LightInject In ASP.net Core

First install the following Nuget package :

Here is the code for your startup.cs:

StructureMap In ASP.net Core

First install the following Nuget package :

Note I also had an issue where I had to install StructureMap itself manually for some reason. If things aren’t working correctly (e.g. Intellisense is going wild), install the following Nuget package.

StructureMap is another IOC container that prefers a separate class(s) to handle your registrations. Let’s just create a simple ServicesRegistry class.

In your startup.cs add the following :

Unity In ASP.net Core

Unity is close to being dead in the water. It would appear according this Github issue that .net core support won’t be coming any time soon.

Windsor In ASP.net Core

At the time of writing, Windsor does not have support for ASP.net core. You can follow the progress in the Github Issue here.

Ninject In ASP.net Core

At the time of writing, Ninject does not have support for ASP.net core

If you’ve never used a dependency injection framework before, then the new Services DI built into .net core could be a bit daunting. Most of all, understanding the differences between transient, singleton and scoped service registrations can be easy to begin with, but tough to master. It seems simple on the service, “register this interface as this service”, but there is a couple of gotchas along the way. Hopefully after reading this, you will have a better grasp on the different types of lifetimes you can use within your application, and when to use each one.

Transient Lifetime

If in doubt, make it transient. That’s really what it comes down to. Adding a transient service means that each time the service is requested, a new instance is created.

In the example below, I have created a simple service named “MyService” and added an interface. I register the service as transient and ask for the instance twice. In this case I am asking for it manually, but in most cases you will be asking for the service in the constructor of a controller/class.

This passes with flying colors. The instances are not the same and the .net core DI framework creates a new instance each time. If you were creating instances of services manually in your code without a DI framework, then transient lifetime is going to be pretty close to a drop in.

One thing that I should add is that there was a time when it was all the rage to stop using Transient lifetimes, and try and move towards using singletons (Explained below). The thinking was that instantiating a new instance each time a service was requested was a performance hit. Personally in my experience this only happened on huge monoliths with massive/complex dependency trees. The majority of cases that I saw trying to avoid Transient lifetimes ended up breaking functionality because using Singletons didn’t function how they thought it would. I would say if you are having performance issues, look elsewhere.

Singleton Lifetime

A singleton is an instance that will last the entire lifetime of the application. In web terms, it means that after the initial request of the service, every subsequent request will use the same instance. This also means it spans across web requests (So if two different users hit your website, the code still uses the same instance). The easiest way to think of a singleton is if you had a static variable in a class, it is a single value across multiple instances.

Using our example from above :

We are now adding our service as a singleton and our Assert statement from before now blows up because the two instances are actually the same!

Now why would you ever want this? For the most part, it’s great to use when you need to “share” data inside a class across multiple requests because a singleton holds “state” for the lifetime of the application. The best example was when I needed to “route” requests in a round robin type fashion. Using a singleton, I can easily manage this because every request is using the same instance.

Transient Inside Singletons

Now the topic of “transient inside singletons” probably deserves it’s own entire article. It’s the number one bug when people start introducing singletons to their applications. Consider the following two classes.

So we have two services, one named “MySingletonService” and one named “MyTransientService” inside it.

Then our services registration looks like the following.

If we ran this, what could we expect? It actually blows up on the second Assert. But why? In our very first example at the top of the page, when we registered a service as Transient they weren’t the same but now they are? What gives?!

The reason lies in the wording of how DI works. Transient creates a new instance of the service each time the service is requested. When we first request an instance of the parent class as singleton, it creates that instance and all it’s dependencies (In this case our transient class). The second time we request that singleton class, it’s already been created for us and so doesn’t go down the tree creating dependencies, it simply hands us the first instance back. Doh! This is also true of other types of lifetimes like scoped inside singletons.

Where does this really bite you? In my worst experience with Singletons, someone had decided the smart thing to do in their application was make the entire service layer singletons. But what that meant was that all the database repository code, entity framework contexts, and many other classes that should really be transient in nature then became a single instance being passed around between requests.

Again, use Singleton if you have a real use for it. Don’t make things singleton because you think it’s going to save on performance.

Scoped Lifetime

Scoped lifetime objects often get simplified down to “one instance per web request”, but it’s actually a lot more nuanced than that. Admittedly in most cases, you can think of scoped objects being per web request. So common things you might see is a DBContext being created once per web request, or NHibernate contexts being created once so that you can have the entire request wrapped in a transaction. Another extremely common use for scoped lifetime objects is when you want to create a per request cache.

Scoped lifetime actually means that within a created “scope” objects will be the same instance. It just so happens that within .net core, it wraps a request within a “scope”, but you can actually create scopes manually. For example :

In this example, the two scoped objects aren’t the same because created each object within their own “scope”. Typically in a simple .net core CRUD API, you aren’t going to be manually creating scopes like this. But it can come to the rescue in large batch jobs where you want to “ditch” the scope each loop for example.

Instance Lifetime

In early versions of .net core (And other DI frameworks), there was an “Instance” lifetime. This allowed you to create the instance of a class instead of letting the DI framework build it. But what this actually meant was that it essentially became a “singleton” anyway because it was only “created” once. Because of this Microsoft removed the Instance lifetime and recommended you just use AddSingleton like the following.

 

Logging in any application is always a contentious issue with many developers rolling their own framework and tacking on third party libraries such as Nlog or Log4net. While these approaches are fine, Microsoft have come in and made logging a first class citizen in ASP.net core. What that means is a standardized way of logging that is both simple and easy to get up and running, but also extensible when you have more complicated logging needs.

The Basics

This guide assumes you have a basic project up and running (Even just a hello world app), just to test how logging works. For getting up and running, we are going to use the debug logger. For that, you need to install the following Nuget package :

In your startup.cs file, in the Configure method, you need to add a call to AddDebug on your logger factory. This may already be done for you depending on which code template you are using, but in the end it should end up looking a bit like the following :

Let’s try this out first. Run your web project and view any URL. Back in Visual Studio you should view the “Output” window. If you can’t see it, go Debug -> Windows -> Output. After viewing any URL you should see something pretty similar to :

The content isn’t too important at this stage, but the import thing is that you are seeing logging in action! Of course it is a bit verbose and over the top, but we will be able to filter these out later.

That sort of automatic logging is great, but what we really want is to be able to log things ourselves. Things like logging an exception or fatal error that someone should look into immediately. Let’s go into our controller. First we need to add a dependency on ILogger in our constructor and then we need to actually log something. In the end it looks a bit like this :

Great, now when we go into this action what do we see in the debug window?

You will notice that our log messages are shown with the level that they are logged at, in this case Information and Error. At this point it doesn’t make too much a difference but it will do in our next section.

See how easy that was? No more fiddling around with nuget packages and building abstractions, the framework has done it all for you.

Logging Levels

The thing is, if we rolled this into production (And used a logger that pushes to the database), we are going to get inundated with logging about every single request that’s coming through. It creates noise that means you can’t find the logs you need, and worse, it may even cause a performance issue with that many writes.

An interesting thing about even the Microsoft packaged loggers is that there isn’t necessarily a pattern for defining their configuration. Some prefer full configuration classes where you an edit each and every detail, others prefer a simple LogLevel enum passed in and that’s it.

Since we are using the DebugLogger, we need to change our loggerFactor.AddDebug method a bit.

What this says is please only log to the debug window errors we find. In this case we have hardcoded the level, but in a real application we would likely read an appSetting that was easily changeable between development and production environments.

Using the same HomeController that we used in the first section of this article, let’s run our app again. What do we see in the debug window?

So we see it logging our error message but not our information message or the system messages about pages being loaded. Great!

Again, It’s important to note that each logger may have their own configuration object that they use to describe logging levels. This can be a huge pain when swapping between loggers, but it’s all usually contained within your startup.cs.

Logging Filters

Most (if not all) loggerFactory extensions accept a lambda that allows you to filter out logs you don’t want, or to add extra restrictions on your logging.

I’ll change the AddDebug call to the following :

In this case, “category” stands for our logging category which in real terms is the full name (Namespace + Class) of where the logger is being used. In this case we have locked it down to HomeController, but we could just as easily lock it to “LoggingExample”, the name of this test app. Or “Controllers”, the name of a folder (And in the namespace) in our example app.

Under this example and running our code again, we see that we log everything from the controller (Information & Error), but we log nothing from the system itself (Page requests etc).

What you will quickly find is that this Lambda will get crazy big and hard to manage. Luckily the LoggerFactory also has an extra method to deal with this situation.

Using this, you can specify what level of logging you want for each category. LogLevel.None specifies that you shouldn’t log anything.

Framework Loggers

ASP.net Core has a set of inbuilt loggers that you can make use of.

AddConsole
Writes output to a console. Has serious performance issues in production so only to be used in development.

AddDebug
What we have used above! Writes output to the debugger. Again, a dev only tool.

AddEventSource
Only available on Windows, writes events out to an ETL. You generally need to use a third party tool such as PerfView to be able to read the logs.

AddEventLog
Writes out logging to the EventLog (Windows only). Again, pretty nifty for logging errors especially when you have a devops team looking after the boxes, they like seeing issues in the Event Log!

AddAzureWebAppDiagnostic
If you are on Azure, this writes out logs to Azure diagnostics. This is very handy and if you are in Azure, it’s a must do as other types of logging (Such as to a database), may fail because of network issues which are causing the errors in the first place.

Third Party Loggers

NLog
https://github.com/NLog/NLog.Extensions.Logging – NLog is usually my go to for the simple fact that it in turn has many extensions into things like Logentries. Obviously there are other more simple options such as logging to a file or sending an email.

Elmah
https://github.com/elmahio/Elmah.Io.Extensions.Logging – Elmah is mostly popular because of the dashboard that it comes with. If you are already using Elmah, then this is an easy slot in.

Serilog
https://github.com/serilog/serilog-extensions-logging – Serilog is gaining in popularity. Again, another easy extension to get going that has a lot of versatility.

Anyone that has used the full .net MVC framework has spent many an hour trying to rejig the web.config and custom MVC filters to get custom error pages going. Often it would lead you on a wild goose chase around Stack Overflow finding answers that went something along the lines of “just do this one super easy thing and it will work”… It never worked.

.net Core has completely re-invented how custom errors work. Partly because with no web.config, any XML configuration is out the window, and partly because the new “middleware pipeline” mostly does away with the plethora of MVC filters you had to use in the past.

Developer Exception Page

The developer exception page is more or less the error page you used to see in full .net framework if you had custom errors off. That is, you could see the stack trace of the error and other important info to help you debug the issue.

By default, new ASP.net core templates come with this turned on when creating a new project. You can check this by looking at the Configure method of your startup.cs file. It should look pretty close to the following.

Note that checking if the environment is development is so important! Just like the CustomErrors tag in the full framework, you only want to leak out your stacktrace and other sensitive info if you are debugging this locally or you specifically need to see what’s going on for testing purposes. Under no circumstances should you just turn on the “UseDeveloperExceptionPage” middleware without first making sure you are working locally (Or some other specific condition).

Another important thing to note is that as always, the ordering of your middleware matters. Ensure that you are adding the Developer Exception Page middleware before you are going into MVC. Without it, MVC (Or any other middleware in your pipeline), can short circuit the process without it ever reaching the Developer Exception page code.

If your code encounters an exception now, you should see something similar to the following :

As we can see, we get the full stack as well as being able to see any query we sent, cookies and other headers. Again, not things we want to start leaking out all over the place.

Exception Handler Page

ASP.net core comes with a catch all middleware that handles all exceptions and will redirect the user to a particular error page. This is pretty similar to the default redirect in the CustomErrors attribute in web.config or the HandleError attribute in full framework MVC. An important note is that this is an “exception” handler. Other status code errors (404 for example) do not get caught and redirected using this middleware.

The pattern is usually to show the developer error page when in the development environment, otherwise to redirect to the error page. So it might look a bit like this :

You would then need to create an action to handle the error. One very important thing to note is that the ExceptionHandler will be called with the same HTTP Verb as the original request. e.g. If the exception happened on a Post request, then your handler at /home/error should be able to accept Posts.

What this means in practice is that you should not decorate your action with any particular HTTP verb, just allow it to accept them all.

Statuscode Pages

ASP.net core comes with an inbuilt middleware that allows you to capture other types of HTTP status codes (Other than say 500), and be able to show them certain content based on the status code.

There are actually two different ways to get this going. The first is :

Using “StatusCodePagesWithRedirects” you redirect the user to the status page. The issue with this is that the client is returned a 302 and not the original status code (For example a 404). Another issue is that if the exception is somewhere in your pipeline you are essentially restarting the pipeline again (And it could throw the same issue up).

The second option is :

Using ReExecute, your original response code is returned but the content of the response is from your specified handler. This means that 404’s will be treated as such by spiders and browsers, but it still allows you to display some custom content (Such as a nice 404 page for a user).

Custom Middleware

Remember that you can always create custom middleware to handle any exception/status code in your pipeline. Here is an example of a very simple middleware to handle a 404 status code.

On our way back out (Remember, the code after the “next” is code to be run on the way out of the pipeline), we check if the status code was 404, if it is then we return a nice little message letting people know we 404’d.

If you want more info on how to create a custom middleware to handle exceptions (Including how to write a nice class to wrap it), check out our tutorial on writing custom middleware in asp.net core.