Exception Handling for ASP.NET Core Web API

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
error handling is a critical aspect of all web applications and if not on right it could lead into missing many critical errors and tons of repeated code that can be hard to maintain so today I'm going to show you the right way to do error handling in your infinite core web apis let's start so here I have a small web API that deals with some fictional game data and that has the typical endpoints for cloud operations so as you can see we do have an endpoint for retrieving all of the games we have an end point 43 and one specific game via its ID and then we have endpoints for creating a game updating a game and also for deleting a game right and so the game data itself leaves right now in this in-mem game repository class that I'm going to go into right now this is a standard implementation of the repository pattern but this is doing everything in memory just to keep things simple for now and so you can see we have just three games and then there's a typical implementation of the repository pattern right there right so if we go ahead and run the server right now so I'm going to use run Dot and run and then we go into my hello sections.hdp file where I have prepared a couple of requests I'm going to click on send request here and then on the right side you can see that we get the data for the for the three games that are available in the repository if I hit the other endpoint you're going to see that we received the data for just one game so that's all good but we want to see is what happens when exceptions start trickling into the into the application right so I'm going to use stop my server for now I'll close this and I'll close that and so what we're going to do is just to temporarily modify our repository so that these two operations get lacing and get async are actually drawing exceptions right you have to simulate a what could happen so we're going to just say here throw new invalid operation exception and then this is going to just say the database connection is closed right and so since we don't have anything to wait on for now I'm going to just remove the async word over there and then I'm going to just copy and paste that same line on the other method and remove the async keyword right there right so since we are this these two methods are drawing now we should have interceptions anytime we invoke either games or gain games by ID so I'm going to just start my server once again right start server and then I'll go into hillaryceptions.http with collapses and then send request and then as you can see on the right side this time we do get a 500 internal server which is kind of the expected exception when unexpected things happens in your web API it really means that something really bad happened on the server side it was not caused by the request but there's just something going on with the backend right which is expected at this point and you can see over here that we have all of the details about the exception and that it will cost right now if we also if you also we open the the terminal over here we want to see that we do have a bunch of details about the exception uh if we yeah if we can see there better we're going to see that this data here is actually coming from what is called or is known as the developer exception page middleware let me actually highlight that better over the developer reception page middleware which is the middleware that kicks in any time that you get this type of exception in a development environment right so right now I am just running this code in my box so by default this is going to be the development environment and so that that that's the reason why I'm going to be seeing all this data now I don't want to be seeing this much details of course if this was deployed in production right and so what would we see if we were in a production environment right so this developer perception a middleware uh gets injected by default when you're in development but if we just go very quickly into our launcher said is that Json file over here which allows us to provide some details for the Google environment where we're running the application in this case we could modify the ASP net core environment variable here which controls this development or production environment is setting so I'm going to just switch these from development into production so that's what we do what you will get when you deploy this application into production so with that change in place let's see what happens now if I just go ahead and let me just collapse that and then I'm going to run the application once again and I'm going to go again into here click Send request and let's see on the right side we no longer have any of those details right so all we have is a standard 500 that is available no more details about what happened and then if we see the Lots here we still see the logs that we were seeing before right so which is which is expected so these logs are now coming directly from the web server from from Castrol as you can see the errors are coming directly from there okay and so there are a couple of issues with this here because of course we usually want to provide a better a better login experience but we also usually want to add some additional metadata to these errors so that we later later on we can troubleshoot the issue in a better way um that for the login side but also on the response side we don't want to just return at this kind of an empty page into the client right we don't want to provide a little bit of information of that can help also troubleshoot from the client side so what can we do about this so let me see let's let's first let's make sure we stop the server for now and so let's close this and close that and let's go back into problem.cs so there are a bunch of ways to deal with these so the first the first way that I'm going to show you here is the well the typical try catch right that you will that you would start with typically for this a purpose so what I'm going to do is just say here so I'll add this try catch a statement and then I'm going to just move a our code into the try block over there and then what we want to do is say Okay so we're going to be catching any exception that goes over here and then we want to do a couple of things the first thing is going to be that usually we want to lock this somewhere right and so well to do that what you can do is just say up dot logger right and then this is going to be logged as log error and then here's where we can we can decide how exactly we want to lock this in our case we want to say okay we're going to be sending over the original exception into the log and I'm going to send it to the next line yeah right there and then we're going to provide kind of a friendlier message to the client well not it can actually to the to the lock line that we're going to store in our application so this is going to be could not process ah request on machine right and here's what we're going to kick in with the structured logging so we're going to just say this is going to show us machine and then we're also going to attach a trace ID so that it is easier to later correlate this error with any of with the other requests that that came into the application so that Trace ID is going to come from address a ID element there that we're going to be injecting now so now we're going to be saying as parameters to this log line we're going to say for the machine we're just going to be using environment dot machine name and for the trace ID regulator we're going to get that from activity activity and then to use that we're going to do control dot here and let's just choose system.diagnostics right so this is going to be activity that current and then this could be new so let's use the equation mark there and then the ID right so with this we have a nicer a little bit of a nicer error message in our logs that clearly says that we could not process a request right so that deals with the issue of recording additional details in our logs but we also want to return something meaningful to the client like I said so let me scroll down a little bit more and then what we want to do is to prepare the appropriate response a back to send back to decline so for that what we can do is just say return results Dot and then here of course you could do just a status code and just say okay so I'll just return a 500 and then I'm going to be done with that but that's not really a great experience for the client so we want to do instead is just say results dot problem right so this is going to prepare a problem problem details a payload which is a standard way to return errors into your clients right which every client should be able to understand very well so for that one we're going to start adding a few elements here so first thing is going to be our title so it's going to be the title like this and so this for this one we do one need to make it kind of friendly so we're going to just say well we made a mistake but we are working on it all right so that's going to be the title of the error and then we want to provide a status code of course so this is going to be status codes dot status 500 internal server because this is still a 500 error right and um finally we also want to provide that same Trace ID as part of this result right and to do that we can use the extensions extensions element over here which is kind of a dictionary of elements where you could put a bunch of things so I'm going to just say new dictionary okay and this is going to be of type string and object uh notable object all right and so in this dictionary let me scroll down a bit over there we want to include just one one element for now is going to be Trace ID all right and this 3cd is the same value that we got over there so let me just copy the activity ID from there and then this is not a column this is a comma of course and then I'll just close this right and so one last thing that we have to do here is uh to make sure that the return type is standard across both the happy pad and the error pad right so right now we're returning a results object on the exception side but on the happy pad we're just returning the list of games so we have to modify this so that we also returned something that is a doctor result a object here so we're going to say results that okay for the case where we return the actual data like that and let's make sure that the weight operator actually goes uh when we invoke the and get all async method like that okay so with this code here what you can see is that anytime this get all async method fails we're going to catch exception we're going to log it in a nice way into our into our application and then we're going to be returning this nice payload back into the client including a nice Trace id2 to keep track of what what's going on there so let's see how that goes okay so I'm just going to restart my server over here so I'll do the net run all right and then I'll go back into hello sections.hdp I'll click on send request and as you can see let's look first at the client side so in the client side you can see that we still get the 500 internal server error but now we are getting a very nice payload of error details over here as you can see so this includes a the standard type of error it includes the the title it includes the status and it even includes a trace ID which like I said is going to be super useful for for keeping track or correlate this this error with any errors that we have in the database so that the application developers can investigate what's going on and then that's a very nice response for the client and also as you can see on the server side we do have the deception now being logged directly by your application and it does have a the expected you know the expected payload over there could not process a request on machine here's my my desktop and then here's the trace ID right and besides we also have the exception the status of exception right there we can see the actual exception over here so that is a very nice experience a very nice way to handle your receptions for this specific endpoint okay and so yeah we stopped the server now if we go back into program CS uh we'll say okay so that's good so we have figured the issue or handle the exception properly for this one endpoint but now we have to deal with the same problem for every other endpoint right so now we have a to do the same being for market for my post my put my delete all of these could be could be failing in this in a similar way right and so we have to do something to properly centralize this this code here so that it only has to be reading in one place and we make sure that we always catch exceptions and report them properly in the right way and so how to do that so here we can we can take advantage of one thing called middleware in ESPN network core and so the middleware is nothing more than a piece of code that can execute between the moment when the request arrives into the web server and the moment when that request actually arrives into your application right so between between the web server receiving the request and your application getting the the request itself um there can be and there is already some code executing as part of the standard aspin core Pipeline and you can add your own middleware at that point to deal with the whatever happens before or after the request is handled by your code and so let's see how we can Implement that middleware too to kind of centralize this error handling so what we're going to do is just go a little bit up here and then we're going to be using this app object here which is a style of type web application and on this one we can actually just use a very simple method to add kind of a Lambda function to act as our middleware so what we can do is just say update use okay and here we're going to introduce our async uh context and then next metadary here and then this is going to be just like that so on this Lambda context here represents the HTTP context which has all the information about the request that is coming in into the into your application and next represents the next function that's going to execute after this middleware which is typically is going to be just your function right like in this case your get function over here okay or it could be another middleware too right so by putting code here we can do the following thing so we can just add our try catch block here so let's do try and then what we want to do in the try block is to actually just let the next request go in right so we want to say oh wait next context right we just let the request go in and if there is an exception right so we have this this catalog over here that's when we can go ahead and do something about about that and so in this case of course what we want to do is just grab that code that we have down here right so copy that code I'll just paste it in this log over here and then just before this is going to take care of logging the error into the into the logger right which could be configured to go anywhere really and you want and next we want to just return the actual problem back to the client to do that we don't want to do return anymore what we want to do is just say await resolves the problem and then after we have defined the problem that we want to that we want to prepare what we want to do is just instead of doing this semicolon we want to do dot execute async and then we send the context right there just like that right so as you can see the code is pretty much the same it's just that now it is running as part of an actual middleware that should execute anytime any request comes into the application so now what we can do is actually get rid of all of this a exception handling code that we have in games so we're going to get rid of all of this stuff right so going back actually to the original implementation as you can see super simple and then let's see what happens when an error trickles into the application so I'm going to just dot net run and then go back into hell receptions I'll click on send request and once again I'm getting the exact same experience from the client side right with the type title status and Trace ID and if we see there is the the console over here we're going to see that we have again we have all the details about the reception uh like right here and we have the stack Trace we have everything that we have before so exactly the same experience but now it is centralized so this means that if now I try the exact same thing with my other endpoint my get by by ID endpoint click Send request and we are getting exactly the same experience without having to write any additional error handling code for that second entry right so I'm getting the exact same errors and the side same thing in my logs down here now that is that is all good uh but if we go back into our program CS let me close this to the server close that and let's go back into currencies uh the only problem with this right now is that of course uh it's just too much code for a problem that's yes right so having a Lambda function just like that over here is not looking great so one way that you can do improve this is by writing what is known as an as an actual a proper middleware class right so let me show you how that goes because this is the most typical approach I have which I have seen so far in other teams for error hunting so let me go to my Explorer we're going to do is just um create a brand new a class over here let's call it Global exception middleware all right and so yeah let's just collapse this and so let's convert this into a Fast Company space and then in order to make this actually act as a middleware let me remove that what you have to do is the following uh the first thing is that we need to capture that next delegate that we were dealing with before uh but as part of the construction of this class so what I'm going to do is just declare my private with only request delegate let's call it next user before and then um the other thing that we're going to need here is just cover the logger right we need a logar instance in order to be able to log into the whatever we want to lock this stuff so let's also add that read-only High logger okay right so this is going to be of type Global exception a middleware in this case blogger and then we want to add a both of these into the into a Constructor so I'm just going to do genetic construction Constructor with a logger let's also add the next element okay so both of both them are received now into our Constructor okay just like that and then by convention what you're expected to do is to implement one very specific function into this class so that it can actually act as a middleware so that functional let me scroll down here it has to have this signature so public async task invoke async and then you're going to receive here the HTTP context object HTTP contact context all right and here we're going to have the actual code for invoking the next delegate and also for handling any possible errors notice that there is nothing here telling us that this is exact signature that we have to implement for this to work right so if this is a standard class it is not implementing in any interface or it's not inheriting from any class so there's nothing here this is online convention right so you have to be very careful about this now what we want to have here is pretty much said that's that code that we had before right so let's copy let's copy this code okay so I'm going to copy this from program over here and in fact let's copy add entire thing including try catcher right so that's that's really what we want it to do here so let me just copy the entire thing with the try catch just like that all right and so yeah so in this case we we decided to name this HTTP context so let's just name it like that this should be context not con context yeah just like that and then we're going to just say here since we have our local variable over here we can now say we can now say logger.log error and then we will do a control dot here to import system Diagnostics just like that and then we have resulted problem execute async and then finally we want to do HTTP context just like that all right and so that's pretty much what you need to do to implement your Global exception middle a 100 middleware right and so as you see it's the same idea now you just import the next delegate and if something happens you lock the error and then you return the proper a payload back to the client now how do you use this middleware in your code where what you want to do is just go back into param CS in this case and then instead of doing a all of this stuff here what you want to do is just say I'm going to delete this okay you just want to say up Dot use middleware okay and then we I'm going to put novel exception middleware just like that and I'll do control dot here so that we can use the hello section snake space right there okay so now we just did one line we are able to configure the global exception builder for our application so and so this time to to be able to see actually things going in action I'm going to just put a breakpoint over here let me put a breakpoint uh right here and then let me put a breakpoint right here so we can see what happens and I'm going to do F5 in my vs code project here okay so the bug operation starts and now I'm going to send a request to games and as you can see we are going about we're about to invoke our code right we're going to allowing both the code and actually can put a breakpoint in programs let me see if I can put a breakpoint over here we're going to invoke and the get all async method okay and so I'm going to hit play and as you can see if we hit the endpoint just after going to the middleware we now hit this this endpoint and then if I keep going of course the exception happens we hit lock error now inside there we are inside the reception middleware and then I'll just step over to the next one we're going to report the problem and then I'll step over okay if I if if I hit play now we're going to get a again on the right side we can see that we get the same thing right uh the same kind of response and then we should have kind of the same thing in the logs a down here in the console right so same idea but this time and let me just stop debugger now this time uh it is nicely centralized into this Global exception wheel work class and our program CS is much much uh cleaner this time okay now like I said there are still some issues with this uh because uh if you see uh you could make mistakes hey while implementing this class right so if you just don't Implement exactly this this name here it is not going to work and then another issue is that you could um I mean there could be some other things happening that you don't have control over here right for instance it could be that by the time that your handler comes into play and let me collapse this for a moment uh the response could have already been started being sent back into the client at which point you cannot really uh prepare a different response by doing this listing that we're doing over here right so in that case it's just not going to work so it's a little bit error prone right there same certain scenarios and certainly you can you can find a proper implementation of these somewhere in the web to try to make sure that this works properly all the time and like I said this is a typical implementation they have seen in many places uh but luckily these days there's even a better way to do this so you don't have to fall into those kind of a corner cases and have a very solid implement so let me show you a how to do that so if we go back and let me go back to my explore review what we're going to do is just to create now an extension methods class right so let's do new C sharp class this is going to be called error handling extensions right so this is all we're going to put here is just one method that's going to be an extension now this class has to be static and let's make it so that this is a fast Opening space and it's going to remove that and then what we want to do here is to implement just one method that's going to be called public static void and you can call it any way you want really it doesn't matter I'm just going to call it configure exception Handler right and this is going to extend the I application Builder a class let's call it app and we're going to do here is just use app to invoke the wrong method so that we can invoke or execute another delegate which is going to have the signature of async context Arrow function let's define the method body over there okay and then here is what we're going to actually Implement our Logic for handling the exception directly right so we don't have to be dealing with a before or after this is when a session happens this is what we're going to do so here we have to do a couple of things before actually handing the code in this case we have to figure out where are we going to get a things like the logger or the exception from in this case so the logger we can get actually from the registered services from the service container so to do that what we can do is just say part logger equals context requests services and then get required service and this is going to be I logger of in this case class program like that okay so that's going to retrieve the instance of the logger that has been registered for a for program right there okay and then the next thing is going to be the exception so to get the exception what you want to do is to do the following so exception details it's going to come from Context that features that you get and then I exception Handler feature all right and so let's do control dot here so 12 can import Microsoft 8 Minute core the Diagnostics just like that and then we can do bar exception uh equals exception details and this could be known so we'll do a question mark that error just like that so now we have a logger and then we have an exception object available for our code and with that we are pretty much ready to bring in that code that we had in the global reception middleware we can now go ahead and do the following so I'm just going to copy all of this code from colonization middleware into our brand new implementation paste that here okay and then it's going to be very similar just that the variable for reception is name exception now for activity we'll do a control Dot and import system.diagnostics all right and the variable for the context is just going to be named context in this case and then uh yeah that's pretty much it that's what you have to do to implement your um kind of your your extension for error hunting as you can see it is kind of the same body but it's much smaller because we don't have to implement an entire class here and there's nothing being done by convention here this is an actual type way of defining your extension now where do we attach this extension right so now we want to go back into program CS and here where we before we were calling use middleware what we're going to do is just say up dot use exception Handler okay and then here we're going to be receiving an exception Handler app object here uh where we in which we are going to invoke as you can see exception Hunter app we're going to invoke configure exception Hunter just like that okay so this userception Handler method is the one that's going to be in charge of taking care of invoking our Handler anytime hey any session happens anywhere in the application all right and so if we try this out now so I'll do Ctrl J let's go back into our terminal and then I'll just delete this and I'll do and run okay that runs application I'll collapse this and then we're going to go back close a few things uh over here send requests and as you can see same experience same error right and same thing in the console down here we're getting exactly the same thing but now we are doing it the right way right uh now the only thing here that still is is a we want to kind of improve here and this is kind of a decision of the implementers of this built-in exception Handler is the fact that that Handler the one that comes with asynet core is already login deception for for you right so you can see this first this first section here this entire first section is not our login this is the login directly from the exception Handler mirror that comes with infinite core our our login comes down here right here where it says firm zero could not process a request something something and then comes the exception here okay and so I mean this could be fine uh but I don't like to have this section duplicated twice like that right so what I like to do for this is just to somehow mute the other exception that's been sent a by default by the exception Handler and just keep my own one right so how can we do that how can we mute that and to clean up things a bit so what you can do is let me see yeah we stop this let's go to app settings.json over here represent.json what you want to do is just to add one section here and I'm going to paste this just to note uh do not mistype what you want to do is just uh do the following so you are going to put a section under logging log level you're going to add Microsoft infinite core Diagnostics exception hardware and you want to put none by doing that you are pretty much turning off all of the logs or events that are coming directly from that Handler so that your one can take over completely so if now we try this once again then it run and so we hit hit and hit the end point you can see that now we only have one error in logs so we have info info info and then just our fail our own fail and there's no more errors over here there you go Club reception handling done the right way and well I hope that was useful and if you found this interesting please check out other videos on my channel where I cover many other topics essential for professional.net Developers so for now thanks for watching and I'll see you next time
Info
Channel: Julio Casal
Views: 5,582
Rating: undefined out of 5
Keywords:
Id: nycII-Cec9I
Channel Id: undefined
Length: 29min 53sec (1793 seconds)
Published: Tue Mar 28 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.