Catch Exceptions Globally using ElmahCore in Aspnet core 3.0? - asp.net-core

I am using Aspnet core 3.0 and I have configured ElmahCore for exception handling. however from their documentation, they advise to catch exceptions using
public IActionResult Test()
{
HttpContext.RiseError(new InvalidOperationException("Test"));
...
}
How can I configure Elmahcore to automatically catch and log all exceptions? or Do I have to write HttpContext.RiseError everytime I want to catch and log an exception?
Like Do I have to put try catch blocks for every ActionResult and call HttpContext.RiseError() in all of my catch blocks?
Is there a way that I can configure catching and logging of exceptions using ElmahCore globally?

Based on #Fei-han's suggestion and this global error handling link, I am able to log exceptions globally in my production environment. In Startup.cs file, I made sure that I have an ExceptionHandler configured when my application is running in production mode like
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseElmah();
//Other configurations
}
This ensures that whenever an uncaught exception has occurred, it will call the Error Action Method of Home Controller
Home Controller
using Microsoft.AspNetCore.Diagnostics;
public IActionResult Error()
{
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
if (exceptionFeature != null)
{
// Get the exception that occurred
Exception exceptionThatOccurred = exceptionFeature.Error;
//log exception using ElmahCore
HttpContext.RiseError(exceptionThatOccurred);
}
//Return custom error page (I have modified the default html of
//Shared>Error.cshtml view and showed my custom error page)
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
Now all of my exceptions are getting logged and I am also showing a customized error page in response to an exception.

Related

in asp.net core, should i still use try-catch if i already have exception filter?

I'm talking about general functions in controller repositories: database stuff and logic.
I presume that a-sync calls to-from other api should always be with try catch in case those fail.
But about all the other stuff, should i still use try-catch or just one exception filter and be done with it??
I'm not sure what the common architecture use in the industry.
thanks
In my preference and experience Yes, even if you have exception filters in ASP.NET Core, you should still use try-catch blocks. While useful, exception filters might not be able to handle every possible error circumstance in your application.
Try-catch blocks let you handle failures that might happen inside the scope of a particular method or block of code as well as more precisely handle exceptions.
Additionally, they let you to capture exceptions and carry out further processing or logging before re-throwing the exception for handling by a higher-level handler, such an exception filter.
It is possible to handle errors globally with the built-in middleware. So you need to create a class
namespace GlobalErrorHandling.Extensions
{
public static class ExceptionMiddlewareExtensions
{
public static void ConfigureExceptionHandler(this IApplicationBuilder app,
ILoggerManager logger)
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode
.InternalServerError;
context.Response.ContentType = "application/json";
var contextFeature = context.Features
.Get<IExceptionHandlerFeature>();
if(contextFeature != null)
{
logger.LogError($"Ooops. : {contextFeature.Error}");
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error."
}.ToString());
}
});
});
}
}
}
and
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ILoggerManager logger)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.ConfigureExceptionHandler(logger);
// ... the other code is omitted for the brevity
}
Read more in this great article.

Core 7 - Api error handling, model state validation + UseExceptionhandler

I am currently working on implementing some Apis using swagger/swashbuckle in net core 7 and implementing some error handling, I've gone down the route of using an exception handler. With separate endpoints from dev/prod.
E.g. Startup.cs
if (env.IsDevelopment())
{
...details ommited
app.UseExceptionHandler("/dev-error");
}
else
{
...details ommited
app.UseExceptionHandler("/error");
}
ErrorController.cs
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public class ErrorController : Controller
{
private ILogger _logger;
public ErrorController(ILogger logger)
{
_logger = logger;
}
[Route("dev-error")]
public IAttempt DevError()
{
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
var exception = context.Error;
return Attempt.Fail(exception);
}
[Route("error")]
public IAttempt Error()
{
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
var exception = context.Error;
_logger.Log(LogLevel.Error, exception, exception.Message);
switch (exception)
{
case UnauthorizedAccessException:
Response.StatusCode = (int) HttpStatusCode.Unauthorized;
return Attempt.Fail("Unauthorised");
default:
Response.StatusCode = (int) HttpStatusCode.InternalServerError;
return Attempt.Fail("Generic Error");
}
}
}
The idea is that all responses are of IAttempt, so that the FE user can check if its succeeded etc. and whether to handle the result or exception in a user friendly way.
This has been working great up until now when I've been implementing Api's that require the model to be validated. I wanted to amend the IAttempt class to provide modelstate feedback, however I have tried many approaches and cant seem to get modelstate validation flow through the exception handler.
I wanted to implement a custom ValidationException that contains the errors which is then handled in these controllers. But when an exception is thrown in either an IActionFilter or when overriding the InvalidModelStateResponseFactory the exception isn't caught by the exception handler.
Is there a work around? Am I missing something?
Alternatively I could define a InvalidModelStateResponseFactory that returns a similar model(IAttempt), but it would be nice for Failed requests to be handled in one place.
Cheers in advance
I think you can make the InvalidModelStateResponseFactory redirect to the ErrorController, sending the required data to create your response
According to your description, I suggest you could consider using the customer action filter to achieve your requirement.
Inside the custom action filter, we could get the model state's results, then you could throw the exception inside it.
More details, you could refer to below codes:
1.Create the custom action filter:
public class CustomValidationActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
if (!context.ModelState.IsValid)
{
var errorList = context.ModelState.Values
.SelectMany(m => m.Errors)
.Select(m => m.ErrorMessage)
.ToList();
throw new Exception();
}
}
public void OnActionExecuting(ActionExecutingContext context) { }
}
2.Inside the program.cs
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new CustomValidationActionFilter());
});
Then if it thrown the exception, it will go to the controller's error action method, since you set the global exception handler.
I was unnecessarily over complicating things so I have dropped what I attempted to do as in theory responses should be handled accordingly to their response status code rather then the object thats passed in.

