ERROR HANDLING In ASP NET Core | Getting Started With ASP.NET Core Series

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
exception handling or error handling is an important aspect when building applications even though we want our applications to work completely fine there are always going to be unexpected cases and scenarios it's very important that we handle these cases and return back valid messages this is also same when we build api applications in this video let's learn about exception handling in asp.net core applications and the different ways we can handle exceptions i will show you how to set up global error handling and how to set the middleware that's required for that now one of the feedback that i received for the videos was to show more real-world examples so i will try and include some examples from that aspect as well hello everyone welcome back to this channel if you are new here my name is rahul and i make videos around dotnet azure and devops now if this is of interest to you please make sure to hit the subscribe button because that will really help me to grow this channel as usual without much delay let's head straight into my command line to create the new project but wait why do i use command line always to create the project is it because it looks cool or is it the only way to create projects the reason why i use command line to set up these projects is because it's less clicks to set up the project and it's much faster when i'm showing this video usually my intent is just to set up a blank api template or a blank react application but in no ways does it mean that using a command line is the only way you can create a project nor does it mean that if you don't use a command line it's not appropriate or it makes you a lesser or a better off developer now one of my friends asked this so i thought i should be mentioning this in the video so for a change let's use the gui tool to create the new project so let's close this and open up rider which is the ide that i usually use i'll click the new solution and choose the asp.net core web application to create a new solution now in this case i simply need an api template so let's use the type and specify web api now this is having a solution name and a project name so let's call this exception handling and let's name the project as api so there will be a solution with the name exception handling and a project with dot api let's click create i have the project all set up and we have the solution file and the project file in here now to avoid these multiple click steps and naming is why i usually use the command line because it's just faster and comes straight to the point so i have the new project opened up here with the default api template which has the program.cs the startup.cs and also the weather forecast controller now inside this controller it returns back a list of weather data that is hard coded whenever we make a call to the get method to start learning about exception handling let's first create some exceptions so let's take in a string city name for this method so let's collapse the sidebar and use string city name in this case let's assume when the city name is a specific city so let's say city name is equal to sydney in this case we don't have valid weather data so let's throw a new exception so let's say throw new exception and specify the message no weather data for sydney let's run this application and see how this is working so let's press f5 the application is running so i'm hitting the weather forecast endpoint and since there is no city name by default i am getting back a list of weather data now let's make a call with the city name as sydney so let's specify question mark and city name is equal to sydney so let's just pass in the value as text now if you're not sure how the city name is getting bound to the variable you can check out my video on model binding which will be linked here or in the description below to continue with our exception handling you can see that we have got back an exception message which specifies no weather data for sydney we also get a lot more information about the exception so this is the exact position in which the exception was thrown the query that was passed the cookies that was available the headers and the routing information now this is a lot of detailed information in the exception that's getting returned back if we navigate back to our rider project and stop this let's navigate to the startup.cs class here we can see there is one line that is when the environment is development we set up the developer exception page now this is exactly the reason why we see that detailed exception page if we navigate into this method ryder shows the details of this implementation so here we can see we are using a middleware which is the developer exception page middleware this is responsible in showing that detailed information that we just saw in the browser now depending on how we call this endpoint this also formats the response slightly differently to show that let me open up a tool which is called postman which allows us to make api request it's freely available on the internet and i'll make sure to prop a link in the description to download this once postman is opened up you can use the plus button here to make a new request so if i navigate back to the url copy this and come back to postman i can paste in this inside the get request now since this is a get endpoint i can simply make a get request now clicking send it is going to return back the same exception message so we have the system.exception no weather data and all the details in here you can also click between the raw preview and visualize tabs to see the different formats of this particular message now in this particular case it is returned back as a raw text if i navigate into the header section i can specify the accept header to specify that i can also accept a text html since i cannot modify this existing header let's create a new header inside here so let's call this accept and specify i can get back a text html so using this i am specifying that you can return me back an html format now if i click send again it's going to return back the data as html so looking at the pretty formatted or the preview you can see the exact response message that we saw in the browser because by default the browser was sending text html now these are the different formats that's available if we navigate back into rider and navigate into the middleware class we can see this is handled inside here so if we scroll down we can see there is an explicit check for the accept header and then it uses the appropriate response so here inside the display exception method in the developer exception page middleware class we can see it checks for the accept header so depending on it being text or html it formats the response appropriately coming back to startup.cs let's close all other tabs and just have that class the use developer exception page is enabled only when it is a development environment usually it's a good practice to not share detailed exception messages and stack traces when returning back your error messages this is the reason why it's enabled only for the development environment now how does asp.net core know which environment you are running on it uses the properties and launch settings.json file inside here we have an environment asp.net core environment this is available on the two profiles the iis express and the project profile depending on the profile you are running it will pick up the appropriate environment variable so let's go ahead and change the asp.net core environment in here to be production or even test environment so let's call this test for example and in rider you can choose the profiles up on this right side and this is very much the same in any other development environment when you run you will have an option to choose the profile that you need to run against so by default it's using the api profile so let's use that and run this again the application is running as expected so let's pass in the city name sydney this now returns a simple 500 message if you look at the postman request and make this request again this is also going to return just a 500 message if you look at the pretty messages there's no messages now available the error code however is returned as 500 internal server error now this is because the application now is running inside the test environment which we had set up in this file and the developer exception middleware is not enabled for that particular environment now the asp.net core runtime is handling this internal server error of ours because it is throwing an unhandled exception so it returns back a server error so how do we start handling and formatting these exceptions the simplest way to handle these exceptions is to navigate into the weather forecast controller and simply add a try catch inside of this method even though in this case the exception is thrown right inside this method it could also be thrown from a deeper class like a repository or a service class which is in a different file so we can add a try catch around this code block and handle any exceptions that is thrown so let's select the entire method code and wrap this with a try catch block so i'll use a keyboard shortcut which is alt enter and then say surround with and specify try cache so this surrounds the entire method called with a try catch exception method for now i will just catch the exception class which will handle all the exceptions that can be thrown because everything inherits from the base class which is exception in this specific case let's return back a bad request because that is exactly what we are throwing so let's specify a bad request result and specify the message as well so we can use e dot message to get the message now since we have modified the return type we cannot just return an i enumerable because the bad request is not matching type now if you are not familiar or the return types of the api you can check out my video which details out on the different possibilities it will be linked here and in the description below now to fix this the only thing i need to do is to come here and say action result and use that as the return type let's specify the t in here and this compiles perfectly fine let's run this application again and see how this is working now the application is running so let me refresh this request and now you can see there is a message no weather data for sydney if you look at the f12 tab you can also look at the network tab to see the api request so let's make a request again and we see that this is returning a 400 error which is a bad request you can also preview the text and the other response parameters from these tabs coming back to rider if we change this bad request to be not found this is going to return back a different error code so let's run this again to see that in action the application is running and now in this case you can see this is returning a 404 which is not found which might be more appropriate in our current scenario because it says that the city name sydney is not found this works great and we are able to handle these exceptions and return back the messages that we want but fast forward this into your application where you have lots of endpoints and lot of different exceptions that's going to happen and that you want to handle and return appropriate messages you will also want to maintain some kind of a consistency so that it can be all changed at a single point so what we now want is to move this up into a single point where you can handle this consistently to do that let's use the middleware so i had covered about middleware in a previous video and if you want to learn that you can check that out here in this case i'll walk you through on setting up an exception handling middleware inside this application open the sidebar and create a new class let's call this exception handling middleware so let's make sure it is a class and say exception handling middleware now there are different types of creating a custom middleware i will use the one which is using an interface if you're not familiar do make sure to check the other video linked before so let's use the i middleware interface which has a method that needs to be implemented so this takes in an http context and also the next delegate so this is the next middleware inside the pipeline so middleware is nothing but a stack of code blocks which can be run one after the other so wherever in the pipeline we inject this middleware the request delegate will have the next middleware inside this pipeline the context gives us the context on the http request so in here all we need to do is to try and catch the next method called because if there is any exception happening inside the next request i just need to handle that in here so let's add a try catch block and call the method into the next middleware so all we need to do is to say await and call next passing in the context again because this is the exact context that the next method will also require now since this is an await i need to make sure that i need to add an sync now i can use the alt enter in rider and specify that make this to be a sync now once this is called we just need to handle the exception message so earlier in our example we explicitly returned a not found message so in here if you want to do exactly the same let's specify context dot response and start setting the status code for that so let's specify status code is equal to http status code and use the status code from there which is not found now since this expects an integer we can cast this into an end we can also set up the message so let's say context dot response and say write a sync and pass in the exception message in this particular case since this is a right essence we need to make sure to add the await call again so now here the code that we had in the api is centralized inside this location even though i'm hard coding it to be not found which we will fix in a while later so let's navigate to our weather forecast controller and we can remove this try catch block since this will be handled inside the middleware so let's remove all this code that we just wrote and make sure to format this now with the middleware class returned let's go to startup.cs to register the middleware so to start with we need to first inject this particular middleware into the dependency injection controller that is done inside the configure services method where we can use services and specify add methods if you are new to dependency injection in asp.net core i highly recommend the video linked here or in the description below so let's say transient and specify the middleware which is the exception handler middleware that we just created so let's just specify the type and close this brackets now this method is only setting up it into the service collection so let's make sure to scroll down into the configure method and set it up inside the middleware pipeline so right after routing let's specify app dot use middleware and specify the class that we just wrote in this case it is exception handling middleware so this is adding this custom middleware into this asp.net core request pipeline so we have everything set up let's go to the middleware and put a breakpoint inside here to see what exactly is happening let's press f5 the application is running successfully so let's pass in the city name sydney so let's specify city name is equal to sydney since we had the breakpoint inside the exception method it has stopped the execution there now if we look at this exception message we can see this is thrown from no weather data for sydney now in this particular case this is returning the status code not found and setting the response message so if we continue the execution we get back the response which is no weather data for sydney now i have moved all the exception handling code and the formatting of that inside the global middleware handler however in this particular scenario i am handling all exceptions and returning a blanket not found exception this might not be appropriate some of the errors might be validation errors some others might be just purely unhandled server errors in which case you need to return a 500 let's see how we can handle these to make it more real-world kind of an example let's first refactor this code into a different project in an earlier video on layering i had shown how to organize your project structure so let's extract the domain related logic which is about the weather data into a separate project so let's go ahead and create a new project so let's say add new project i just need a class library so i can call this as exception handling dot domain now this is the domain related logic for this particular project so let's click create and this has created a new project since this application is related with weather information let's drag the weather forecast class inside the domain class now i'm going to move that file inside the domain project i'll navigate to the weather forecast and make sure to fix this namespace now since i want to get the weather data which might typically be a weather service class let's rename this class 1 to be a weather service class so let's call this weather service and also rename this to match exactly that now we can also add in an interface if you require so let's say public interface i weather service and add in a method for getting the weather so let's add task of innumerable of weather forecast which is now coming from the domain class and specify the get method along with the string city name now this is the interface let's make sure to include the missing references and implement this inside the weather service class so let's make sure to implement the missing methods and this returns all the dummy data in this particular case so let's navigate to the weather forecast controller copy out this code which is returning the hard coded data so let's copy that inside of this weather service method now let's include the missing references navigate back since we don't need this anymore let's delete that and copy this static list of strings that we have so copying that inside the class we have all this functionality created up here as well now since i don't have any sync and await i can fairly remove this task from here and also from this method implementation and it compiles as expected so let's specify i enumerable of weather forecast let's make sure to implement the generic interface as well so that let's import the missing references now this compiles perfectly fine so let's navigate back into the controller and inject in the weather service so along with the logger let's add in an i weather service implementation as well so let's say i weather service which now comes from the other project so let's make sure to reference that project from the api project as well so let's specify that and say weather service let's add a backing property for this scroll down and call this particular method from the weather service so let's say underscore weather service dot get and pass in the city name the action result requires a concrete type to be returned so let's call to array in this particular case so this works perfectly fine so now everything is compiling so next navigate to the startup.gs make sure to register the iweather service so under the services again we can call dot add transient and specify the i weather service and specify the implementation as weather service class this will make sure that the weather service is dependency injected into the weather forecast controller now if any of this is feeling too fast i highly recommend checking out by layering video in where i show this in detail and explain the details around why i do this in this particular way let's build this to make sure everything is working fine the build is successful so let's run this application to see this in action the application is still working as expected so let's add in the city name this is working exactly as before and we are still handling the exception class now inside the domain we can start adding in custom domain exceptions these are very specific to our domain and not generic as exception or argument exception for example so going back into the domain class we can create a new folder and keep all our exceptions inside one folder so let's click directory and specify this as exception you can choose to manage your exceptions however you prefer let's create a new class inside here and call this as domain exception now this is going to be our base exception class inside this domain so let's create that class and inherit this from the exception class we can add in a constructor if you want and specify a default message always now this depends on how you want to format and respond back with all the exceptions what details you want to include and how you want to track your exception request maybe you want to add in a request id or a tracking id that you can pass back which you can always log and make sure what happened with that particular exception now depending on what your requirements are this will change now in this particular case i can simply pass on this message to the base exception class let's make sure to include the appropriate references for the exceptions so let's say import missing references and pass on this message onto the base class for that let's call base and pass in the message to it now if you don't want anyone to create a class of this type but further divide this into specific domain exceptions we can mark this class as abstract and then create more specific classes for our need now looking back at our example we had a not found exception so let's call this as public class and name this as domain not found exception and also inherit this from the domain exception that we just created so let's have a specific class for not found and make sure to include the constructor as required so let's cross in the message and take in the base and call the message again so this is going to pass in this message inside to the domain exception which is going to further send this to the exception class now like i said before you can add in any more details inside these constructors and also this object that is relevant for your application now if we want to start using this so let's say domain not found we can navigate back to our weather service and instead of throwing the exception we can say domain not found exception make sure to include the appropriate references navigate to our exception handling middleware now it makes more sense to handle this as a domain not found exception and since this is a domain not found exception it makes sense to have the not found as a status code we can also catch a blanket exception method which is getting caught in case it's some other kind of exception so let's say catch exception and again name this as e and use this again in this case since this is an unhandled exception i can set up the response.status code to be 500. i can similarly write await.context.response.writesync and pass in just the message let's run this we still have the breakpoint there let's see how that works now the application is running successfully so let me pass in the city name sydney and now we have the breakpoint hit at domain not found exception so this is going to set this as not found and then respond back with this response now navigating back we see this message no weather data for sydney if you look at the network tab we can see this is still the 404 which is not found in our case now if we navigate back into our source code and if this was throwing a different exception so let's go into the weather service and throw in a different exception so we can always go inside this exception class and create a new class so let's copy this and say this is now going to be a validation exception so instead of domain not found we create a new class which is validation exception now this could have validation errors from your class as you expect now if we come back and throw a validation exception here since we don't have this handled inside our global controller it's going to return a 500 in this particular case so let's run this to make sure that's exactly what happens the application is running let's refresh this call and this now returns a 500 exception this is because inside the global exception handling middleware it is not matching the domain not found exception so then it falls back to the exception class and sets that as 500. now in your case if you want to respond back with a bad request in case of validation errors you can simply come here add a cache and specify the validation exception and specify e to capture that as well now here we'll simply return back the same but make the status code as bad request so if you are unsure of the status quo you can use http status code and specify bad request so in this case make sure to cast this to an integer now going back to this validation exception class i would prefix this with domain so that it is consistent let's make sure to apply this naming refactoring everywhere so going back we have this as domain validation exception as well let's stop and rerun this application again to see how this works now the application is running so let's refresh this again and this time i get back a 400 which is a bad request this is because the api is throwing a domain validation exception and the global middleware is explicitly handling for that i hope this helps you to understand more about exception handling and the different ways of handling exceptions inside your asp.net core applications we learned about how you can handle this at the controller level and why that is not a great idea to do and also moving this up inside a global error handler we learned how in a real life application you could have custom domain classes for exceptions and use that to handle inside your middleware if you like this video please make sure to hit the like button if you have any feedback or questions please make sure to drop in in the comments below and don't forget to hit the subscribe button because that really helps me to grow this youtube channel and i'm almost nearing the 5k mark as i record this particular video and i'm really excited for that thank you and see you soon
Info
Channel: Rahul Nath
Views: 9,113
Rating: undefined out of 5
Keywords: error handling in asp.net, error handling in asp.net core, exception handling in asp.net mvc, exception handling in asp.net core web api, global error handling in asp.net core web api, global error handling middleware, asp.net core global exception handler, asp.net core web api error handling, global exception handling in asp.net core web api, domain exceptions in asp.net core, how to handle exceptions in asp.net core, how to handle exceptions in asp.net
Id: Cy53ENszjWo
Channel Id: undefined
Length: 27min 34sec (1654 seconds)
Published: Tue Jun 01 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.