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.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvcWithDefaultRoute(); }
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 :
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/home/error"); } app.UseMvcWithDefaultRoute(); }
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 :
app.UseStatusCodePagesWithRedirects("/error/{0}");
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 :
app.UseStatusCodePagesWithReExecute("/error/{0}");
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.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.Use(async (context, next) => { await next.Invoke(); //After going down the pipeline check if we 404'd. if (context.Response.StatusCode == StatusCodes.Status404NotFound) { await context.Response.WriteAsync("Woops! We 404'd"); } }); app.UseMvcWithDefaultRoute(); }
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.
Thanks it was very useful