How to a I redirect to Custom Error Handler Page

I want a generic error page for all application errors.
I have followed the guidelines to create a custom error handler in ASP.NET core and this catches the errors as expected. However, I cannot see how to redirect to a generic error handling the page. Examples seemed to be focused on Web API, not UI.
I have the following custom error handling code
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
int exceptionId = ExceptionManager.Publish(exception);
return context.Response.WriteAsync(new ErrorViewModel()
{
ExceptionId = exceptionId
}.ToString());
}
The exception details are logged to a database and return an Id. I have a controller action that displays the Id so the users can report it.
How do I redirect to my error view?
In Startup.cs method you need to call ExceptionHandlerMiddleware like below.
app.UseMiddleware(typeof(ExceptionHandlerMiddleware));
create a middleware class and write below code
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate next;
public ExceptionHandlerMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
//Write you logic
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.StatusCode = 500;
if (IsRequestAPI(context))
{
//when request api
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
State = 500,
message = exception.Message
}));
}
else
{
//when request page
context.Response.Redirect("/Home/Errorpage");
}
}
}
Middleware is "waterfalled" down through until either all have been executed, or one stops execution (in the case of our exception handling, we'll be writing ours so it stops the execution. More on that later).
The first things passed to your middleware is a request delegate. This is a delegate that takes the current HttpContext object and executes it. Your middleware saves this off upon creation and uses it in the Invoke() step.
Invoke() is where the work is done. Whatever you want to do to the request/response as part of your middleware is done here. Some other usages for middleware might be to authorize a request based on a header or inject a header into the request or response. For more examples, check out the Middleware documentation.

Error handling in the controller

I have this two controllers actions
#1 will throw divide by zero exception
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
var x = 0;
var y = 5 / x;
return View();
}
#2 after the exception, i want this action to be called in production mode.
public IActionResult Error()
{
var model = new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier };
return View(model);
}
I have two issues
When I am in VS Debug mode, app.UseDeveloperExceptionPage() is used and therefore Error controller is not called (that is fine for development environment).
When I am in VS Production mode, visually app.UseDeveloperExceptionPage() is used but i dont know how to confirm what environment is really used (breakpoints in startup.cs does not work in production mode.)
If somehow Error action was called, how can I get full exception message (for log purpose) ? By default only RequestId is stored inside the model.
I do not want to use try catch syntax in each action or repeat action filter attribute, because i want to have general exception handler implemented like i had before in global.asax before core version.
My startup.cs is very easy.
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseStatusCodePages();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
any idea ?
To get the exception details, you just need:
var ex = HttpContext.Features.Get<IExceptionHandlerFeature>();
we can use the below line of code
var feature = HttpContext.Features.Get<IExceptionHandlerFeature>();
var error = feature?.Error;
_logger.LogError("Oops!", error);
return View("~/Views/Shared/Error.cshtml", error);
Can you refer the below link for more details
Click

Prevent error page action executing directly from a request

In asp.net-core we can show user-friendly error pages by adding the StatusCodePages middleware to the pipeine. In Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// code ...
app.UseExceptionHandler("/error/500");
app.UseStatusCodePagesWithReExecute("/error/{0}");
// code ...
}
With above code, when an unhandled exception occurs or a requested resource can not be found, the responses is handled by redirecting to /error/{0}. Framework correctly invokes this action
[Route("[controller]")]
public class ErrorController : Controller
{
[HttpGet("{statusCode}")]
public IActionResult Error(int statusCode)
{
Response.StatusCode = statusCode;
return View("Error", statusCode);
}
}
The problem starts when client directly requests something like ~/error/{int}. For example www.example.com/error/500 or www.example.com/error/400
In these cases again the above action is being invoked (from MVC not StatusCodePages middleware) and client gets a 500 and a 400 response. In my opinion, 404 status code must be returned for all ~/error/{int} requests.
Is there any solution, when client makes a ~/error/{int} request to prevent MVC middleware from invoking the error action?
Use HttpContext.Features.Get<IExceptionHandlerFeature>() to check whether an error has occurred. If one hasn't, then return a 404. Here is an example.
ErrorController.cs
using Microsoft.AspNet.Diagnostics;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Mvc;
[Route("[controller]")]
public class ErrorController : Controller
{
[HttpGet("{statusCode}")]
public IActionResult Error(int statusCode)
{
var feature = HttpContext.Features.Get<IExceptionHandlerFeature>();
if (feature == null || feature.Error == null)
{
var obj = new { message = "Hey. What are you doing here?"};
return new HttpNotFoundObjectResult(obj);
}
return View("Error", statusCode);
}
}
According to the docs (emphasis added),
The HttpContext type... provides an interface for getting and setting these features... Use the pattern shown above for feature detection from middleware or within your application. Calls made to GetFeature will return an instance if the feature is supported, or null otherwise.