Introduction 📢
Hey folks!!!
How are you all!!! Here I am with the new blog “Efficient Way of Writing Controlled Global Exceptions in.NET Core”.
In this exciting blog titled “Efficient Way of Writing Controlled Global Exceptions in.NET Core” I’ll share valuable insights on effectively managing exceptions in a controlled manner, resulting in a positive impact on your application. This article offers unique perspectives that you won’t find elsewhere. So, get ready to dive into some interesting learnings!
Make sure to pay attention to the specific use case I’ll discuss later in the article below.
Why you need custom loggings🤔
There are numerous reasons why you may desire these features, but for the sake of this article’s focus and objective, I won’t delve into all of them here. However, I will provide a list of some common reasons:
- Customizing logging behavior.
- Tracking additional contextual information.
- Logging to multiple destinations (e.g., displaying informational logs in the console while storing only failed requests in the LoggingDB).
- Improving logging readability for future analysis.
- Integrating and customizing behavior for multiple destinations (e.g., storing informational logs in different databases and error logs in separate databases).
- Presenting a well-formatted error structure to API users or consumers.
Any other way to write global level logs other than Middleware way?
Yes, you can implement IExceptionFilter
. However, it's essential to understand the fundamental difference between IExceptionFilter
and CustomExceptionHandlerMiddleware
in terms of their execution within the request pipeline. Perhaps I will write another article that provides a detailed comparison and helps you choose between the two.
For now, the diagram below will give you a general understanding of when each component comes into play:
‘UseCase’ for the approach that I use✍:
I have multiple microservices as a part of my big codebase. These multiple web API services will have almost same code for GlobalExceptionHandlerMiddleware
, which basically handles unhandled exceptions at the request level.
However, I need to be consistent in terms of standard HTTP response codes so that I don’t violate DRY principles.
For the most commonly encountered exceptions, the best practices follow the following HTTP codes.
Let's architect this scenario into code 🏹:
Yah! so, the idea is to abstract the most used exceptions along with their http code in a separate shared library.
Below, we have Three exception types in our shared library DomainException
,ApplicationException
and InfrastructureException
.
And the consumers will inherit from below Three base exceptions.
Write DomainException
base
[Serializable]
public abstract class DomainException: Exception {
protected DomainException() {}
protected DomainException(string message)
: base(message) {}
protected DomainException(string message, Exception exception)
: base(message, exception) {}
}
Write ApplicationException
base
[Serializable]
public class ApplicationException: Exception {
protected ApplicationException() {}
protected ApplicationException(string message)
: base(message) {}
protected ApplicationException(string message, Exception exception)
: base(message, exception) {}
}
Write InfrastructureException
base
[Serializable]
public class InfrastructureException: Exception {
protected InfrastructureException() {}
protected InfrastructureException(string message)
: base(message) {}
protected InfrastructureException(
string message, Exception exception)
: base(message, exception) {}
}
Write Middleware implementation
// GlobalExceptionHandlerMiddleware.cs
public class GlobalExceptionHandlerMiddleware {
public async Task InvokeAsync(HttpContext context) {
try {
await _next(context).ConfigureAwait(false);
} catch (DomainException ex) {
await HandleException(context, ex, HttpStatusCode.BadRequest);
} catch (ApplicationException ex) {
await HandleException(context, ex, HttpStatusCode.InternalServerError);
} catch (InfrastructureException ex) {
await HandleException(context, ex, HttpStatusCode.ServiceUnavailable);
} catch (ArgumentException ex) {
await HandleException(context, ex, HttpStatusCode.BadRequest);
} catch (Exception ex) {
await HandleException(context, ex, HttpStatusCode.InternalServerError);
}
}
private async Task HandleException(HttpContext httpContext, Exception exception, HttpStatusCode statusCode, bool logRequestBody = true) {
// Log into Infra/Console/File etc
WriteResponse(httpContext, statusCode);
}
//Modify and return request context
private async Task WriteResponse(HttpContext httpContext, HttpStatusCode statusCode) {
httpContext.Response.ContentType = "application/json";
httpContext.Response.StatusCode = (int) statusCode;
var responseModel = new {
..........
};
await httpContext.Response.WriteAsync(
JsonSerializer.Serialize(responseModel, _seralizeSetting),
Encoding.UTF8);
}
}
//Expose an extension method from your shared library
public static IApplicationBuilder UseGlobalExceptionHandling(
this IApplicationBuilder app) {
app.UseMiddleware<GlobalExceptionHandlerMiddleware>();
return app;
}
*Finally, Make sure your code line app.UseGlobalExceptionHandeling()
as your first middleware in request pipeline.
Enjoy using Exceptions from consumer code:
Summary📚
I’m excited to share this blog titled “Efficient Way of Writing Controlled Global Exceptions in .NET Core”! In this article, I’ll provide valuable insights on how you can effectively manage exceptions in a controlled manner, ensuring a positive impact on your application.
We saw below points:
- We were onto mission on: valuable insights on how you can effectively manage exceptions in a controlled manner.
- We saw the ‘why’ part of ‘need of custom logging’.
- Then we saw the UseCase of above approach I had written.
- We saw the HTTP codes for general types of exceptions.
- Then we also went through the implementation idea and the actual hands-on code by making our hands dirty.
- Above is just example in my perticular use case. You can have your own/different ExceptionsTypes that most suits in your context.🙌
Top comments (1)
Thanks for sharing! Efficient exception handling is crucial in any development project. Looking forward to learning more about Forbidden Pants approach in .NET Core.