I have the following middleware in my ASP.NET Core application:
public class ExceptionHandlingMiddleware : IMiddleware
{
// Constructor and other stuff...
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
try
{
await next(context);
}
catch (Exception ex)
{
_logger.Error(ex, "An unhandled exception was thrown!");
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
// Handle exception and return a human-readable
// message in a JSON-object in the response...
}
}
Which I add to the pipeline in the Startup.cs
app.UseMiddleware<ExceptionHandlingMiddleware>();
When running the application from Visual Studio the debugger doesn't break when an exception happens, but is just handled in the middleware and execution continues. However, I want the debugger to break at the place the exception is thrown, when it's not caught by anything but this middleware. How do I achieve this?
I actually tried not adding this middleware when running locally. E.g. doing the following in Startup.cs:
// I'm setting env to "none" when running locally
if (!env.IsEnvironment("none"))
{
app.UseMiddleware<ExceptionHandlingMiddleware>();
}
But this seems to just cause the frontend to act differently - understandably as it's not receiving a readable JSON-object, but it makes me think the problem is not my middleware. I'd prefer to keep this middleware active but if that's not possible while getting the debugger to break properly I guess I can live with that.
Related
I have a .net 6 console app that I configured with polly policies depending on what each service does.
Program.cs
try
{
//other setup code
services
.AddHttpClient<ISubjectData, SubjectData>()
.AddTransientHttpErrorPolicy(ConfigurePolicy);
//other setup code
IAsyncPolicy<HttpResponseMessage> ConfigurePolicy(PolicyBuilder<HttpResponseMessage> policy)
{
try
{
return policy.Or<TaskCanceledException>()
.WaitAndRetryAsync(Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromSeconds(10), 5));
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
The policy is working, however, the program is throwing an unhandled exception which is TaskCanceledException because the HttpClient timed out, which isn't being caught by either catch statements or the policy in ConfigurePolicy.
How can I catch this error, since it is crashing the app?
Also, is there a way to allow Polly to override HTTP client timeout depending on how long it takes to complete all retries?
TL;DR:
You can't catch the exception because you wrapped the policy definition with try- catches.
In case of Polly the policy definition and its execution are separated.
You define a policy (or a combination of policies)
You are decorating some method(s) with the above policy
You are executing the decorated method explicitly or implicitly through the policy.
Whenever you register a typed/named HttpClient and decorate its methods with an IAsyncPolicy<HttpResponseMessage> then you are doing only the first two steps.
Whenever you call a method on the HttpClient, like PostAsync or SendAsync that's where the policy is being executed on your behalf / implicitly (via a special DelegatingHandler).
That's where you can catch the exceptions
ones which is thrown either by the HttpClient (like TaskCancelledException, HttpRequestException, etc.)
or those that are thrown by one of the policies (like TimeOutRejectedException, BrokenCircuitException, etc.)
UPDATE #1
What is confusing to me, is why does a try-catch block that covers my entire Program.cs not catch the exception, but a try-catch block in the scope of the request does? Aren't exceptions propagated back to the original caller? (The original caller would be a method inside my Program.cs file)
Try catch inside the ConfigurePolicy
As I stated above, here you are building the policy, not executing it. So, the try-catch here can be used to detect and resolve misconfiguration.
Try catch inside Program
Yet again the AddHttpClient and AddTransientHttpErrorPolicy are just builder methods. They are not executing any http request on your behalf. So, wrapping this code into try-catch can help you to spot misconfiguration.
Your try-catch is covering those exceptions which are thrown by the Main but not those which are thrown by different threads which are not in the Main.
Using Serilog with .Enrich.FromLogContext()
I am adding Logged In User as a scope late in pipeline using LogContext.PushProperty("UserName", user.Name)
The issue is that this scope is not available on return pipeline like in ExceptionHandling, RequestLogging etc.. So I am missing important information when exception occurred.
Example of pipeline ::
RequestLogging --> ExceptionHandling --> Authentication --> Controller |
|
RequestLogging <-- ExceptionHandling <-- Authentication --> Controller |
I tried with IDiagnosticContext but it is only available in UseSerilogRequestLogging()
Could you please advice how to work around it?
In your Invoke method of the exception handling middleware you have as argument the current HttpContext. Knowing this you can access the identity property of the context.
public async Task Invoke(HttpContext context)
{
try
{
await this.next(context);
}
catch (Exception e)
{
var username = context.User?.Identity?.Name ?? "__unknown";
using (LogContext.PushProperty("Username", username))
{
this.logger.Log(......)
}
}
}
This way you will have access to the logged user even if the exception middleware is first in the pipeline as long as the exception has occurred after the authentication middleware.
EDIT 02/10/2020
If you need the identity information in the .UseSerilogRequestLogging() you can use the overload witch accepts options. Check the last example in Request logging on their github page.
I've been reading about errors handling in ASP.NET Core and I came across these 2 ways:
UseExceptionHandler("/error")
UseStatusCodePagesWithRedirects("/error/{0}");
I'm wondering what's the difference between the two? Both redirect to an error page so why use one over the other? I even saw some people using them both at the same time.
You are right that both middlewares do provide error pages. However, they have two different use cases which will make it useful to actually use both at the same time in an application. To understand the differences, let’s take a look at how the middlewares actually work internally.
This is essentially what the StatusCodePages middleware does:
// …
await _next(context);
// …
// Do nothing if a response body has already been provided.
if (context.Response.HasStarted
|| context.Response.StatusCode < 400
|| context.Response.StatusCode >= 600
|| context.Response.ContentLength.HasValue
|| !string.IsNullOrEmpty(context.Response.ContentType))
{
return;
}
var statusCodeContext = new StatusCodeContext(context, _options, _next);
await _options.HandleAsync(statusCodeContext);
It executes the pipeline by calling _next and after the call has returned (meaning that all following middlewares have executed), it will inspect the current response: Basically, if there’s an error status code or no content at all, it will execute the status code page, signalizing the HTTP status code.
The ExceptionHandler middleware on the other hand does something very different:
try
{
await _next(context);
}
catch (Exception ex)
{
// …
try
{
// …
await _options.ExceptionHandler(context);
// …
return;
}
catch (Exception ex2)
{
// Suppress secondary exceptions, re-throw the original.
_logger.ErrorHandlerException(ex2);
}
throw; // Re-throw the original if we couldn't handle it
}
This will try to invoke the middleware pipeline and catch any exception it might produce. Then, it will attempt to run the registered exception handler (which, when setting a path basically means to invoke that path internally and return its response).
So to sum this up:
The StatusCodePages middleware will handle non-successful status code responses and allows you to specify e.g. custom error pages for things like a 404 Not Found.
The ExceptionHandler middleware on the other hand will catch unhandled exceptions in your application and allows you to handle those gracefully for the end user.
Both middlewares have different purposes and actually don’t overlap in what they do. So it often makes sense to include both of them, unless you handle these issues differently of course; e.g. an API will probably not need status code pages, but might still want an exception handler that returns a generic failure and logs everything properly.
Is there anyway to capture handled exceptions when CreateErrorResponse() is used to return Error response with API methods?
I currently have registered a simple global exception loggger for any exceptions, however for some api responses including Model Filter attribute, I'm CreateErrorResponse() to return error responses but this doesn't to enter the Log() method in the Global Exception Logger.
Example usage of CreateErrorResponse in an API method :
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
Global Exception Logger:
public class GlobalExceptionLogger : ExceptionLogger
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public override void Log(ExceptionLoggerContext context)
{
log.Error(context.Exception);
}
}
The only way to get Log() method to be called is rather than use CreateErrorResponse, throw an Exception instead.
Is this correct?
You should log the error and any trace info in the same negative flow where you are constructing and returning the Error Response.
Using exceptions to control application flow is bad practice, avoid it.
The global handlers use is to handle anything that you haven't already handled and should be for Very exceptional cases, you also log there and present the user with a generic error message, while you sort out the chaos in the back of course :)
If I misunderstood your question please correct me.
I am trying to install and test ELMAH for the fist time.
I think I have setup everything correctly.
I know ELMAH is designed to log unhandled exceptions.
I use a standard template MVC 4 application generated by Visual Studio 2012
In a HomeControler class I throw an error without try and catch block
public ActionResult About()
{
// Throw a test error so that we can see that it is handled by Elmah
// To test go to the ~/elmah.axd page to see if the error is being logged correctly
throw new Exception("A test exception for ELMAH");
return View();
}
In my opinion this an unhandled exception.
Further I use a HandleErrorWithELMAHAttribute class to handle the error. This construct is shown in many ELMAH tutorials originally posted here:
How to get ELMAH to work with ASP.NET MVC [HandleError] attribute?
The code that bothers me is:
public override void OnException(ExceptionContext context)
{
base.OnException(context);
var e = context.Exception;
if (!context.ExceptionHandled // if unhandled, will be logged anyhow
|| RaiseErrorSignal(e) // prefer signaling, if possible
|| IsFiltered(context)) // filtered?
return;
LogException(e);
}
In the if statement the property contect.ExceptionHanled is checked.
This property is set to true, so the thrown error is not logged.
Can you explain why it is set to true while there is no try-catch.
Best Regards,
Sebastian