The exception-handling features help us deal with unforeseen errors that could appear in our code. To handle exceptions we can use the try-catch block in our code as well as finally keyword to clean up resources afterward.
Even though there is nothing wrong with the try-catch blocks in our Actions in Web API project, we can extract all the exception-handling logic into a single centralized place. Doing that makes our actions more readable and the error-handling process more maintainable. If we want to make our actions more readable and maintainable, we can implement Action Filters. We won’t talk about action filters in this article but we strongly recommend reading our post Action Filters in .NET Core.
In this article, we are going to handle errors by using a try-catch block first and then rewrite our code by using built-in middleware and our custom middleware for global error handling to demonstrate the benefits of this approach. We are going to use an ASP.NET Core Web API project to explain these features and if you want to learn more about it (which we strongly recommend), you can read our ASP.NET Core Web API Tutorial.
VIDEO: Global Error Handling in ASP.NET Core Web API video.
Let’s start.
Error Handling With Try-Catch Block
To start with this example, let’s open the Values
Controller from the starting project (Global-Error-Handling-Start project). In this project, we can find a single Get()
method and an injected Logger
service.
It is a common practice to include the log messages while handling errors, therefore we have created the LoggerManager
service. It logs all the messages to the C
drive, but you can change that by modifying the path in the nlog.config
file. For more information about how to use NLog in .NET Core, you can visit Logging with NLog.
Now, let’s modify our action method to return a result and log some messages:
[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { private ILoggerManager _logger; public ValuesController(ILoggerManager logger) { _logger = logger; } [HttpGet] public IActionResult Get() { try { _logger.LogInfo("Fetching all the Students from the storage"); var students = DataManager.GetAllStudents(); //simulation for the data base access _logger.LogInfo($"Returning {students.Count} students."); return Ok(students); } catch (Exception ex) { _logger.LogError($"Something went wrong: {ex}"); return StatusCode(500, "Internal server error"); } } }
When we send a request at this endpoint, we will get this result:
And the log messages:
We see that everything is working as expected.
Now let’s modify our code, right below the GetAllStudents()
method call, to force an exception:
throw new Exception("Exception while fetching all the students from the storage.");
Now, if we send a request:
And the log messages:
So, this works just fine. But the downside of this approach is that we need to repeat our try-catch
blocks in all the actions in which we want to catch unhandled exceptions. Well, there is a better approach to do that.
Handling Errors Globally With the Built-In Middleware
The UseExceptionHandler()
middleware is a built-in middleware that we can use to handle exceptions in our ASP.NET Core Web API application. So, let’s dive into the code to see this middleware in action.
First, we are going to add a new class ErrorDetails
in the Models
folder:
public class ErrorDetails { public int StatusCode { get; set; } public string Message { get; set; } public override string ToString() { return JsonSerializer.Serialize(this); } }
We are going to use this class for the details of our error message.
To continue, let’s create a new folder Extensions
and a new static class ExceptionMiddlewareExtensions
inside it.
Now, we need to modify it:
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($"Something went wrong: {contextFeature.Error}"); await context.Response.WriteAsync(new ErrorDetails() { StatusCode = context.Response.StatusCode, Message = "Internal Server Error." }.ToString()); } }); }); } }
In the code above, we’ve created an extension method in which we’ve registered the UseExceptionHandler
middleware. Then, we populate the status code and the content type of our response, log the error message and finally return the response with the custom-created object.
To be able to use this extension method, let’s modify the Configure
method inside the Startup
class for .NET 5 project:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerManager logger) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.ConfigureExceptionHandler(logger); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
Or if you are using .NET 6 and above:
var app = builder.Build(); var logger = app.Services.GetRequiredService<ILoggerManager>(); app.ConfigureExceptionHandler(logger);
Finally, let’s remove the try-catch
block from our code:
public IActionResult Get() { _logger.LogInfo("Fetching all the Students from the storage"); var students = DataManager.GetAllStudents(); //simulation for the data base access throw new Exception("Exception while fetching all the students from the storage."); _logger.LogInfo($"Returning {students.Count} students."); return Ok(students); }
And there you go. Our action method is much cleaner now and what’s more important we can reuse this functionality to write more readable actions in the future.
So let’s inspect the result:
And the log messages:
Excellent.
Now, we are going to use custom middleware for global error handling.
Handling Errors Globally With the Custom Middleware
Let’s create a new folder named CustomExceptionMiddleware
and a class ExceptionMiddleware.cs
inside it.
We are going to modify that class:
public class ExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILoggerManager _logger; public ExceptionMiddleware(RequestDelegate next, ILoggerManager logger) { _logger = logger; _next = next; } public async Task InvokeAsync(HttpContext httpContext) { try { await _next(httpContext); } catch (Exception ex) { _logger.LogError($"Something went wrong: {ex}"); await HandleExceptionAsync(httpContext, ex); } } private async Task HandleExceptionAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; await context.Response.WriteAsync(new ErrorDetails() { StatusCode = context.Response.StatusCode, Message = "Internal Server Error from the custom middleware." }.ToString()); } }
The first thing we need to do is to register our IloggerManager
service and RequestDelegate
through the dependency injection. The _next
parameter of RequestDeleagate
type is a function delegate that can process our HTTP requests.
After the registration process, we create the InvokeAsync()
method. RequestDelegate can’t process requests without it.
If everything goes well, the _next
delegate should process the request, and the Get
action from our controller should generate a successful response. But if a request is unsuccessful (and it is, because we are forcing an exception), our middleware will trigger the catch block and call the HandleExceptionAsync
method.
In that method, we just set up the response status code and content type and return a response.
Now let’s modify our ExceptionMiddlewareExtensions
class with another static method:
public static void ConfigureCustomExceptionMiddleware(this IApplicationBuilder app) { app.UseMiddleware<ExceptionMiddleware>(); }
In .NET 6 and above, we have to extend the WebApplication
type:
public static void ConfigureCustomExceptionMiddleware(this WebApplication app) { app.UseMiddleware<ExceptionMiddleware>(); }
Finally, let’s use this method in the Configure
method in the Startup
class:
//app.ConfigureExceptionHandler(logger); app.ConfigureCustomExceptionMiddleware();
Now let’s inspect the result again:
There we go. Our custom middleware is implemented in a couple of steps.
Customizing Error Messages
If you want, you can always customize your error messages from the error handler. There are different ways of doing that, but we are going to show you the basic two ways.
First of all, we can assume that the AccessViolationException
is thrown from our action:
[HttpGet] public IActionResult Get() { _logger.LogInfo("Fetching all the Students from the storage"); var students = DataManager.GetAllStudents(); //simulation for the data base access throw new AccessViolationException("Violation Exception while accessing the resource."); _logger.LogInfo($"Returning {students.Count} students."); return Ok(students); }
Now, what we can do is modify the InvokeAsync
method inside the ExceptionMiddleware.cs
class by adding a specific exception checking in the additional catch block:
public async Task InvokeAsync(HttpContext httpContext) { try { await _next(httpContext); } catch (AccessViolationException avEx) { _logger.LogError($"A new violation exception has been thrown: {avEx}"); await HandleExceptionAsync(httpContext, avEx); } catch (Exception ex) { _logger.LogError($"Something went wrong: {ex}"); await HandleExceptionAsync(httpContext, ex); } }
Now if we send another request with Postman, we are going to see in the log file that the AccessViolationException
message is logged. Of course, our specific exception check must be placed before the global catch block.
With this solution, we are logging specific messages for the specific exceptions, and that can help us, as developers, a lot when we publish our application. But if we want to send a different message for a specific error, we can also modify the HandleExceptionAsync
method in the same class:
private async Task HandleExceptionAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; var message = exception switch { AccessViolationException => "Access violation error from the custom middleware", _ => "Internal Server Error from the custom middleware." }; await context.Response.WriteAsync(new ErrorDetails() { StatusCode = context.Response.StatusCode, Message = message }.ToString()); }
Here, we are using a switch expression pattern matching to check the type of our exception and assign the right message to the message
variable. Then, we just use that variable in the WriteAsync
method.
Now if we test this, we will get a log message with the Access violation message, and our response will have a new message as well:
{ "StatusCode": 500, "Message": "Access violation error from the custom middleware" }
One thing to mention here. We are using the 500 status code for all the responses from the exception middleware, and that is something we believe should be done. After all, we are handling exceptions and these exceptions should be marked with a 500 status code. But this doesn’t have to be the case all the time. For example, if you have a service layer and you want to propagate responses from the service methods as custom exceptions and catch them inside the global exception handler, you may want to choose a more appropriate status code for the response. You can read more about this technique in the article about the Onion Architecture. It depends on your project organization.
Using the IExceptionHandler Interface from .NET 8
IExceptionHandler
is an interface that we can use to handle exceptions in ASP.NET Core applications. It defines an interface that we can implement to handle exceptions globally. This allows us to write custom logic for handling individual exceptions or groups of exceptions based on their type, in turn providing tailored responses, error messages as well as logging.
Since we already have an article on this topic, feel free to read it here. You will find all the information you need to use this interface, which will improve the handling logic as well.
Conclusion
That was awesome.
We have learned, how to handle errors in a more sophisticated way and cleaner as well. The code is much more readable and our exception handling logic is now reusable for the entire project.
Thank you for reading this article. We hope you have learned new useful things.
Hi Marinko,
Works great in my AspNetCore Web.Api 7.0 app.
I just had to delete the lines below from Program.cs
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
I hope this helps,
Phil
(en.IsDevelopment()) { app
.UseDeveloperExceptionPage(); }
(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
“”
Great stuff Marinko, as always!
Thanks a lot, Milan. Glad to hear that.
Great content! Instead of the hard coding the content type, how can we set dynamic based on the accept header? Is it possible to make it support both XML or JSON?
To be honest, I didn’t try it.
Hi, I have a question, if given a wrong json format,
example:
{
“valueKind”: “string
}
it doesn’t even hit the controller when I place a debug pointer.
it gives error message as following in response body.
can you help me how to log these errors.
That’s because of the ApiController attribute, which does the validation by default and your request can’t reach the action at all. If you want to override that behavior, either remove it (I do not recommend that, but you can do it) or you can turn off the validation from that attribute in the Program or Startup class (depending on which version of .NET you are using):
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
Very useful, thanks!
You are most welcome, I’m glad you like it.
hey, daft question, where is the following found in regards to the menu above? I can’t seem to find what section this and the signalr stuff is in please
Hello Jacob. We didn’t place the mentioned articles in any special section, but they are all connected to the Web API tech. So if you want to read more about it, all you can do is type Web API in the search box, and you will get all the articles there. To be honest, I am not sure how would we group all those articles except in the web API category that you can get from the search box.
hi, no problem, the site is phenomenal, so don’t want to miss out
Thanks a lot Jacob. We’ll try to implement some tags soon, so the readers can easily navigate the articles from the same category (let’s say).
Thanks ..this helped out.
Good day. I am trying to cast the custom exception using the following line of code where statusCode = HttpStatusCode.NotFound.
In my Xunit test when I am trying to ReadFromJsonAsync<ErrorDetails>()
I am getting the following exception i.e.
System.Net.Http.HttpRequestException : Error while copying content to a stream.
The following the implementation of the Exception middleware. Its so strange the above code works for DataValidationException custom exception.
Hi-
I want to show the plugin exception from Dynamics 356 into ASP.net core web API. However, it is getting thrown the same exception from plugin to web API the first time but when we repost the same request which has an exception then it will not throw in postman instead it shows
Error: read ECONNRESET
Hi, Thank you for this great article.
but I have a little problem, I can’t think of a way to handle this response from Blazor wasm.
You can find more on that here: https://code-maze.com/global-http-error-handling-in-blazor-webassembly/ Or even in more detail in our Blazor WebAssembly Video Course, which you can find in the navigation bar.
Hi Marinko, Really an Great exceptional Article ..
Thank you very much. I’m glad you like it.
This no longer works for .NET 6 ? I keep getting this error when running the application when registering the middleware like so:
app.UseMiddleware<ExceptionHandlerMiddleware>();
System.InvalidOperationException: ‘An error occurred when configuring the exception handler middleware. Either the ‘ExceptionHandlingPath’ or the ‘ExceptionHandler’ property must be set in ‘UseExceptionHandler()’. Alternatively, set one of the aforementioned properties in ‘Startup.ConfigureServices’ as follows: ‘services.AddExceptionHandler(options => { … });’.’
The issue was that I was using the wrong import for ExceptionHandlerMiddleware. I was using the one from Microsoft.Diagnostics and not the custom one we created in the tutorial.
Everything works perfectly in .NET 6. I’ve just tested it, and also we’ve implemented this in our Web API book, which is written in .NET 6. Of course, since you don’t have that Startup class and those ConfigureServices and Configure methods, you have to play a bit around (for the first example) to get that ILogger service, but this is quite easy to achieve and then to send it as a parameter to the ConfigureExceptionHandler method. Other than that, the entire example works without a single issue. I am really not sure why do you have this error.
Yes, it was an issue on my side. Thank you for your fast response. All of the tutorials are awesome. 🙂
I am learning .net core. I read your Ultimate ASP.NET Core Web API book and practiced up to part 5 Global Error Handling. I had a problem where the example required adding the ILoggerManager logger to the Configure constructor in Startup.cs in section 5.2 Startup Class Modification. But .net core 6 does not have Startup.cs file but only Program.cs. I don’t know how and am stuck here. Can you help me. Thanks.
Could you please share the email that you’ve used to purchase our book? Also, we now have the second edition in .NET 6 and everything is covered there. You have to extract the logger as a service and then use it, something like this:
var logger = app.Services.GetRequiredService<ILoggerManager>();
app.ConfigureExceptionHandler(logger);
Lovely little article, very informative and very clean. Thankyou very much
You are most welcome.
Thank you, this is exactly what I needed to know. A perfect article on the topic.
Thanks Bruce.
superb:)
Thanks a lot. I’m glad you like it.
Is it possible to use a service that insert something in the database? I’m injecting my service the way you did with your logger, but when i tried to use the DbContext in my service throw me an error (Cannot access a disposed context instance).
I already probed the service in other services and works fine, but here in the ExceptionMiddlewareExtensions is not working. What am I not seeing? =(
I believe that ad soon as your error handling middleware kicks in, EF Core disposes of the context and that’s why you have that error message.
Thanks I like this approach 🙂 Everything works fine
You are most welcome. I’m glad you like it.
Thanks you article so useful
I am glad you like it.
Hello, thank you for your articles. I have used Handling Errors Globally with the Built-In Middleware. I wrote in controller ” throw new Exception(“Exception”);” for checking. But it doesn’t work. I have got only exception in vs 2019. I don’t know why. I have written all such as in article.
https://uploads.disquscdn.com/images/81c80d07e82ae6001e98413b46c1136207811339d6c4e340c325bdc0582e2ba6.png
You can do two things. First you just press the Continue button and you will get an exception in the Postamn. Second you can start your app with CTRL + F5 and the exception won’t hit the debugging code.
./
I have done it. I have created a project on asp net core 5. So I changed WriteAsync to WriteAsJsonAsync and removed ToString() in ErrorDetails
await context.Response.WriteAsJsonAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error."
});
That WAS awesome! Great article. I love having a consistent way catching and handling exceptions through an entire api without any cruft and repeated code.
I just want to add that you can add in a lot of flexibility for some rare cases with something like this:
await _next(httpContext);
switch (error)
{
case AppException e:
// custom application error
response.StatusCode = (int)HttpStatusCode.BadRequest;
break;
default:
// unhandled error
response.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
Hi Mason. First of all, thanks a lot for the nice words and for reading the article. Regarding your addition, I couldn’t agree more. I would just like to mention that in this article, I wanted to set a base project that will help readers with global exception handling and leave it at the point where everyone could extend it to there own needs. So, having you adding your own code is just confirming that I succeded in what I wanted to do 🙂
One more time, thanks a lot for the comment and the code suggestion.
I am using .net 5 now.
Firstly, Which aproach is the best either Built-In Middleware or Custom Middleware for globally exception handling? Secondly I want to see the original exception apart from a general error, “Internal error”. How to do that?
Great Job, No doubt about it. Well done.
Hi ,
I am trying to do the same with .net core 2.1 and it is not working.
Could you suggest if it is even possible? Thanks.
Hello. I believe it can be done with 2.1 as well. If you take a look at the commits in the source coude, you are going to see that we had two updates. To version 2.2 and to version 3.0. So, even this article was published a while ago, I believe that the first version was 2.1 (but I am not 100% sure in that). Why is not working in your project?
Thank you very much for the articles & prompt reply
Thank you for reading. I hope you are going to solve your problem. I’m glad you like our articles, we are giving our best.
As hinted at by Umair Riaz, with this custom error handler, you have to be careful.
If the Controller returns an (un-enumerated) IEnumerable, an exception will be thrown only when the response is serialized. While this should be caught by the middleware, it may be too late to write to the request headers.
Not sure whether there is an elegant way to deal with this (I can only think of ensuring that controllers always enumerate IEnumerables – e.g. by calling .ToList()).
My practice is always after I am finished with shaping my query to execute it with the ToLIst(); And it is done in the service or repo class if there is no service layer. So, there is no warry about un-enumerated data.
Why HandleExceptionAsync method made as static?
Hello Adam, to make a long story short, I forgot to remove it. Thank you for noticing this. Before writing this article I was testing something with static methods and then with non-static ones. In the process it stayed there. It will be fixed. Thank you one more time. Best regards.
Why NOT mark
HandleExceptionAsync
asstatic
? It doesn’t use any class member and thus should be markedstatic
.Hello Dave. First of all, thank you for this one and all the other comments. It is very kind of you. About your question, there is no any great reason for not leaving the method static, you have a point in there. Just my personal preference. Again, thank you very much for your time. Best regards.
Thanks
You are very welcome Navid.
greate article. thanks !
Thank you Damien. I’m glad you enjoyed it. Best regards.
Registering the first extension method
app.ConfigureExceptionHandler()
is sufficient, also this is GLOBAL error handling for asp.net core, it does affect page controllers as well as api controllers.Hello Eric. First of all thank you for reading this article, I hope you can find some more interesting articles on our site. About your comment, I can’t see where we stated that the first method is not sufficient. What we provided is an alternative way of doing the same thing, and this is the way I am doing on my projects. About your second statement, this is the Web API project thus the title in that manner. Of course it is a global way of handling exceptions in ASP.NET Core. Anyhow, best regards.
This is only if you conflate your web app with your API app. It is generally a best practice to separate the Web app from the API.
Thanks, nice post.
You are welcome Make. Thank you for reading.
Hi Marinko, thanks for create this amazing article, it is helping me a lot. This filter only catch exceptions in the main project? I’m trying to implement a project using clean architecture and generating a database exception in the application component to check if it is catched, but in this case the filter exception does not work. Can you give me some advise please? again, thaks a lot for your time and help.
Hello Fred, thank you for the kind words and for reading this article. This global handling should handle errors globally, so, even from another projects. I just created additional project in this one (from the article) and throw exception from there, and everything works as it supposed to. https://uploads.disquscdn.com/images/addc9371457b5f4e9386e038b2dc725882d5893de446ea0a393348f4f6ca2788.png
Hi,
The ExceptionMiddlewareExtensions.cs file is in Entities project and the ILoggerManager.cs is in Contracts project. Adding the Contracts reference in Entities doesn’t work. So Injecting ILoggerManager in the extensions file isn’t possible now. Anything wrong here?
If you are trying to add Global Error Handling to our ASP.NET Core project, from a ASP.NET Core tutorial, then you can’t reference Contracts in Entities, because Entities are already referenced inside the Contracts project. So don’t place this logic into the Entities project, there is no need for that. Place it in a separate folder inside the main project, it is the best place to reside.
Just use var logger = LoggerFactory.CreateLogger(“category name”);
Hi Marinko!
How can I return an JsonResult object to the client?
Details:
suppose we have many actions and some actions need to return a JsonResult object to the client that contains a useful information about his activity.
public async Task ActionMethodA( object a){
// if happens a special exception I want to send some information(JsonResult object) to the client. For example: " First Message!"
// for another exception, I want to have default behavior
}
public async Task ActionMethodB( object b){
// if happens a special exception I want to send some information(JsonResult object) to the client. For example: " Second Message!"
// for another exception, I want to have default behavior
}
Well, if user did something wrong you always have a BadRequest or Not Found or you can return from your action StatusCode() method with the response code and a message as well. With a global way of handling unhandled exceptions, if you want to filter them, then in the HandleExceptionAsync method you can check your exception object and then decide whether or not you want to send a generic message as a response or a non-generic one.
thanks for sharing.
Thank you for reading. All the best.
i like this approach but someone suggested me to follow the technique described in this article: https://www.strathweb.com/2018/07/overriding-externally-set-headers-and-http-status-codes-in-asp-net-core/
can someone tell me which one has more benefits? the article I shared above describes the disadvantage of adding new middleware as: ‘The reason for that is that ASP.NET Core would flush the headers of the response as soon as the first body write happens, and Identity Server, in its pipeline, would start writing to the body already. This means that even though you can technically (there would be no exception thrown for that) change the status code on the response, or inject some headers into it using a custom middleware that runs at the end of the pipeline, that would have no effect on the response anymore, as it is simply too late. You can actually normally see that on the response object by inspecting the response.HasStarted property – at that moment status code and headers modifications are not possible anymore.’
Why are you returning the HTTP error code ? Isn’t it redundant ?
In my case, I will return an internal code for cached exception, and leave it null for unexpected exceptions…
Hello Gab. The purpose of this and all of our other articles is to show how the things can be done and to help readers to adjust it to they needs as you did. So if this article helped you even a bit, it is a great news. About status code, in my practice it is obligatory for API to return not only the payload but the status code as well, and all my actions will behave in that manner. So that’s why 😀
You may want to differentiate between a status code that your code received from an external API vs. the status code you return to the client.
Hi, How can I handle the exception to an action method for creating a user friendly message? I don’t want to show to users only a simple message(content of JSON file)!
Hello. As you can see, we are handling Internal server errors and we return status code of our error as well. Just process the server response on the client side and based on that error status code show whatever message, modal, page you want.
Marinko great post it’s very useful. But I have a doubt I implement this practice on my code with different layers, presentation, business logic, data. And I generate an exception to see how it was working, but I noticed the code stop working and I need to continue the process to return the exception notification. My question is, this is the regular behavior or shouldn’t occur?
Thanks
Hello Daniel. It is normal thing due to debug environment.
There is a way to avoid it when debugging?
May be run it on release mode.
Hello,
Thanks again. Question. One issue I am finding is that this catches every single request to the logError method and cannot be overridden in a controller method for example. Is there a way where this can be used handle UnHandled exceptions? the problem I am facing is that it is very hard to determine exact details of the error (ie actual line number and specific error details). So basically this will only be used for any error that is not caught and handled elsewhere within the application.
Great Stuff!
That is the entire point. This is only to catch unhandled exceptions. Any method with a
try/catch
where thecatch
does not re-throw will not bubble the exception up to the middleware (since it was handled in the method with thetry/catch
)This a great site, thank you
You are welcome Andres.
In the section “Handling Errors Globally with the Built-In Middleware” you modify the
Configure
method inside theStartup
class adding the lineapp.ConfigureCustomExceptionMiddleware();
. Shouldn’t it beapp.ConfigureExceptionHandler()
? ConfigureCustomExceptionMiddleware isn’t created until the section “Handling Errors Globally with the Custom Middleware“.Hello Toure Holder. Yes, you are right. We have been updating our articles to the 2.2 version, and while adding the code parts, I have made that mistake. Thank you for your suggestion. It is going to be fixed right now 😀
Thank you for this article, what about asp.net core mvc razor implementation, I mean when I need to return the same view with user-friendly message where the exception is thrown, how can I support this in exception handler?
Hello burak. Thank you for reading our article and for commenting as well. In MVC project you have already implemented error handling feature. It is just conditioned whether it is development or production environment. You can find in the Configure method this code:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(“/Home/Error”);
app.UseHsts();
}
What you can do is to remove this condition, leave only app.UseExceptionHandler line and to change route to the error controller for example. In there you can create your own action and your own view as well, to show valid messages to the users.
Hope this helps.
All the best.
Great article,
So when would you use Built-In vs. Custom Middleware?
Hello Lee. Thank you for reading this article. Hope you will find other articles useful as well. About your question. As you can see both approaches do the same thing so it is up to you which you want to choose. But I believe that if you want to create some more complex custom logic for your error handling, than the custom approach is a better choice. You can use that exception object in the HandleExceptionAsync method and work your way with it in a different directions.
OK, cool, makes sense thank you. One more thing I’m noticing and not sure if it’s a big deal or not, but in the internallog I keep getting the following exception:
Error Error loading extensions. Exception: System.IO.FileNotFoundException: Could not load file or assembly ‘NLog.Extended, Culture=neutral, PublicKeyToken=null’. The system cannot find the file specified.
This is being referenced in nlog.config. Logging still works fine, but just wondering why this is there and is there supposed to be an error lol.
And yes, I have read the other articles very useful 🙂
Much appreciated!
You are right. I have left one extension tag in the nlog.config file which should be removed. After removal you won’t have that error any more. Thank you for that suggestion, I appreciate it a lot.
All the best.
Unable to resolve service for type ‘MyProject.Middleware.ErrorHandler.ILoggerManager’ while attempting to activate ‘MyProject.Middleware.ErrorHandler.CustomExceptionMiddleware’.
Hello Arjun Arora. Thank you for reading this article. Please use our starting project (link is provided at the top of this article) to follow along with this tutorial. ILogger is already registered in IOC for you.
Is ILogger already registered in case of Custom Middleware? could you prompt me with that code line here?
ILoggerManager is registered for entire project. If you take a look at our starting project source code (or finished project as well) you will find in the Startup class this code:
public void ConfigureServices(IServiceCollection services)();
{
services.AddSingleton
services.AddMvc();
}
As you can see, ILoggerManager is registered inside the IOC as singleton. And all you have to do is to inject it inside constructor, as we did in the ExceptionMiddleware class.
Seems IloggerManager is not installed in my vs code, I cant inject it in my app like i did to RequestDelegate. How could I use it?
Hello Marichu Colon. ILoggerManager is not installed in your project because it is OUR custom project included in our source code. So just download our source code for starting project (link is at the top of the article) and then just follow along. If you like to write your own LoggerManager, as I did for this article, just follow the tutorial from the link provided in the article as well (http://34.65.74.140/net-core-web-development-part3/).
All the best.
Thanks Marinko!
Thanks for the post. I wonder what order should this middleware be put inside startup, since I have other middlewares including Authentication
Hello Richard Luo and thank you for reading this post. If the registration of your other middlewares doesn’t depend on this one than the order is not that important. What I like to do is just to keep the app.UseMVC() expression as the last line of code in the Configure() method.
Thanks the middleware extension did the job. They should especify that example in the microsoft.docs more explicit.
Problem with this method is all exceptions are thrown as 500’s, maybe a little example on how to handle specific types? I’m not sure if it’s a .net core or an angular httpclient issue but 500’s aren’t handled well by my application.
Hello cj. Thank you for reading this post and for your comment as well. About your question. If you take a look at our custom middleware, you will see the public async Task InvokeAsync method. Well inside you see a simple try catch block. So if you want your error to be more specific, all you have to do is to add another specific catch block (FileNotFoundException or SqlException…etc) before the global catch block. Also you can check for the error code of your exception variable.
Thank you for that suggestion.
All the best.