Adding Global Error Handling and Logging in ASP.NET Core with IExceptionHandlerPathFeature

tldr;

In ASP.NET Core you can hook up a route to be your global exception handler.  Simply add the ExceptionHandler middleware to your Startup.cs Configure method:

In your HomeController’s Error action, add this code to get the exception that was raised, as well as the route that caused the exception, and then you can do something with it (log to a database, send an email/text/fax/carrier pidgeon).

You can checkout this GitHub commit if you’d prefer to see it that way.

The start of the non-tldr;

No ELMAH in ASP.NET Core… yet

In pre-Core versions of ASP.NET, ELMAH was a popular library for doing Global Exception Handling.  When unhandled exceptions occurred, it let you do things like log them to a database, send an e-mail, etc. automagically.  Unfortunately, ELMAH is not ported to ASP.NET Core yet, so this led me to look at what other options were out there.

You could use one of the many of the Cloud providers for this like Application Insightselmah.io (unaffiliated with the ELMAH project) or Exceptionless, but what if your company requires you to stay on-prem?  You can use Exception Filters, but those have some limitations, and I found a simpler way (arguably) that I didn’t find a blog post on, so I thought I’d write one up.

Step 1 – Hook up the ExceptionHandler middleware

If you’ve done much with ASP.NET Core, this will look very familiar to you.  First you need to hook up the ExceptionHandler middleware by adding this line to the Configure method of your Startup.cs:

What that does is anytime an uncaught exception happens, it will send the request to the route listening on the path you  specified.  In the example above (and the default in the templates), that’s the Error Action on the Home Controller, obviously you can just change that to whatever you want.

Often, you’ll only want to use the Exception Handler in non-development environments, and instead use the DeveloperExceptionPage in Development, which is the new YSOD that gives you a stack trace and other good information.

A sample Configure method showing this is below:

Step 2 – Use IExceptionHandlerPathFeature to get the exception and the Path

Make sure you have the Microsoft.AspNetCore.Diagnostics NuGet package in your .csproj, or if you have the Microsoft.AspNetCore meta-package, then that contains the Microsoft.AspNetCore.Diagnostics package, so you’re all set.

In the action of the controller you’re listening on (in the above case, the Error action on the Home Controller).  Add the following code to your Error action.

Breaking the code down

The code is pretty straight forward, but let’s break it down.  When an exception occurs, an IExceptionHandlerPathFeature is set.  This contains two properties: 1) the Path of the route at which the exception occurred (such as /Home/Index), and 2) the Exception that occurred under the Error property.  After that, you can do whatever you want with this information such as log it to a database, send an e-mail or trigger a carrier pidgeon (I believe the CarrierPidgeon API is coming back in .NET Standard 2.0).

If you want to see the code that makes IExceptionHandlerPathFeature work, you can view go to GitHub here and see the code where they set the Path in the ExceptionHandler middleware that we rigged up in our Startup class.

Make sure you get the right Feature

Be careful to make sure you get an IExceptionHandlerPathFeature with the Path in the name.  There is also a IExceptionHandlerFeature without Path in the name that does not have the Path of where the exception occurred.  Obviously, it’s pretty useful to know the source route of where the exception happened.

Make sure you catch any Exceptions in your Exception Handler Action

Also be careful and make sure to catch any exceptions in your Exception Handler Action.  Causing a second exception in your exception handler action will cause the entire middleware to stop and you’ll end up with a blank page and a 500 status code.  A scenario where this could happen is if you’re logging the exception to a database, but your database is down causing an exception.

Microsoft.AspNetCore.Diagnostics.Elm

Microsoft provides a package called ELM (Error Logging Middleware) that has some similar features to what ELMAH did.  One of the main features ELM has is it allows you to see events on a web page that aggregates those events.  You can read more about that here.  However, there doesn’t seem to be any intent to extend ELM to do things like report to a database or send e-mails, based on this response from Eilon of the ASP.NET team here, and it is non-persistent through app restarts.  In my scenarios, we don’t have a ton of use for this, as we prefer to be able to look and query for logs in a database, but I thought I’d mention it in case it is useful for your scenarios.

Additionally – AppDomains Coming Back in .NET Standard 2.0

If you’re using .NET Core (and not full framework), AppDomains are coming back in .NET Standard 2.0 to help with global exception handling, as described by Damian Edwards in the ASP.NET Community Standup here: https://youtu.be/_3y0QsXj2e4?t=31m17s 

Don’t like using IExceptionHandlerPathFeature in your Controller action?

That’s ok.  There’s another way to achieve this same thing using raw middleware, even bypassing IExceptionHandlerPathFeature completely, but I’ll save that for a followup blog post, as this is approaching 1000 words.

Thanks for reading!

Precompiling MVC Views in ASP.NET Core with .csproj’s

I didn’t see this documented on docs.asp.net yet for .csproj style projects, so I thought I’d blog about it, as it’s something I do on every project.

tldr;

Add this to your .csproj file:

