The New Global Error Handling in ASP.NET Core 8

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
proper error handling is critical for net applications and in this video I'm going to discuss how I approach application errors that I know and don't know how to handle we're going to talk about the result pattern middleware and some interesting features coming in net 8 I'm going to start my discussion about error handling from a minimal API endpoint this particular endpoint accepts a create user command sends it using mediator and the command Handler returns a result object back back based on the result object I know if this command was handled successfully or not if it was handled successfully I'm going to return a 200 okay result otherwise I'm going to return a bad request so how does the result pattern work well let's check out the create user command Handler inside of the handle method I have a couple of validation steps like checking if this is a valid email if the email is unique or not in the database and I could also have some guard clauses running inside of the constructors or the methods that I'm calling regardless we're going to end up returning a result object from this handle method which represents a success or a failure of this command I'm using error objects to represent the failure of a particular command and this allows me to document my application errors such as in this example in the user errors class and you can see I have a few not found errors and one error when the email is not unique and what is common between all of these errors is that they are predictable I already know that a user could be null either when I'm looking for the user by the identifier or the email or I'm expecting an email to not be unique in the database and these are the situations where I'm going to use the error along with the result object to represent a success or a failure of an operation because I know what the expected outcome is when I don't know what the expected outcome is it's appropriate to use an exception if I try to call my endpoint with an invalid command I'm going to get back a bad request however there's a better solution for this and you can standardize your API responses in case of a failure using the problem details object this is an RFC standard for documenting failures in HTTP apis so let's try to return a problem details response I'm going to start by setting this status code to status codes 400 bad request so I'm still returning a bad request response except I'm returning a problem details object the next thing I'm going to set will be the title I'll just say that this is a bad request you can optionally return a type this should be a URI pointing somewhere where the API consumer can get more information about this failure for our example I'm just going to point to the 400 b request in the RFC documentation and one more thing you can return is an extensions object to extend the problem details with arbitrary values for example I can create a new dictionary of a string and an object inside I'm going to create a dictionary key called errors and it's going to contain an array of error instances and for the specific error instance I'm going to pass the result error so let's check out how this is working here's the request that I'm going to send to my API and I'm going to leave the email empty on purpose if I send this request I'm going to get back a 400 b request response and you can see the problem details response that I return from my endpoint so the status code is 400 and inside of the errors array which I added using problem details extensions you can see the specific error along with the code and the description for this particular issue so if I set some value for the email I'm going to get a different type of error this time that this is an invalid format and if I actually set something that could be a valid email email and send this I'm going to get an internal server error because I have an unhandled exception I mentioned that I use the result pattern for errors that I know how to handle and something else for exceptions that I don't know how to handle so before we dive into solving this exception and handling it globally let's slightly improve our API I'm going to create an extension method on the result class to return a problem details response so let's create a new folder I'll call it extensions and inside of it I'm going to create a new class that I will call result extensions so let's make this public and static I'll add a static method inside that's going to return an i result which is what my minimal API endpoints return and let's call it two problem details this will be an extension method on the result object and then for the implementation I'm going to use what I already wrote inside of my endpoint so this part here and I'm going to say return and I'll call results problem and use my result object to initialize the problem details I also need to add a guard Clause to check if this is a success result and in that case I want to throw an exception so let's say I throw a new invalid operation exception and I'll say can't convert success result to problem and then if I want to use my extension method I can just say result to problem details and I can read use this method in my API endpoints and now let's see how I'm going to handle the internal server error so my preferred approach is using a global exception handling middleware so I'll create a middleware folder and let's create a new class inside that I will call the exception handling middleware I'm going to use the convention based approach to Define my middleware so I need to define a private read only field that's going to represent my request delegate and let's call it next I'm going going to inject this from The Constructor and I'm also going to inject an instance of an ilogger of the exception handling middleware type let's call this the logger and I'm also going to initialize this from The Constructor I'm going to align these vertically and then the next thing that I need is a public method called invoke async and it needs to accept an argument which will be the HTTP context for the current HTTP request and then inside of this method I can Implement my Global exception Handler so how I will do this is write a try catch statement catch the exception let's give it the full name and in the try statement I'm just going to await the request delegate and give it the current HTTP context and what I'm interested in is writing out the catch statement to return a problem details response so let's first write our error log so let's log the error I'm going to log the exception instance I'll say exception occurred I'll use the except ction message as a structured log entry and I'll pass it here as an argument then I'm going to create my problem details instance by calling the problem details Constructor and let's set the values that I want to set on this instance so I'll set the status code to status codes 500 internal server error because I don't know how to handle this exception so I'm just going to return a problem details response for the title I'm going to say server error and then for the type I'm going to pass in a URI that's going to point to the 500 internal server error in the appropriate RFC then what I'm going to do is write the problem details into my HTTP response I'm also going to set the status code on the response explicitly I'll also use the status codes again to grab the 500 internal server error and now I can say await context response right as Json async and I'm going to write the problem details so this middleware is going to catch any unhandled exception log the exception details and it's going to return a standardize problem details response from my API I'm not leaking any information about the exception from my API and this is important for security reasons but you still want a way to monitor your exceptions and for local development you could use something like SE which you can run in a Docker container now the next thing that I need to make this middleware work is to configure it with the request pipeline so I'm going to say app use middleware and then I'll specify my exception handling middleware you could also create an extension method from this I'm just making it explicit so let's see how this works I'm going to send the same request from Postman to my API and I placed a breakpoint inside of the exception handling mware so now I'm going to send my API request and we hit the breakpoint inside of the invoke async method in our middleware so now I'm going to press continue we're going to catch the application exception that's thrown somewhere down the call chain and we're going to log this exception create a problem details and write it to the response so if I hit continue we're going to get back a problem details response in Postman the exception was locked and we can monitor it somehow and we're not leaking any sensitive information from the API so this takes care of our Global error handling using middleware but there's a new approach in net 8 so net 8 introduces the I exception Handler interface for handling unhandled exceptions so I'm going to create an infrastructure folder here and I'm going to add a new class inside which I will call the global exception Handler I'm going to implement the I exception Handler interface from asp.net core and you'll see that it has one method which is the try handle acing method it gives me access to the HTTP context the exception that I'm supposed to handle and the cancellation token so for the handling logic I'm going to reuse what I already have in my middleware but first i'm going to inject a logger so I can log the exception and I'm also going to use a typed logger with the global exception Handler so let's inject this from The Constructor and I'm going to reuse the exception handling logic from here where we already have access to the exception because I have access to the exception in the try handle acing method so let me paste this and I need to adjust this so that it compiles so I need to use the HTTP context directly I can also pass the cancellation token to this method and I need to return true if I'm able to handle this exception successfully so you can see that this follows the same idea I'm logging the exception using a structured log I create the problem details and write it to the HTTP response now how do I actually make this work with my net 8 API and mind you you need to be running net 8 which I'm doing here to be able to access the I exception Handler interface and in general use this new feature so I need to go to my program file and I need to make a few adjustments I need to say Builder Services add exception Handler and I'll specify my Global exception Handler here this is going to register my exception Handler with dependency injection then I'm going to add one more call here and I'll say add problem details and I add this to simplify how I registered the exception Handler middleware so I'll say use exception Handler and if I didn't add the problem detail Services I would need to at least try to configure the exception Handler options but because I added the services I can omit this and just say use exception Handler now I'm going to leave the global exception Handler in place to show you what's going to happen when I have both this one and the I exception Handler in place if I send this same request to my API I'm first going to hit the breakpoint inside of my invoke async method so I'm going to hit continue and I already know that I'm going to get an exception only this time we're going to first land in the try handle async method of our Global exception Handler so let's go ahead and log the exception create a problem details and I'm going to write this value into my API response and if I hit continue you'll see that I'm going to get the response back in Postman and completely skipped the breakpoint in my exception handling middleware the reason that my Global exception Handler managed to handle this exception first is because I added the exception Handler middleware after my custom exception Handler and it was able to handle the exception and return a response before my middleware so I'm going to Omit this registration here and just use the built-in exception Handler and rely on my Global exception Handler implementation of the I exception Handler interface now now you can chain multiple implementations of this interface for the various kinds of exceptions that you want to handle in your API and you just have to return true for one of them before the exception bubbles up and you get an internal server error or you could Implement a global exception Handler like I did here that just catches any unhandled exception and returns a problem details response I think the new I exception Handler interface is a great addition to net 8 but ideally I'm going to get rid of exceptions from my code completely and you can find out how I do this in this video which you can watch next make sure to smash the like And subscribe buttons and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 12,350
Rating: undefined out of 5
Keywords: error handling, global error handling, global error handling in asp.net core web api, global error handling in asp.net core, global error handling in mvc, global error handling in asp.net mvc, global error handler, global exception handler, global exception handling, error handling api, error handling middleware, global error handler middleware, global error handling middleware, global exception handler c#, error handling api c#, web api error handling c#, iexceptionhandler
Id: uOEDM0c9BNI
Channel Id: undefined
Length: 13min 40sec (820 seconds)
Published: Fri Dec 08 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.