Error Pages Based on Status Code in ASP.NET Core MVC

Error Pages Based on Status Code in ASP.NET Core MVC

In this article, I will discuss Error Pages Based on Status Codes in ASP.NET Core MVC Applications with Examples. Please read our previous article discussing Handling Non-Success HTTP Status Codes in ASP.NET Core MVC Application.

HTTP Status Codes:

HTTP status codes indicate the outcome of an HTTP request. While successful requests return status codes in the 2xx range, non-successful codes (4xx and 5xx ranges) indicate errors or exceptional conditions. Handling these non-successful codes effectively allows applications to provide more meaningful feedback to users rather than displaying generic browser errors. In ASP.NET Core MVC, we can intercept these codes and render custom error pages to provide a better user experience.

Common Non-Success HTTP Status Codes:

The following are some of the common non-success HTTP status codes and when they are typically used in a web application:

  • 400 Bad Request: Indicates invalid syntax or client-side input errors that prevent the server from processing the request.
  • 401 Unauthorized: This Indicates the request is missing authentication credentials or the provided credentials are invalid.
  • 403 Forbidden: Indicates that the server understands the request but denies access due to insufficient permissions. The client does not have permission to access the resource.
  • 404 Not Found: Indicates that the requested resource is unavailable or does not exist on the server.
  • 500 Internal Server Error: A generic error response indicating something went wrong on the server side.
  • 503 Service Unavailable: This status code is used when the server is temporarily unavailable to handle the request due to being overloaded or undergoing maintenance.
Error Pages Based on Status Code in ASP.NET Core MVC

Custom error pages can be displayed to the user instead of a generic browser error page when an error occurs. This improves usability and user experience. We need to follow the below steps to render Error Pages Based on Status Code in ASP.NET Core MVC Applications:

  • Create Custom Error Views: Develop dedicated views (e.g., for 401, 404, 500 errors, etc.) that display clear messages to users.
  • Configure Middleware: Set up error handling middleware (either using ReExecute or Redirects) to catch non-success status codes and direct requests to the error controller.
  • Develop an Error Controller: Create a controller to handle error status codes by rendering the corresponding view.
  • Integrate Error Handling in Other Controllers: Modify other Controller actions to return appropriate status codes, triggering our custom error pages via Error Controller.

Let us proceed and implement this step-by-step in our ASP.NET Core MVC Application. First, create a new ASP.NET Core MVC Project with the name NonSucessHTTPStatusCodeDemo.

Create Custom Error Views:

First, create the error views for the non-success HTTP Status codes you want to handle in your application. These views will be displayed to the user when the action method returns the corresponding HTTP status code.

For example, you can create views like PageNotFoundError.cshtml for handling “Not Found” errors (HTTP status code 404) and InternalServerError.cshtml for handling server errors (HTTP status code 500). You need to place these views inside the Views/Shared directory. For example:

  • Views/Shared/PageNotFoundError.cshtml
  • Views/Shared/InternalServerError.cshtml
  • Views/Shared/UnauthorizedError.cshtml
  • Views/Shared/ForbiddenError.cshtml
  • Views/Shared/GenericError.cshtml
  • Views/Shared/ServiceUnavailableError.cshtml
PageNotFoundError.cshtml

Create a view named PageNotFoundError.cshtml within the Views/Shared folder, then copy and paste the following code.

@{
    Layout = "_Layout";
    ViewBag.Title = "404 - Page Not Found";
}
<div class="container mt-5">
    <div class="card border-danger shadow">
        <div class="card-header bg-danger text-white">
            <h1 class="card-title mb-0">404 - Page Not Found</h1>
        </div>
        <div class="card-body">
            <p class="card-text lead">Oops! The page you are looking for doesn't exist.</p>
            <hr>
            <p>Please check the URL or return to the homepage for further assistance.</p>
            <a href="/" class="btn btn-primary">Back to Home</a>
        </div>
    </div>
</div>

This will render the following page:

Error Pages Based on Status Codes in ASP.NET Core MVC Applications with Examples

UnauthorizedError.cshtml

Create a view named UnauthorizedError.cshtml within the Views/Shared folder, then copy and paste the following code.

@{
    Layout = "_Layout";
    ViewBag.Title = "401 - Unauthorized";
}
<div class="container mt-5">
    <div class="card border-warning shadow">
        <div class="card-header bg-warning text-white">
            <h1 class="card-title mb-0">401 - Unauthorized</h1>
        </div>
        <div class="card-body">
            <p class="card-text lead">Access Denied. You do not have the required credentials to view this page.</p>
            <hr>
            <p>If you believe this is an error, please contact your administrator.</p>
            <a href="/" class="btn btn-primary">Back to Home</a>
        </div>
    </div>
</div>

This will render the following page:

Error Pages Based on Status Codes in ASP.NET Core MVC Applications

InternalServerError.cshtml