2017-03-10_13-02-21.png

Alternatively, you can check out this GitHub commit that just adds pre-compiled views here.

Also – if you’re running on .NET Core and not Full Framework, then make sure your TargetFramework is netcoreapp1.1+ and not netcoreapp1.0.

What does precompiling views do?

Without precompiling your views, none of your views are compiled until they are requested for the first time.  This results in an extra few seconds of wait time for the end user.  Instead, most developers want to take a little bit of extra time in the publish step to precompile their views, so that users don’t have to eat that performance hit on the first request on every page.

Another benefit of precompiling your views is if there is a C# error on one of your .cshtml Razor pages (such as you changed the name of a property on your View Model and the rename didn’t update your .cshtml file), you will find out up front when you go to publish (via VS or a build server) rather than at run time when a user goes to request that page.

So now that we’ve seen what it does, let’s look at what it used to be in project.json.

What did it look like in project.json?

In project.json land, the pre-compiling of Views was a little verbose.  You had to do the following:

  1. Add a reference to “Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design” under the “dependencies” section
  2. Add a reference to “Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Tools” under the tools section.
  3. Under buildOptions, set “preserveCompilationContext”to true
  4. And finally you had to add a fairly verbose post-publish script to actually use the pre-compile tool.All of that looked like this (removing the rest of the project.json for brevity):

This then generates a <ProjectName>.PrecompiledViews.dll in the root of your publish directory.  You will have no refs folder as well.

How does it look in .csproj?

In project.json, it was pretty verbose.  So what does it look like in .csproj?  It’s much cleaner.

  1. Add
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="1.1.0" />
    
  2. Add
    <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
    
  3. Add
    <PreserveCompilationContext>true</PreserveCompilationContext>
    

So the final output of a .csproj might look a little something like this.  Note lines 5,6, and 32.

This will still generate a <ProjectName>.PrecompiledViews.dll in the root of your publish directory and you will still have no refs folder.

This simplicity in order to get precompiled views is yet another win for the .csproj style ASP.NET Core projects over project.json.

Using the ASP.NET Core Script TagHelper to Polyfill The Latest JavaScript Features

tl;dr

The primary use case for fallbacks in the Script Tag Helper is checking if a CDN is down and loading a local script instead.  Another use case is you can use fallbacks to make it easy to polyfill missing JavaScript features in older browsers, while not having your users using the latest browsers take a network hit.

Background – What is a Polyfill?

A polyfill is a fallback, written in JavaScript, that fills in a new JavaScript feature that is missing in an older browser and replicates the same functionality that newer browsers provide natively.  This allows JavaScript code using the latest features only available in newer browsers to work on older browsers as well.

To polyfill the API, you simply just check if that API is available, and if not, load the polyfill that adds that API to your browser.  That way, if your user is using the latest browser, you don’t punish them by downloading something they don’t need and hurting their performance.

How a Polyfill works

This JavaScript code will polyfill the “new” fetch API for making AJAX calls, that is currently available in all of the latest major browsers (Chrome, Edge, Firefox, Safari, etc.), but not in any of the Internet Explorer family tree (not even IE11).

First, the code looks to see if the Promise API is available, because fetch relies on Promises in order to work.  If the API is not available, it loads a script from a CDN that polyfills the Promise API.  It does the same thing for the fetch API.

The built-in Script Tag Helper – CDN fallback

The built-in Script Tag Helper in ASP.NET Core has fallback support baked in that generates code very close to this.  The original intent of their fallback support was for attempting to load a script from a CDN.  After loading from that CDN, test to see if a JavaScript API is available, and if it’s not (due to the CDN being down, blocked, or whatever) fall back to loading a local JavaScript file.  That code looks like this:

The fallback works for polyfills too!

What I haven’t seen anyone mention is to use this to conditionally polyfill if a JavaScript feature doesn’t exist.  And it works right out of the box, simply omit the src attribute, and the rest will be taken care of:

The code above will generate nearly identical code to what’s in the first code sample.  As mentioned above, fetch needs Promises to work, so I’m also polyfilling that conditionally as well.  In the real world, I would likely bundle these two together (locally or using something like Polyfill.io) and make one network request, but using unpkg (an NPM CDN) was just for simplicity’s sake.

With the code above, I get the best of both worlds.  My code using fetch runs on older browsers, but my users who use the latest browsers don’t take a network hit for something they don’t need.  This same methodology will work for other ES2015 and above features and is nice for small apps that want to use some of the latest features and not have to deal with the build config of Babel or TypeScript transpiling down to ES5.

I’m not sure if this was an intended feature of the Script Tag Helper, but it’s a good use case for the fallback feature that I hadn’t seen anyone talk about, so I wanted to call attention to it.

And if you are lucky enough to only have to support Chrome at work.  I assume this is you right now reading the hoops us plebes have to go through to get our code to run on all browsers:

Custom Tag Helper: Toggling Visibility On Existing HTML elements

