Introduction:
In web development, errors are unavoidable in a .NET Core website. Managing errors is a standard practice in real-world web applications. In ASP.NET Web Forms, specific events and custom error pages were employed.
In ASP.NET MVC, the [HandleError] attribute, exception filters, and custom error pages were utilized. In ASP.NET Core, various methods and exception filters are available. This article explores five methods associated with error handling in ASP.NET Core.
Utilized within the Configure() method of the startup class, these methods serve as extension methods for the IApplicationBuilder object received in Configure(). Essentially, these extensions invoke the Microsoft.AspNetCore.Diagnostics middleware to execute their tasks.
To observe these methods, generate a new ASP.NET Core application using the Web Application project template. Subsequently, open the HomeController and intentionally trigger an exception within the Index() action, as illustrated below:
public IActionResult Index()
{
throw new Exception("This is some exception!!!");
return View();
}
The above code is straightforward and requires no further clarification.
Now, let's go through each of the five methods individually.
1) Use DeveloperExceptionPage() - Examine exception details during development.
The UseDeveloperExceptionPage() method is useful in the development stage. To implement this method, open the Startup.cs file, find the Configure() method, and modify it as follows:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseDeveloperExceptionPage();
....
....
}
As observed, the UseDeveloperExceptionPage() method is applied to the app object. Running the application in the browser will display a detailed error report, presenting information in the Stack, Query, Cookies, and Headers sections.
As previously noted, this method should only be invoked during development to avoid displaying exception details to end-users. Therefore, it is advisable to use the UseDeveloperExceptionPage() method as follows:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//something else here
}
Now, the UseDeveloperExceptionPage() is encapsulated within an if block. If the hosting environment is in development, the IsDevelopment() method returns true, and only then is UseDeveloperExceptionPage() executed. Otherwise, an alternative error handling code is executed.
2) Use ExceptionHandler() - Delegate control to a custom error handler.
The UseExceptionHandler() method addresses the exception using a designated error handler. This error handler may log the exceptions and display a user-friendly message to the end user. To apply this method, open the HomeController and identify the Error() action. This action is included in the Web Application project template and is associated with the corresponding view - Error.cshtml - located inside the Views folder.
Update the Error() action as depicted below:
public IActionResult Error()
{
var exception = HttpContext.Features
.Get<IExceptionHandlerFeature>();
ViewData["statusCode"] = HttpContext.
Response.StatusCode;
ViewData["message"] = exception.Error.Message;
ViewData["stackTrace"] = exception.
Error.StackTrace;
return View();
}
The Error() action retrieves exception details and transfers them to the Error view via ViewData. To capture the thrown exception, the code employs the Features.Get() method on HttpContext. The HTTP status code is acquired using the Response.StatusCode property. The status code, exception message, and exception stack trace are then stored in the ViewData dictionary for utilization in the Error view.
Now, open the Error view and change it as illustrated below:
<h1>@ViewData["message"]</h1>
<h2>Status Code : @ViewData["statusCode"]</h2>
<div>@ViewData["stackTrace"]</div>
The code presented above is straightforward and does not need additional explanation.
Finally, return to the Configure() method and change it to invoke UseExceptionHandler().
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
app.UseExceptionHandler("/Home/Error");
....
....
....
....
}
As observed, in this instance, we utilize the UseExceptionHandler() method and specify "/Home/Error" as a string parameter. Consequently, in the case of any unhandled exception, control will be redirected to the Error() action.
3) Use StatusCodePages() - Display error pages for HTTP status codes.
The two methods discussed earlier address unhandled exceptions originating from your code. However, these are not the sole sources of errors. Many errors result from internal server issues, non-existent pages, web server authorization problems, etc. HTTP status codes indicate these errors like 500, 404, and 401.
To handle such errors, you can utilize the UseStatusCodePages() method. This method can be applied as follows:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
....
....
app.UseStatusCodePages();
....
....
}
Simple. Execute the application and attempt to navigate to a page that doesn't exist. The result will be the error code.
While the default rendering allows us to view the HTTP status code, it appears in plain text. Enhancing it by formatting it as an HTML fragment would be beneficial. This can be achieved as illustrated below:
app.UseStatusCodePages("text/html",
"<h1>Status code page</h1> <h2>Status Code: {0}</h2>");
The UseStatusCodePages() method now utilizes HTML markup to display the status code. Note using {0} to insert the status code at a specific location within the markup.
4) Use StatusCodePagesWithRedirects() - Execute URL redirection
Sometimes, you might want to redirect control to a specific action in case of an HTTP error (like 404). This is where UseStatusCodePagesWithRedirects() proves useful. It sends an HTTP status code 302 (Found) to the browser and executes the redirection using the specified action. Consider the following code:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
....
....
app.UseStatusCodePagesWithRedirects("/CustomErrorPages/{0}");
....
....
}
The code invokes the UseStatusCodePagesWithRedirects() method and provides "/CustomErrorPages/{0}" as a string parameter. The {0} placeholder is employed if you want to pass the status code (like 404) to the handler. Then, add another action to the HomeController as depicted below:
[Route("/CustomErrorPages/{code}")]
public IActionResult StatusCodeErrors(string code)
{
ViewData["statusCode"] = code;
return View();
}
The [Route] attribute links /CustomErrorPages to StatusCodeErrors(). The StatusCodeErrors() action takes a code parameter representing the HTTP status code. Ensure that you include the StatusCodeErrors view and display the ViewData statusCode value.
Please note that the Error view generates this Response. The {0} placeholder passed 404 to the Error() action. You can selectively display a different view based on the status code if desired. You can also create a mechanism like this:
app.UseStatusCodePagesWithRedirects("/CustomErrorPages/{0}.html");
As evident, the code now redirects to distinct HTML pages stored as 404.html, 500.html, and so forth. This allows you to display various custom error pages for different status codes.
5) Use StatusCodePagesWithReExecute() - Re-execute the request pipeline with an alternate action.
The UseStatusCodePagesWithReExecute() is comparable to UseStatusCodePagesWithRedirects() in that both execute an alternative action in the presence of an HTTP error. Nevertheless, UseStatusCodePagesWithRedirects() initiates redirection to the alternate action, while UseStatusCodePagesWithReExecute() re-executes the entire request pipeline and executes the alternate action.
The UseStatusCodePagesWithRedirects() sends a status code of 302 to the browser, and the browser's address bar reflects the new URL upon redirection. In the case of UseStatusCodePagesWithReExecute(), the original status code is sent to the browser, and the address bar will continue to display the original URL.
You can employ the UseStatusCodePagesWithReExecute() method in the following manner:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
....
....
app.UseStatusCodePagesWithReExecute("/CustomErrorPages/{0}");
....
....
}
In this case, the browser output remains consistent, and the URL does not change, as in the earlier case.
Conclusion!
In conclusion, effectively troubleshooting errors in a .NET Core website involves a systematic and strategic approach.
By leveraging various methods such as UseDeveloperExceptionPage(), UseExceptionHandler(), UseStatusCodePages(), UseStatusCodePagesWithRedirects(), and UseStatusCodePagesWithReExecute(), developers can gain insights into and manage different types of errors.
Whether it's detailed exception information during development, custom error handling, or redirection based on HTTP status codes, these techniques provide the means to identify, analyze, and address errors within a .NET Core web application.
Additionally, the ability to customize error pages and re-execute the request pipeline offers flexibility in handling diverse scenarios, contributing to a robust and resilient web application.
