Introduction

Okay, so you’ve probably come across tons of broken links on our enormous world wide web, and it gets even more frustrating when there aren’t any links or buttons redirecting to a working page. However, the hard truth is, in reality, errors are inevitable. Whether they occur through wrong user input, or simply just one or two bugs in your code, they are simply just inevitable.

This article explains perhaps the most efficient way to set up custom error pages for your ASP.NET core web Application. Using custom error pages is immensely satisfying, as opposed to having blank pages, default browser-specific pages, or using the default error pages provided by ASP.NET core.


How it works

So, let’s say we run our application and go to a page which doesn’t exist. Once a request gets all the way through to the end of our MVC pipeline, and we have no middleware handling the request, we get the dreaded 404 error (or “Page not found”, if you will). Some browsers might be nice enough to display an error page, others will just leave a blank page, leaving your user slightly nonplussed – Not exactly the kind of thing that makes great user experiences great, is it?.


Setting up our custom error page


Creating the Controller class

To Begin setting up our error page, we need to create a controller to respond to the request. To keep this example as simple as possible, I'm going to create a simple MVC controller class – as a matter of fact, I will be using the default template provided by visual studio (2015), and then adding a URI template for the route.   


x
 
1
public class StatusCodeController : Controller
2
{
3
        // GET: /<controller>/
4
        [HttpGet("/StatusCode/{statusCode}")]
5
        public IActionResult Index(int statusCode)
6
        {
7
            return View(statusCode);
8
        }
9
}
10

for a more detailed example, we can go all the way to implement logging, using the ILogger  and IStatusCodeReExecuteFeature interfaces, logging the url that the request came through.

16
 
1
public class StatusCodeController : Controller
2
{
3
  private readonly ILogger<HomeController> _logger;
4
  public StatusCodeController(ILogger<HomeController> logger)
5
  {
6
    _logger = logger;
7
  }
8
  // GET: /<controller>/
9
  [HttpGet("/StatusCode/{statusCode}")]
10
  public IActionResult Index(int statusCode)
11
  {
12
    var reExecute = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
13
    _logger.LogInformation($"Unexpected Status Code: {statusCode}, OriginalPath: {reExecute.OriginalPath}");
14
    return View(statusCode);
15
  }
16
}


Creating the view

Next we create the view for the index action of our StatusCode Controller class. 


For a simple view, we could have.

5
 
1
@model int
2
<div class="jumbotron">
3
    <h1>Error @Model</h1>
4
    <h3>That’s odd... Something we didn't expect happened"</h3>
5
</div>

For a more dynamic view, we could go a bit further.

30
 
1
@model int
2
@{
3
    var statusCode = Model;
4
    var statusmessage = "";
5
    switch (statusCode)
6
    {
7
        case 400:
8
            statusmessage = "Bad request: The request cannot be fulfilled due to bad syntax";
9
            break;
10
        case 403:
11
            statusmessage = "Forbidden";
12
            break;
13
        case 404:
14
            statusmessage = "Page not found";
15
            break;
16
        case 408:
17
            statusmessage = "The server timed out waiting for the request";
18
            break;
19
        case 500:
20
            statusmessage = "Internal Server Error - server was unable to finish processing the request";
21
            break;
22
        default:
23
            statusmessage = "That’s odd... Something we didn't expect happened";
24
            break;
25
    }
26
}
27
<div class="jumbotron">
28
    <h1>Error @Model</h1>
29
    <h3>@statusmessage</h3>
30
</div>

you get the idea. Feel free to include more Http status code cases.

   

Setting up the middleware

Once we are done with the controller and the view, we finish up by setting up a middleware to handle the request in the pipeline, in the configure method of our startup class. Like so.

x
 
1
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
2
{
3
  loggerFactory.AddConsole(Configuration.GetSection("Logging"));
4
  loggerFactory.AddDebug();
5
  if (env.IsDevelopment())
6
  {
7
    app.UseDeveloperExceptionPage();
8
    app.UseBrowserLink();
9
  }
10
  app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
11
  app.UseStaticFiles();
12
  app.UseMvc(routes =>
13
             {
14
    routes.MapRoute(
15
      name: "default",
16
      template: "{controller=Home}/{action=Index}/{id?}");
17
  });
18
}

And you're done!


Results

You can test your error pages by generating errors yourself. Easily begin by navigating to a non-existent page, to try out a 404 error like so. 

The entire code sample used can be found on my github here . Good luck !