A lot of times we need a way to toggle the visibility of an element based on some server-side values.  The most common example is if someone is logged in, show them more options than someone who is not logged in.

Option without Tag Helpers

This solution always felt a little dirty to me

What I’d like to do is to add an attribute off of div to toggle visibility.  So instead I’d like something that looks like this:

So let’s create a custom Tag Helper to do this

  • Create a TagHelpers folder in the root of your MVC project
  • Create a new class called VisibilityTagHelper
  • Copy and paste the super simple code below

  • Go to /Views/_ViewImports.cshtml and add a line for all the TagHelpers in your project by doing @addTagHelper *,  .  Example below:

  • Build your project *this is important or you won’t get intellisense*
  • Start using it such as the example shown below (same as above)

  • That’s it!

 

Or view the GitHub Commit that makes this happen

I also have a repo, where the first two commits are just the initial commit with a File => New Project.  This 3rd commit outlines all the changes needed to make to get this to work if you’d rather learn via a Commit.

 

Breaking Down The Code

The code for the Visibility Tag Helper is pretty straight forward, but here’s a breakdown of it:

  • HtmlTargetElement
    • You first decorate your class with the HtmlTargetElement attribute to tell it that you’re targeting div’s.
      [HtmlTargetElement("div")]
    • You can target more than just divs by adding more attributes.
      [HtmlTargetElement("div")]
      [HtmlTargetElement("a")]
    • You could add the is-visible attribute to ALL elements by using *.  This could be quite handy
      [HtmlTargetElement("*")]
  • Inherit from TagHelper.
    • Naming the TagHelper doesn’t matter, as we are choosing the HtmlTargetElement.  If we were making our own tag, then the tag would be based off of the name.
      public class VisibilityTagHelper : TagHelper
      
  • Add a property to store the visibility of the tag.
    • We need to default it to true, otherwise none of the HTML tags specified in the HtmlTargetElement will be visible, because booleans default to false.  THAT IS REALLY IMPORTANT.  The name will be kebab-cased which means each capital letter will be split by a dash.  Example IsVisible becomes is-visible when using the tag.
      public bool IsVisible { get; set; } = true;
      
  •   Override the Process method
    • If the tag is set to not IsVisible, then we need to supress the output which will not render the tag at all.  (Thanks to Damian Edwards for the tip about SuppressOutput())
      public override void Process(TagHelperContext context, TagHelperOutput output)
      {
          if (!IsVisible)
              output.SuppressOutput();
          base.Process(context, output);
       }
      
  • That’s about it!  Super simple.

 

Update 1/3/2017: As mentioned above – thanks to Damian Edwards for informing me of the output.SuppressOutput() method.  That made things simpler.

How to fix a failure in upgrading from ASP.NET Core 1.0.x to ASP.NET Core 1.1

Error I received

  1. The project has not been restored or restore failed – run dotnet restore

  2. The project does not list one of ‘win10-x64, win81-x64, win8-x64, win7-x64’ in the ‘runtimes’ section.

  3. You may be trying to publish a library, which is not supported. Use dotnet pack to distribute libraries.

Can not find runtime target for framework ‘.NETCoreApp,Version=v1.1’ compatible with one of the target runtimes: ‘win10-x64, win81-x64, win8-x64, win7-x64’.

How to fix it

Open your project.json and under “dependencies” change

"Microsoft.NETCore.App": "1.1.0",

to

"Microsoft.NETCore.App": { "version": "1.1.0", "type":  "platform" },

Then re-run your app.

 

How it happened

I’m still using Visual Studio 2015, but I have .NET Core 1.1 downloaded and installed and I wanted to upgrade to .NET Core 1.1 and update to all the latest ASP.NET Core 1.1 packages.

I went to File => New Project => ASP.NET Core Web Application, which proceeded to give me .NET Core 1.0.1, which was expected.  I followed the upgrade path, but I forgot to hit “Save” on my project.json in step 2, when I changed the values to .NET Core 1.1.0.  Therefore, it did not restore the .NET Core 1.1 NuGet package.

So when I went to the NuGet UI under Updates, .NET Core was an option to update, which I didn’t realize when I hit “Update All.”  Well that jacked with my project.json in this way…

Before:

core101

After:

netcoreapp11

NuGet removed my JSON nested object with just the version number.  What the “type”:”platform” does is it tells my project that it expects .NET Core will already be installed on the machine, therefore it will not bundle .NET Core with my app.

When “type” : “platform” is NOT specified, then it assumes I am bundling .NET Core WITH my app.  In that case, I need to specify the exact platforms that I’m targeting (i.e. Windows 10, Linux, Mac, etc.) in the “runtimes” section.  However, that “runtimes” section was not there previously, because I was targeting the platform.

This seems like it would be a bug with NuGet.  It should only update the Version, and not overwrite anything else.  Maybe this gets fixed during the switch to csproj coming soon.  I’m not going to file a bug at this point on GitHub, because I doubt they make the investment to fix this in project.json.