TOC

The community is working on translating this tutorial into Hungarian, but it seems that no one has started the translation process for this article yet. If you can help us, then please click "More info".

Core Concepts:

Handling Errors (404 etc.)

Handling exceptions is very important in every type of application, but as we discussed in the previous article, you also need to be aware of another type of errors when developing an application for the web. The server (e.g. IIS on Windows or Apache on Linux/Unix) and the client (usually a webbrowser, e.g. Chrome) communicates using a protocol called HTTP, which requires the server to always include a status code when responding to a request. If everything goes as expected, the response code will normally be 2xx, e.g. 200, which simply means "Ok" (in this case short for "Everything went Okay - here's the result").

On the other hand, if things doesn't go as expected, another type of status code will be returned. One of the most well known is the 404 response, which is returned when the server couldn't find the resource requested by the user. This is also the best example of a status code which should be handled by you. Why? Because if you don't do anything, you will rely on the default error page of either the webserver (if one exists) or even the browser. This doesn't look very nice, since it becomes very obvious that this page is not really a part of your website, but perhaps even worse, it prevents your application from noticing the fact that a page is missing.

Handling errors (e.g. 404) gracefully

What we need to do instead is catch these types of errors, log them and then display a user-friendly message to the user. Since we get full control of this message, it can include anything from a short apology and a funny picture to a mail form where the user can tell you more about how they ended up on this page, which can help you getting it fixed.

The UseStatusCodePages() method

By default, when a non-successful status code should be returned, ASP.NET Core doesn't do anything other than return the actual status code along with an empty response body - in other words, the client (browser) will need to take it from there and let the user know that something went wrong. This is very easy to change, though - you can simply include a call to app.UseStatusCodePages() in the Configure() method of Startup.cs, like this:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStatusCodePages();
    ....

Whenever you try to access a non-existing page now, you will get a response like this one:

Status Code: 404; Not Found

That's not really more user-friendly than letting the browser handle it, but it does prove that these errors are now handled by our application instead of the browser. The slightly more user-friendly version could look like this:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStatusCodePages("text/html", "We're <b>really</b> sorry, but something went wrong. Error code: {0}");
    ....

This overload of the UseStatusCodePages() makes it easy for you to customize the error message, but it still doesn't look like the rest of the application and we're still not able to log the problem. But don't worry - there are several related methods which you can use. To solve the problem of being able to log the error, we can use another overload of the UseStatusCodePages() which gives us even more control:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStatusCodePages(async context =>
    {
context.HttpContext.Response.ContentType = "text/html";

if(context.HttpContext.Response.StatusCode == 404)
{
    // Log this error here, e.g. to a database. You can use the context.HttpContext.Request
    // object to access important information like the requested URL
}

await context.HttpContext.Response.WriteAsync(
    "We're <b>really</b> sorry, but something went wrong. Error code: " +
    context.HttpContext.Response.StatusCode);
    });
    ....

With this overload, we finally get all the control we need: We can log the error and we can then generate a nice response to the user, with all the HTML we want. However, generating an entire HTML error page with C# is not ideal, so if you want a more sophisticated error page, I suggest that we use another approach.

The UseStatusCodePagesWithRedirects() method

We'll replace the UseStatusCodePages() method with a call to UseStatusCodePagesWithRedirects(). As the name implies, it will respond with a redirect instead of an actual message. We can then redirect to any action in our application, which also means that we can respond with a View - even a View which uses a Layout, which will finally allow us to achieve an error page which looks like the rest of the website.

In the previous article, we talked about exceptions and how it could make sense to implement an ErrorController to handle unexpected exceptions. This also makes sense when dealing with HTTP status code errors like the 404. You could then implement an action on this controller to deal with these types of errors. But first, let's change the Configure() method of Startup.cs to use this approach:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStatusCodePagesWithRedirects("/Error/Http?statusCode={0}");
    ....

With this in place, all HTTP status errors will be redirected to a method called "Http" on the ErrorController and the actual status code will be included as a parameter. The Http() method could then look like this:

public class ErrorController : Controller  
{  

    public IActionResult Http(int statusCode)  
    {  
if(statusCode == 404)  
{  
    // Log this error here, e.g. to a database. You can use the HttpContext.Request  
    // object to access important information like the requested URL  
}  
// Return a View where the problem is explained. It can use the common Layout or not  
// and you can even make specific views for the types of errors you want to handle,  
// e.g. Error404.cshtml, as shown here...  
if(statusCode == 404)  
    return View("Error404");  
return View();  
    }  
    ....

We now have all that we set out to accomplish: A user-friendly error page which can blend in with the rest of the website (because it can use the existing Layout of the application) and the ability to log the error.

Summary

Handling HTTP errors in your application is a nice touch, because it allows you to display user-friendly error messages, but it's also important because it allows you to log the problems. In combination with exception handling, which was described in the previous article, this will help to make your web application much more robust.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!