Create a view named InternalServerError.cshtml within the Views/Shared folder, then copy and paste the following code.

@{
    Layout = "_Layout";
    ViewBag.Title = "500 - Internal Server Error";
}
<div class="container mt-5">
    <div class="card border-danger shadow">
        <div class="card-header bg-danger text-white">
            <h1 class="card-title mb-0">500 - Internal Server Error</h1>
        </div>
        <div class="card-body">
            <p class="card-text lead">An unexpected error occurred on our server.</p>
            <hr>
            <p>Our technical team has been notified. Please try again later.</p>
            <a href="/" class="btn btn-primary">Back to Home</a>
        </div>
    </div>
</div>

This will render the following page:

Error Pages Based on Status Codes in ASP.NET Core MVC

GenericError.cshtml

Create a view named GenericError.cshtml within the Views/Shared folder, then copy and paste the following code.

@{
    Layout = "_Layout";
    ViewBag.Title = "Error";
}
<div class="container mt-5">
    <div class="card border-dark shadow">
        <div class="card-header bg-dark text-white">
            <h1 class="card-title mb-0">Oops! Something Went Wrong.</h1>
        </div>
        <div class="card-body">
            <p class="card-text lead">An unexpected error has occurred.</p>
            <hr>
            <p>Please try again later or contact support if the problem persists.</p>
            <a href="/" class="btn btn-primary">Back to Home</a>
        </div>
    </div>
</div>

This will render the following page:

Error Pages Based on Status Codes in ASP.NET Core

ForbiddenError.cshtml

Create a view named ForbiddenError.cshtml within the Views/Shared folder, then copy and paste the following code.

@{
    Layout = "_Layout";
    ViewBag.Title = "403 - Forbidden";
}
<div class="container mt-5">
    <div class="card border-secondary shadow">
        <div class="card-header bg-secondary text-white">
            <h1 class="card-title mb-0">403 - Forbidden</h1>
        </div>
        <div class="card-body">
            <p class="card-text lead">You do not have permission to access this resource.</p>
            <hr>
            <p>If you believe this is a mistake, please contact your system administrator.</p>
            <a href="/" class="btn btn-primary">Back to Home</a>
        </div>
    </div>
</div>

This will render the following page:

Error Pages Based on Status Codes

ServiceUnavailableError.cshtml

Create a view named ServiceUnavailableError.cshtml within the Views/Shared folder, then copy and paste the following code.

@{
    Layout = "_Layout";
    ViewBag.Title = "503 - Service Unavailable";
}
<div class="container mt-5">
    <div class="card border-info shadow">
        <div class="card-header bg-info text-white">
            <h1 class="card-title mb-0">503 - Service Unavailable</h1>
        </div>
        <div class="card-body">
            <p class="card-text lead">The service is currently unavailable.</p>
            <hr>
            <p>We might be undergoing maintenance or experiencing high traffic. Please try again later.</p>
            <a href="/" class="btn btn-primary">Back to Home</a>
        </div>
    </div>
</div>

This will render the following page:

Error Pages Based on Status Codes in .NET Core MVC

Modified Layout Page

Please modify the Layout view as follows. Here, we have added menu links that will let you easily navigate to each error simulation page (such as Bad Request, Unauthorized, Forbidden, Not Found, Internal Server Error, and Service Unavailable.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - NonSucessHTTPStatusCodeDemo</title>
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">NonSucess404HTTPStatusCodeDemo</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse"
                        aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>

                        <!-- Menu items for error simulation actions -->
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="BadRequestPage">Bad Request (400)</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="UnauthorizedPage">Unauthorized (401)</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="ForbiddenPage">Forbidden (403)</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="NotFoundPage">Not Found (404)</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="InternalServerErrorPage">Internal Server Error (500)</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="ServiceUnavailablePage">Service Unavailable (503)</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2025 - NonSucessHTTPStatusCodeDemo - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <!-- jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Creating Error Controller:

Create a new Empty MVC controller named ErrorController within the Controllers folder and then copy and paste the following code. This controller handles different HTTP status codes by returning the corresponding error view. The Index action accepts the status code as a parameter, clears any pre-existing response data, resets the status code, and then returns the corresponding view based on the error.

using Microsoft.AspNetCore.Mvc;
namespace NonSucessHTTPStatusCodeDemo.Controllers
{
    public class ErrorController : Controller
    {
        // Route to capture any error status code from the URL
        [Route("Error/{statusCode}")]
        public IActionResult Index(int statusCode)
        {
            // Clear any pre-existing response content
            Response.Clear();

            // Explicitly set the response status code
            Response.StatusCode = statusCode;

            // Render the appropriate error view based on the status code
            switch (statusCode)
            {
                case 401:
                    return View("UnauthorizedError");        // For Unauthorized Access
                case 403:
                    return View("ForbiddenError");           // For Forbidden Access 
                case 404:
                    return View("PageNotFoundError");        // For Not Found errors
                case 500:
                    return View("InternalServerError");      // For Internal Server errors
                case 503:
                    return View("ServiceUnavailableError");  // For Service Unavailable 
                default:
                    return View("GenericError");             // For any other error codes
            }
        }
    }
}
Configure the Middleware:

We need to configure the Error Handling Middleware to capture specific status codes and render the corresponding views. In the Program.cs file, we can configure the middleware to use custom error pages based on status codes. To use the UseStatusCodePagesWithReExecute middleware, add the following code to the Configure method:

app.UseStatusCodePagesWithReExecute(“/Error/{0}”);

This configuration tells ASP.NET Core to re-execute the request with the Error Route and pass the status code as a parameter to the action method. To use the UseStatusCodePagesWithRedirects middleware, you can use the following code:

app.UseStatusCodePagesWithRedirects(“/Error/{0}”);

This configuration will redirect the user to the Error Route with the corresponding status code in the URL. Please modify the Program class as follows.

namespace NonSucessHTTPStatusCodeDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            var app = builder.Build();

            if (app.Environment.IsDevelopment())
            {
                // Use the developer exception page for detailed error information during development.
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // Re-executes the request with the error code parameter for custom error handling
                app.UseStatusCodePagesWithReExecute("/Error/{0}");

                // app.UseStatusCodePagesWithRedirects("/Error/{0}");
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");

            app.Run();
        }
    }
}
Note:
  • UseStatusCodePagesWithReExecute: This option does not change the URL in the browser. It internally re-executes the request.
  • UseStatusCodePagesWithRedirects: This option performs an external redirect, changing the URL in the browser.
