PROWAREtech

articles » current » asp-net-core » handle-view-was-not-found-server-error

ASP.NET Core: Handle "View Was Not Found" Server Error

How to handle view not found server errors and return a status code 404 - not found; written in C#.

View not found errors are easy to handle.

There are examples using .NET 6 and .NET Core 3.1. If using .NET 5 then follow the .NET Core 3.1 code.

First, create a new ASP.NET Core Web Application Project or open an existing one.

Modify the C# file named Program.cs. Add app.UseStatusCodePagesWithReExecute("/Home/Error/{0}") as in the following code snippet. This single line of code is key to making this error handling function properly.

if (app.Environment.IsDevelopment())
    app.UseDeveloperExceptionPage();
else
{

    app.UseStatusCodePagesWithReExecute("/Home/Error/{0}"); // NOTE: ADD THIS LINE OF CODE TO THE PROGRAM.CS FILE


    app.UseExceptionHandler("/Home/Error");
}

Modify ErrorViewModel.cs as follows. Most of this code file is modified.

using System;

namespace ProjectName.Models
{
	public class ErrorViewModel
	{
		public string RequestId { get; set; }
		public string Path { get; }
		public int StatusCode { get; }
		public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
		public ErrorViewModel(string path = null)
		{
			StatusCode = 500;
			Path = path;
		}
		public ErrorViewModel(int statusCode, string path = null)
		{
			StatusCode = statusCode;
			Path = path;
		}
	}
}

Modify the HomeController (located in the HomeController.cs file). The modifications are all noted.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ProjectName.Models;

namespace ProjectName.Controllers
{
	public class HomeController : Controller
	{
		private readonly ILogger<HomeController> _logger;

		public HomeController(ILogger<HomeController> logger)
		{
			_logger = logger;
		}

		public IActionResult Index(string id) // NOTE: THIS LINE MODIFIED FOR TESTING
		{
			return View(viewName: id); // NOTE: THIS LINE MODIFIED FOR TESTING
		}

		public IActionResult Privacy()
		{
			return View();
		}



