So in our previous video we have integrated
our login and register component with WebAPI. We have seen how to call WebAPI methods and
how to trap errors at the client side. We understood the basic concept and it is
working quite well. But definitely this is not the best approach. If you have only one or two components, then
it is fine to trap errors like this in components. But if you have 1000s of components, this
is really bad advice to follow this approach. Because we need to repeat this code in all
the components. Although we can write this error trapping
logic in our service as well. But we may have 100s of different services
as well in bigger applications. Then also we need to repeat this error trapping
logic in all of our services. This is a very serious anti pattern and we
should never repeat our code. Also if something needs to change with the
way we handle errors, for example suppose later we decide to log all the HTTP errors
in a database, we have to update each and every component or service, if we follow this
approach. Angular provider http interceptor to intercept
all http requests and responses to transform or handle the errors, before passing further
in the chain. We can use http interceptor to create a global
error handling middleware just like we had created in our WebAPI, to solve this problem. So in this video we are going to add an interceptor
in our angular application and also we will make some changes in our WebAPI as well, to
return errors in a more consistent way throughout the different controllers. So without further ado let’s implement an
interceptor in our angular app to handle the error globally next. So we will add an interceptor as service,
we can also use angular CLI to generate an interceptor using ng generate interceptor
and provide the name of interceptor. But I am adding this first interceptor manually,
so that you can understand the concept deeply. Let’s export the class as we need to use
this in other modules, name it HttpErrorInterceptorService and this class must implement HttpInterceptor. And any class that is implementing HttpInterceptor
must have a method named intercept, that should have two arguments. First one should be the HttpRequest that we
can intercept here and transform it as per our requirement before sending it further
in the chain. Let’s name it request. And the type of this argument is HttpRequest
and it is a generic type. We need to pass the type of data here that
should be carried by this request. But we want to make it generic and we do not
know what kind of data a request will get here, so I am going to use ‘any; here or
we can also use ‘unknown’ as well here. And the 2nd argument of this method typically
is a function or better we can say it is an object of type HttpHandler, that provides
us a handle to pass the transformed request to the next interceptor in the chain. We typically return an observable of event
stream here in this method, using next.handle and pass the request here. This is very important, if you do not call
this next handle, you will break your application, as your request will not pass further in the
flow of application. We can perform some operations on this request
using some rxjs operators on this request, before sending the same to the next interceptor. We will look at that shortly. But before that, let’s just write something
before this return statement in the console. Let’s say “HTTP Request started”
So this is the typical setup of an interceptor. And finally in order to tell about our interceptor,
as usual we will have to go to the appmodule. Scroll down to the providers array. And here we need to add a javascript object. We need to provide 3 properties here, the
first one is “provide” and here we need to use HTTP_INTERCEPTORS, all in capital letters. This is the token name that angular will look
in all the classes and will consider those classes as Interceptor and run those classes
in the chain. And the second property is useClass. And here we need to tell the name of our interceptor
class. And the 3rd property is multi. And it must be set to true in the case of
HTTP_INTERCEPTORS because it is a multiprovider token and if we will not set this property
true, it will not work. Required modules automatically be imported
on the top, but in case if these are not added automatically by VS code. Then you will have to import these classes
manually at the top like this from angular/common/http. So our interceptor is now ready, let’s run
our application and see what we get in the console. So if you refresh this page, we are already
calling http get method to load this home page data from JSON file using HTTPGet method. So our interceptor should be called. Yup, it's called as we get this line here
“HTTP Request Started” that we put in our interceptor. And if we try to login, let’s just use some
invalid username and password. Yup, this time as well our interceptor is
called. So our setup is working fine. Next we will see how to move all of our error
handling logic in the interceptor and remove it from the component. We can use the pipe operator on this observable,
that will take this observable as input and inside this pipe we can use the different
RxJS functions. And to catch the error we will use, catchError,
this method has one argument to get error. And type of this error is HttpErrorResponse. And in this method we can log the error. Let’s copy all the code from our component
and paste it here. Remove this line as it was wrongly imported. And this alertify is a service and in order
to use it here, we will have to add the constructor in this class. And inject alertifyService here in constructor. And finally it is important to return an error
from this function, let’s throwError and return error.error. You can see all of these modules are automatically
getting imported here at the top of this class. Just to remind you, sometimes it does not
work in vscode and you have to manually import a few modules here. Also you need to add this injectable decorator
at the top of this class and it should be provided in root. Otherwise this alertify notification will
not work. So now we can remove this error handling code
from our component class. Let’s try to login, first provide some invalid
user name, so that we can see if our interceptor is catching our error or not. Perfect, we are getting this alert from our
interceptor. And if we provide the correct user ID and
password. Yup we are successfully logged in, that means
our interceptor is correctly passing requests further in the chain. Now we can remove this error handling code
from the register component as well, because our interceptor will work globally for all
http requests. We do not need to put this error handling
logic now in any component or service. Let’s try to add a user that already exists,
and it should throw an error from the interceptor. Yup, we are getting an error notification
from the interceptor. So happy path or we can say positive test
cases are working fine. Let’s test it for few negative test cases
as well. Let’s see what will happen if our API server
is down. Trying to login, doesn't matter if we use
invalid or valid users, as our API server is down at the moment. Definitely our interceptor has catched this
error, but we did get any clue in this notification of what wrong has happened here. And if we look at the console, you can see
this time we get this JSON object in error, it is not directly exposing the error as we
were getting previously. And if we look at the other nodes, you can
see we get status as zero here. So whenever we do not get any response from
API or API server is down. We always get zero here in the status node. So we can use this status code in our interceptor
to display “Unknown error” or something else you would like to display to the user
on the base of this status code. Let’s create a separate method to set Error,
instead of directly putting if else conditions here in catch error block. Accept the error response here as an argument,
that we will pass from the catch error block. This method will return a string type. Let’s define errorMessage and set the default
value as “Unknown error occurred”. There are two kinds of errors that usually
occur in angular apps. One is client side errors, that we usually
throw from the frontend. We can identify client side errors by checking
the instance type using instanceof type, and client side error instance type is ErrorEvent. So we can handle “client side error” in
this block. And set errorMessage here. We get the error message in error.message
node for client side errors. All other types of errors are server side
errors. Set the error message - error.error. And return this errorMessage here. At present we are throwing all known errors
in this format error.error. But as you have seen if our server is down. This is a kind of unknown error and we got
a json object in that node, you have seen it results in throwing a blank notification
to the user. And we know if the server is up, error.status
will not be zero. So if error.status is not zero, we will set
the error message using error.error. And as we already have set the default value
here and if all of these conditions are failed, this method will return the default value
as “Unknown error occurred” Let’s use this method here in the catch
block. Define a constant, name it errorMessage and
set its value using this.seterror and pass error in the argument. And use the constant here in both the places. So if now we will try to login, perfect, we
are getting this unknown error notification. So it’s working fine for both the test cases
like for the “handled” error that is returned by our API and the 2nd test case is also handled
to return an unknown error if the server is down. The handled error that is being returned by
our controller is defined here. So when the user was invalid we are throwing
this error in error response. Let’s think what will happen, if in case
our API is running, but our database server is down. That is also possible in real world scenarios. Let me stop the SQL Server instance so that
we can replicate this issue in our WebAPI. We are not handling this error in our controller. And our exception middleware should catch
this error. And you can see, we are returning all unknown
error responses using this class, we have some properties here to set error code, error
message and error details. And if the environment is development, we
are returning this error trace as well. But we are not returning the error trace in
the production environment. And this is totally a different kind of json
object, we are returning from here in case of unhandled error. So our database server is down, let’s see
what will happen if we try to login now. Again we got blank notification this time. And the reason is we got status code 500,
that is not zero this time, so our interceptor did not consider this as unknown error. And error node contains a JSON object, that
contains 3 different properties. But our interceptor is expecting a string
in this error node. So to fetch this error in our interceptor,
we will have to use error.errorMesssage here. But we are still getting blank errors. Let me check the node, maybe I have made some
spelling mistake. Yup got it, we are returning these 3 properties
in Pascal Notation, the first character is capital. But if you look at the other nodes, we are
getting node names in camel case notation. So either we have to use here as well as pascal
notation. And if I change the first letter capital here,
we should correctly fetch the error. But I do not want to go with this approach
and would like to keep the same standard for all error nodes. Let’s correct this behaviour at API level
instead of renaming it to the pascal notation. So go to the API Error class and here we are
serializing this object. By default dotnet use pascal notation for
json serialization. But we can also set the camel case using the
JsonSerializer Options and set propertyNamingPolicy to JsonNamingPolicy.CamelCase. Now we can pass this option as the 2nd argument
of this serialize method to tell that all the properties should be serialized in CamelCase. Save it and Let’s try to login one more
time. Yup we get this error detail now that is thrown
by our API middleware. We are getting so much details in this error
because we are running our API in a development environment. But if we will run it in the production environment,
we will get only “Internal Server Error” here in the notification. You can see, now we are getting all properties
here in camel case notation. So it's working fine for unhandled error now,
but it will start to fail for the handled error that we already have tested, because
in our controller we are not using this API Error class to return an error response. So we will use the object of the same class
here as well for the handled error in all of our controllers. We should always be consistent in returning
error responses so that our API client should not have to put so many if else cases to correctly
display the error at their end. So let’s define a variable of type ApiError. It seems we do not have a constructor in this
class that accepts null arguments, that is why I am getting this error here. Yes, we do not have that in place. Let’s add one more constructor with a blank
argument here, so that we can instantiate the object without passing any argument. Save it. And now the error has gone. We can now set the properties of this object
here. Set error code = it should be the code of
error we are going to return in the response. We can use unauthorized.status code here to
get the correct error code for unauthorized error. And set the apierror.ErrorMessage, Just copy
this message from here. And also we can set the 3rd optional argument
here to set the errordetails, where we can provide some details of this error. Let’s tell the user that “This error appear
when provided user id or password does not exists”. And finally return this apiError object here
in response. Let’s run the database server and test if
we are correctly getting this handled error or not. And if I try to login using invalid user id. Yup, we are getting the correct error. And in the console we are getting JSON objects
in the error node with all of these 3 properties that we are setting from our controller. So we have tested all 3 test cases, one when
API server is down and another when database server is down and the 3rd one when handled
error is thrown by our API and all 3 are working fine. And also we need to update this register method
as well to return error using the object of type APIError. So this time I have not set errorDetails. Let’s test it by trying to create a user
that already exists in the database. Yup we are getting errors properly on the
component as well. And in Console, if we examine this error node,
we get null in errorDetails property as we are not setting anything for this property
in our controller. So we have successfully implemented global
error handling using interceptor in this video and also updated our API to return consistent
error response in different methods. We were supposed to fix this issue as well
by putting a validation check to return error in case users try to pass a blank user name
or password in this video, but video has become little lengthy. And, so I would like you to take it as an
exercise. As a hint we need to put a check here in the
register method. We need to check if the provided user id or
password is blank, then we will have to return this badrequest. And we should return the error response using
the API error class. You can use string.isnullorEmpty function
on the user id and password for this check. And if that is true, you need to return the
bad request. If you are not able to do that, no worry I
will explain the same in our next video. And we will also see how to add an extension
method to incorporate null checks more efficiently, so that we can use that extension throughout
the application. Also we will see how to implement retry logic
in our interceptor, so that http requests should be able to retry automatically in case
some connectivity issue happens during http calls. And also please tell me some more scenarios
that we can implement in our interceptor, feel free to ask if you have any questions. Please like this video, if you really learned
something new in this video. And… see you in the next video, stay tuned,
bye bye.