Modify the Home Controller:

In HomeController, we need to implement various actions that deliberately return different HTTP status codes. So, please modify the HomeController as follows. Each action method demonstrates how to return specific HTTP status codes. These examples help you test the custom error pages by simulating different error conditions.

using Microsoft.AspNetCore.Mvc;

namespace NonSucessHTTPStatusCodeDemo.Controllers
{
    public class HomeController : Controller
    {
        // Default home page action
        public IActionResult Index()
        {
            return View();
        }

        // Simulate a 400 Bad Request error page.
        public IActionResult BadRequestPage()
        {
            // In a real scenario, this might be triggered when the request is malformed.
            bool isBadRequest = true;
            if (isBadRequest)
            {
                // Return HTTP 400 status code to trigger the Bad Request error handling.
                // This will render the Generic error page
                return new StatusCodeResult(400);
            }
            return View();
        }

        // Simulate a 401 Unauthorized error page.
        public IActionResult UnauthorizedPage()
        {
            // Here, we simulate an unauthenticated user scenario.
            bool isAuthenticated = false;
            if (!isAuthenticated)
            {
                // Returns 401 Unauthorized to trigger the custom error view.
                return Unauthorized();
            }
            return View();
        }

        // Simulate a 403 Forbidden error page.
        public IActionResult ForbiddenPage()
        {
            // Simulate a scenario where the user lacks permission.
            bool hasPermission = false;
            if (!hasPermission)
            {
                // Returns HTTP 403 to trigger the Forbidden error view.
                return new StatusCodeResult(403);
            }
            return View();
        }

        // Simulate a 404 Not Found error page.
        public IActionResult NotFoundPage()
        {
            // Simulate a scenario where the requested resource does not exist.
            bool resourceExists = false;
            if (!resourceExists)
            {
                // Returns 404 Not Found to trigger the custom error page.
                return NotFound();
            }
            return View();
        }

        // Simulate a 500 Internal Server Error page.
        public IActionResult InternalServerErrorPage()
        {
            try
            {
                // Simulate code that might throw an exception.
                throw new Exception("Simulated exception for testing 500 error.");
            }
            catch (Exception ex)
            {
                // Log the exception as needed.
                // Returns HTTP 500 to trigger the Internal Server Error view.
                return new StatusCodeResult(500);
            }
        }

        // Simulate a 503 Service Unavailable error page.
        public IActionResult ServiceUnavailablePage()
        {
            // Simulate a scenario where the service is down (maintenance or high load).
            bool serviceDown = true;
            if (serviceDown)
            {
                // Returns 503 Service Unavailable to trigger the custom error view.
                return new StatusCodeResult(503);
            }
            return View();
        }
    }
}

Now, run the application and access the Home Controller Action Methods. Based on the status code, you should see the respective error pages.

Following the above steps, we implemented a robust error-handling mechanism in our ASP.NET Core MVC application. This approach enhances the user experience by displaying friendly error pages and simplifies error tracking and debugging during development and production.

In the next article, I will discuss the Result Filter in ASP.NET Core MVC Application. In this article, I try to explain Error Pages Based on Status Codes in ASP.NET Core MVC Applications with Examples. I hope you enjoy this Error Pages Based on Status Codes in the ASP.NET Core MVC article.

Leave a Reply

Your email address will not be published. Required fields are marked *