		// NOTE: THE FOLLOWING 15 LINES OF CODE ARE ADDED/MODIFIED
		[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: MUST NOT ALLOW CACHING
		public IActionResult Error()
		{
			var ehpf = HttpContext.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerPathFeature>();
			if (ehpf.Error.Source == "Microsoft.AspNetCore.Mvc.ViewFeatures" && (ehpf.Error.HResult == -2146233079 || ehpf.Error.Message.Contains("was not found")))
				return View(new ErrorViewModel(404, ehpf.Path));
			return View(new ErrorViewModel(ehpf.Path) { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
		}

		[Route("Home/Error/{id}")]
		[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: MUST NOT ALLOW CACHING
		public IActionResult Error(int id)
		{
			return View(new ErrorViewModel(id));
		}


	}
}

Modify Error.cshtml as follows.

@model ErrorViewModel
@{
    Context.Response.StatusCode = Model.StatusCode;
    switch (Context.Response.StatusCode)
    {
        case 404:
        	ViewData["Title"] = "404: Resource Not Found";
            break;
        default: // NOTE: CAN HANDLE OTHER STATUS CODES
        	ViewData["Title"] = Context.Response.StatusCode + ": Error";
            break;
    }
}
<h2>@ViewData["Title"]</h2>

@if (Model.ShowRequestId)
{
    <p><strong>Request ID:</strong> <code>@Model.RequestId</code></p>
}
<pre>@Model.Path</pre>

Modify launchSettings.json to use Production settings. Only two lines of code need to be modified here, from "Development" to "Production".

{
  "iisSettings": {
    "windowsAuthentication": false, 
    "anonymousAuthentication": true, 
    "iisExpress": {
      "applicationUrl": "http://localhost:56789",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    },
    "ProjectName": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    }
  }
}

Modify Index.cshtml as follows.

@{
	ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4"><a href="/Home/Index/BogusView">Open "BogusView"</a></h1>
</div>

Now, run the project and try to load a view that does not exist, such as "BogusView" which a link is provided for on the home page. A 404 resource not found error should be shown instead of a 500 internal server error.

Modify the C# file named Startup.cs. Add app.UseStatusCodePagesWithReExecute("/Home/Error/{0}") as in the following code. This single line of code is key to making this error handling function properly.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ProjectName
{
	public class Startup
	{
		public Startup(IConfiguration configuration)
		{
			Configuration = configuration;
		}

		public IConfiguration Configuration { get; }

		public void ConfigureServices(IServiceCollection services)
		{
			services.AddControllersWithViews();
		}

		public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
		{
			if (env.IsDevelopment())
			{
				app.UseDeveloperExceptionPage();
			}
			else
			{
				app.UseStatusCodePagesWithReExecute("/Home/Error/{0}"); // NOTE: THIS LINE IS NEW

				app.UseExceptionHandler("/Home/Error");
			}
			app.UseStaticFiles();

			app.UseRouting();

			app.UseAuthorization();

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

Modify ErrorViewModel.cs as follows. Most of this code file is modified.

using System;

namespace ProjectName.Models
{
	public class ErrorViewModel
	{
		public string RequestId { get; set; }
		public string Path { get; }
		public int StatusCode { get; }
		public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
		public ErrorViewModel(string path = null)
		{
			StatusCode = 500;
			Path = path;
		}
		public ErrorViewModel(int statusCode, string path = null)
		{
			StatusCode = statusCode;
			Path = path;
		}
	}
}

Modify the HomeController (located in the HomeController.cs file). The modifications are all noted.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ProjectName.Models;

namespace ProjectName.Controllers
{
	public class HomeController : Controller
	{
		private readonly ILogger<HomeController> _logger;

		public HomeController(ILogger<HomeController> logger)
		{
			_logger = logger;
		}

		public IActionResult Index(string id) // NOTE: THIS LINE MODIFIED FOR TESTING
		{
			return View(viewName: id); // NOTE: THIS LINE MODIFIED FOR TESTING
		}

		public IActionResult Privacy()
		{
			return View();
		}



		// NOTE: THE FOLLOWING 15 LINES OF CODE ARE ADDED/MODIFIED
		[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: MUST NOT ALLOW CACHING
		public IActionResult Error()
		{
			var ehpf = HttpContext.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerPathFeature>();
			if (ehpf.Error.Source == "Microsoft.AspNetCore.Mvc.ViewFeatures" && (ehpf.Error.HResult == -2146233079 || ehpf.Error.Message.Contains("was not found")))
				return View(new ErrorViewModel(404, ehpf.Path));
			return View(new ErrorViewModel(ehpf.Path) { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
		}

		[Route("Home/Error/{id}")]
		[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: MUST NOT ALLOW CACHING
		public IActionResult Error(int id)
		{
			return View(new ErrorViewModel(id));
		}


	}
}

Modify Error.cshtml as follows.

@model ErrorViewModel
@{
    Context.Response.StatusCode = Model.StatusCode;
    switch (Context.Response.StatusCode)
    {
        case 404:
        	ViewData["Title"] = "404: Resource Not Found";
            break;
        default: // NOTE: CAN HANDLE OTHER STATUS CODES
        	ViewData["Title"] = Context.Response.StatusCode + ": Error";
            break;
    }
}
<h2>@ViewData["Title"]</h2>

@if (Model.ShowRequestId)
{
    <p><strong>Request ID:</strong> <code>@Model.RequestId</code></p>
}
<pre>@Model.Path</pre>

Modify launchSettings.json to use Production settings. Only two lines of code need to be modified here, from "Development" to "Production".

{
  "iisSettings": {
    "windowsAuthentication": false, 
    "anonymousAuthentication": true, 
    "iisExpress": {
      "applicationUrl": "http://localhost:56789",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    },
    "ProjectName": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Production"
      }
    }
  }
}

Modify Index.cshtml as follows.

@{
	ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4"><a href="/Home/Index/BogusView">Open "BogusView"</a></h1>
</div>

Now, run the project and try to load a view that does not exist, such as "BogusView" which a link is provided for on the home page. A 404 resource not found error should be shown instead of a 500 internal server error.

Coding Video

https://youtu.be/Ip_1L7Ow-XY


This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE