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#.

"An unhandled exception occurred while processing the request. InvalidOperationException: the view 'ViewNameHere' was not found. The following locations were searched: ..."

View not found errors are easy to handle.

There are examples using .NET 6/.NET 8 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". When published, the application uses "Production" so after confirming that the application works as intended, revert this back to "Development" and continue as before.

{
  "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". When published, the application uses "Production" so after confirming that the application works as intended, revert this back to "Development" and continue as before.

{
  "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


PROWAREtech

Hello there! How can I help you today?
Ask any question

PROWAREtech

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.
ACCEPT REJECT