Asp.Net Core Custom Error Handling
my custom Exception class in BaseLibrary
public class HttpStatusCodeException : Exception
{
public int StatusCode { get; set; }
public string ContentType { get; set; } = @"text/plain";
public HttpStatusCodeException(int statusCode):base("")
{
this.StatusCode = statusCode;
}
public HttpStatusCodeException(int statusCode, string message) : base(message)
{
this.StatusCode = statusCode;
}
public HttpStatusCodeException(int statusCode, Exception inner) : this(statusCode, inner.ToString()) { }
public HttpStatusCodeException(int statusCode, JObject errorObject) : this(statusCode, errorObject.ToString())
{
this.ContentType = @"application/json";
}
}
my custom Middleware
public class HttpStatusCodeExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<HttpStatusCodeExceptionMiddleware> _logger;
private readonly string _errorPath;
public HttpStatusCodeExceptionMiddleware(RequestDelegate next,
ILoggerFactory loggerFactory, string errorPath)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_logger = loggerFactory?.CreateLogger<HttpStatusCodeExceptionMiddleware>()
?? throw new ArgumentNullException(nameof(loggerFactory));
_errorPath = errorPath;
}
public async Task Invoke(HttpContext context)
{
int statusCode;
string message = "";
try
{
await _next(context);
if (context.Response.StatusCode == StatusCodes.Status404NotFound)
{
_logger.LogInformation("Not Found Page 404 status code.");
throw new HttpStatusCodeException(404, $"Not Found");
}
}
catch (Exception ex)
{
var httpStatusCodeException = ex as HttpStatusCodeException;
if (httpStatusCodeException != null)/// my errors
{
_logger.LogInformation("I throw HttpStatusCodeException");
_logger.LogInformation(httpStatusCodeException.Message);
statusCode = httpStatusCodeException.StatusCode;
message = httpStatusCodeException.Message;
}
else ///system errors
{
// TODO: Do something with the exception
statusCode = context.Response.StatusCode;
}
try
{
context.Response.Headers.Clear();
context.Response.StatusCode = statusCode;
context.Request.Path = $"{_errorPath}/{statusCode}/{message}";
await _next(context);
}
catch (Exception ex2)//if there are something errors routing
{
_logger.LogInformation("ex2", ex2.Message);
await context.Response.WriteAsync($"Error Status Code: {statusCode}, Message: {message}");
return;
}
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class HttpStatusCodeExceptionMiddlewareExtensions
{
public static IApplicationBuilder UseHttpStatusCodeExceptionMiddleware(this IApplicationBuilder builder, string errorPath=null)
{
return builder.UseMiddleware<HttpStatusCodeExceptionMiddleware>(errorPath);
}
}
add app.UseHttpStatusCodeExceptionMiddleware($"/Errors/Error")
in Startup.cs
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
//custom error middleware
app.UseHttpStatusCodeExceptionMiddleware($"/Errors/Error");
}
in ErrorsController.cs
// for custom middleware
[Route("Errors/Error/{statusCode}/{message?}")]
public IActionResult Error(string statusCode, string message)
{
ViewData["StatusCode"]=statusCode;
ViewData["Message"]=message;
return View();
}
in Views > Shared > Error.cshtml
@{
string statusCode = ViewData["StatusCode"]?.ToString();
string message = ViewData["Message"]?.ToString();
if (string.IsNullOrWhiteSpace(message))
{
switch (statusCode)
{
case "400":
message = "Bad Request";
break;
case "404":
message = "Not Found";
break;
case "401":
message = "Unauthorized";
break;
case "403":
message = "Forbidden";
break;
case "500":
message = "Internal Server Error";
break;
default:
statusCode = "404";
message = "Not Found";
break;
}
}
ViewData["Title"] = "Error " + statusCode;
}
<h1 class="text-danger">Error @statusCode</h1>
<h4 class="text-danger">
@message
</h4>
<!-- ... -->
some test action in HomeController.cs
public class HomeController : Controller
{
public IActionResult Index() => View();
//Error examples
public IActionResult Test1() => View();//not view file
public IActionResult Test2() => throw new HttpStatusCodeException(400);
public IActionResult Test3() => throw new HttpStatusCodeException(401,"This is test Unauthorized");
public IActionResult Test4() => throw new Exception();
}
some ready middleware in Startup.cs
app.UseStatusCodePages();
app.UseExceptionHandler($"/Errors");
app.UseStatusCodePagesWithReExecute("/Errors/Index/{0}");
for system middleware and get errors in ErrorsController.cs
// for system middleware
public IActionResult Index(string id)
{
// when an exception occurs, route to /Errors/Index
///app.UseStatusCodePages();
///app.UseExceptionHandler($"/Errors/Index");
///app.UseStatusCodePagesWithReExecute("/Errors/Index/{0}");
// Get the details of the exception that occurred
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionFeature != null)
{
// Get which route the exception occurred at
string routeWhereExceptionOccurred = exceptionFeature.Path;
// Get the exception that occurred
Exception exceptionThatOccurred = exceptionFeature.Error;
// TODO: Do something with the exception
// Log it with Serilog?
// Send an e-mail, text, fax, or carrier pidgeon? Maybe all of the above?
// Whatever you do, be careful to catch any exceptions, otherwise you'll end up with a blank page and throwing a 500
}
return View("~/Views/Shared/Error.cshtml");
}