Video details

Ward Bell: Http Interceptors - The Room Where It Happens

Angular
07.15.2020
English

Ward Bell

Watch all the ng-conf: Hardwired presentations/videos at https://videos.ng-conf.org
ng-conf: Hardwired is brought to you by: - https://thinkster.io/ - https://herodevs.com/
ng-conf is a three-day Angular conference focused on delivering the highest quality training in the Angular JavaScript framework. 1500+ developers from across the globe converge on Salt Lake City, UT every year to attend talks and workshops by the Angular team and community experts.
Follow us on twitter https://twitter.com/ngconf Official Website: https://www.ng-conf.org/

Transcript

Because I'm talking about Http interceptors and the theme we're still with musicals is the great one Hamilton. There's got to be a little pop quiz at the end of my talk. What? Don't look in your wallets. Don't go on the net. What what bill is Alexander Hamilton on? Just keep that in the back of your mind. So back in 1790, Alexander Hamilton got his bangs and the cost of moving the capital to some swamp there, Virginia. And no one knows how that happened. It's the room where it happened. So there's an analogy here somewhere. If you're using HTP client and you're the requester, you send requests to the server and you get responses back, but you don't know what happened. It's all behind the scenes. Now, if you're like me, you'd probably like to inspect that request. Or maybe you would want to modify it in some way, add some headers. Maybe you don't want to issue that request at all. Maybe you want to look at that response and interpret it before it goes back up. Perhaps you want to modify a little bit, tweak it a little bit before you send it back to the caller. And in some cases, you'll simply want to replace the response altogether. And for that, you on age TDP interceptors, hard to say, but easy to use. OK. Let's jump right into one. See the structure of it? We're going to write a locking interceptor and it's a class. Hey, that's great. And implements and angular interface. All of the interfaces that you'll need are in the HDTV client library there and get our common HDTV. And oh yes. So the interface only has one method. And it's called intercept. And it takes a request. And this funny thing called an HTP handler. And it's going to return and observable. Let's take a look at that. There's that request. Then you do your thing with that request. Then you take the request and you handle it. You pass it to the handle method and you return that. And that is, in fact, an observable of HTP response. I know it says HP event, but 90 percent of the time it's a response. All right. So that was the going out part. You can see that we logged it. We could have done. That's the thing we wanted to do with the request. Now that we've done that, we are going to do something. And the next handle returned and observable. And so you do need to know a little bit about observables. You need to know enough that you're going to pipe something onto the end of it before it wanders on its way. And you use your operators. They go there. And in this case, what we're going to do is tap into the response and lock it because we're interested in a side effect. Just log it somewhere. Now, you might also be interested in logging errors, too, because things don't always go right. So Tap has two arguments in the second, one can log in error. Now, interceptors are not just classes, they're injected surfaces. So that means, well, you've got to go provide it. You can't you can't just throw an ad injector on it, as you'll see for a minute. So you got to provide it and you got to provide it in a very specific place. You got to provide it at the same injector level where you import these be client module for me and many people, that's at module. And you just throw it in there. You just throw it in there. And thank you, Siri, for enjoying the show as well. She just interrupted. So with this, you're using a different kind of providing. You're using an injector symbol. And there's this funny thing, that multi true. And that's because you're not going to have one injector. You're going to have a lot of them. And so we're not just providing a single service. We're providing an array of services. So let's let's go do another one and and note that like any service which an interceptor is, it can take injected dependencies that you can use in your interception. So I don't match you, but sometimes things take a long time to load and you need a spinner, some kind of indicator that you're busy. So, you know, it sounds simple, but we realize that we've got all of these requests for our application, they're flying in from everywhere, and you don't want to hook each one of them up to a busy indicator. You'd like to have a centralized place to do it. And when you do that, you can realize that you can have many requests that get launched all at once or in sequence. And so you can't just turn it on. You have to keep track of what you're doing and count them up. And then when you're done, you let them all go. So you've got to show that busy on the first request and you got to hide the busy after the last response. And in between, you just kind of keep it up there. So that's what we're going to do. And you'd start with a busy component. You can imagine the the how it would display. And it's going to use an observable that I created. It might this service called this state. And we've got to inject it. And when Boise State is true, the loading spinner shows up and then when it goes false, it disappears. All right. Onto the interceptor starts as a class implementing HTP interceptors. And now, you know, just like anyone, you put the service you want to eject right there in the construct there and it gets injected more. By the way, there's no provided in. All right. Remember, we can't do that provided in roup thing. And here is the intercept method, which is, you know, the shell of it. They all have this for. Let's figure out what message we want to show if if the request is a get, we'll throw loading. If it's something else, we'll just assume it's saving. And now we're on the request side. No. We just called a busy service increment and bang, the spinner will come up. Now we've got it sort of take the spinner down when the last one comes through. So we're going to do that in here somewhere inside the pipe. Remember, that's how you manipulate a response. But what one do you what operator do we choose? The one we want is finalized. And the reason we want finalize and not tap this time is because we want to be able to decrement that. We want to shut. That's been done for whatever terminates that response. So that could be, you know, the response happened. It could be that there was an error. It could be that somebody just unsubscribe without there being an error or a result. And in all those cases, you want to do it and finalize is your operator. Course, you've got to remember to provide it. So we go back to module and we find a place to put it. And I guess we've decided that it should go after logging HTP Interceptor. So now we've got to. That raises an interesting question. What's actually going on with all of these interceptors? What's going on? So we said we have a requestor at the top here and we have a server at the bottom. And well, you know, in this diagram, we've got three interceptors. So what's going on? Well, the request comes in. It goes into that. You do whatever you do to it goes into the handle. It goes onto the next interceptor, goes in, does whatever you do to it, goes on to the next intercept, whatever you did to it, goes to the hand and goes on to the next. And eventually it ends up at the server. And then the server does whatever it wants to do and it sends it back to the first interceptor, which is coming right out through the one that's closest to the server. And it goes through whatever piping you had in mind there, because it's an observable and you're just extending it and that pops out, Mako's pops back up to the next interceptor, which pops to the next interceptor. And eventually your observable ends up back there at the requestor. This is sometimes known as the Russian doll pattern, because each of the interceptors handles both the request stream going down and the response to me coming back up. So it's like stacked dolls write one into the other and all the requests flow down one way and then the responses bubble back up the other way. Let's give ourselves a nother interceptor here. This is one I actually use called the Reed Only Interceptor, and it's going to be an example of how we interrupt that flow that we just showed. So the problem is this, my app goes into read only mode from time to time for reasons I don't need to explain. And while I'm doing that, if I really want to have a read only mode, I know I should go in there and disable the input controls. But what if I miss one? And I have I need some kind of backup. I don't want a save request going to the server. So I'm going to block the posts and deletes, although there's some posts because they've got these antique service that are actually gets. So you got to let those through. So you filter those. And there are other kinds of posts that I don't really have to stop. Maybe their analytic posts. So I have to filter them a little bit. But basically, you get the idea. So let's write it again, starts with a class, and we're this time we're injecting a session service so we can know a little bit about the read-only state and going to have a logger. We probably should have had a logger in our logging interceptive. Here's the shell of our intercept method. Again, request. Next up, it's an observable and I'm going to pick up the Read-Only state from the session surface. And now, if it's not read-only or if it's one of those things that I said is okay to be sent, even if it's a post or output or something like that. Well, you know, I'm just going to handle it in the usual way. Otherwise, if it is read only it I'm supposed to stop it. Of course, I'm going to compose an error and I'm going to log it out. But now I got to do something a little different. I'm going to I'm not going to throw an error. I'm going to return an error observable. And one of the key things to notice here is I am not calling next handle. That's right. I am not going to hand this down the down to the next doll in the stack dolls. So let's see what that looks like. All right. We have our three. Now, we really do have three interceptors and they're logging Read-Only and Busi Interceptor because that's the way I'm going to set them up. Right. So the first one goes the logging and it gets logged. The next one goes to the Reed only interceptor. Oh, but it happens to be a post, right? You can see it's a post at the top. So that gets intercepted and it's an error response that flows up, that flows back to the requester. Busy interceptor. Never saw it. The surfer never saw. We love being able to do that kind of thing. And in order to get it to work just like that, it's it's really an all in how you provide it. So we we are already had the logging interceptor and the busy interceptor, and we know we want to log it, but we don't want to get to the busy. So what are we gonna do? Well, the order in the array matters. So we got to move that one up and stick our read only interceptor in there. So the order in which you provide matters because that matters. By the way, we're always going to want to have busy last because there's no reason to throw a spinner unless you're actually actually actually going to the server. So the best way is not to dump them into app module or sprinkle them around in a bunch of mud. The best is to sort of pull it together so you can control the order. And I do that in a barrel. And so I create an array, a provider array of interceptor providers. It's what I call it. And that's where I have the exact same thing I would have done in a module. But I order them here exactly the way I want them. And then I go back to my app model module. I'm refactoring here and I pull out all those guys and I put in my HTP interceptor providers. And now as I evolve my interceptors, I'm not revisiting a module, which is a good thing. That's another good thing. Let's add a fourth one, because our we're interested in. Well, generally in authenticating, but also our lesson here is about how to manipulate the request and the response. So it is the same structure with, you know, we're going to have an authentication service and we're going to use the router. So we inject those and then we're ready to start power intercept method. That's our show. So from the service, we want to get our access token and we want to prepare our headers here. So we we set up some nice headers and we just designed those to the request. Right now, the request object is immutable. You're not allowed to change it. But it does come with a clone method. And so the clone will make a shallow copy of it. And it also takes an optional argument where you can tell it, what it how it should update the request. And so that's what we're going to set the headers that we created there. And also because we're talking cause here we want cookies to flow back and forth. We can set some other things at the same time, like with credentials. And now we're ready to return the handle. But notice what I'm doing here, because I may want to handle errors, but I don't have to jam it all into the intercept method. I can delegate that to another method. Not only is it another method, it's actually not another method. It's an operator. Yes, friends. We're going to create our own are x. J. S operator. Is that hard to do? No, it's not hard at all. An operator is simply a function that takes an observable in and puts an observable out. And in our case, for these things, we're going to take that that observe all of the response in and put one out. All right. Well, how hard is that to do? It's really not hard at all. You just take that source and you pipe onto it and then you go back to your, you know, whatever your level of our extraneous skills are and start working that source. So in our case, we're going to catch the error. That's the only thing we're going to do. If it's good, it's just going to fly right out of here. It's gonna to be a pass through operator. And then we we do whatever we're going to do. So inside the catch error, if it's a four, a one, which means you're not authenticated, there's some kind of unauthorized in some sense, then I'm going to pass that on. That's something that does something about that. And otherwise, I'm going to use that thing. We use last time the throw error. I'm just going to return an error object that that will pass back up through the operator, the interceptor dolls that we create. What made that error handler for the fall look like? Well, I'm gonna go dig into the headers from the response. And if the token has expired, I'll do one thing if it's any other, cause I'm going to do something else. Well, if it's expired, there's not much I can do. So I've got that all service hanging around and I could just sign it again when you sign in, you know, uses some off out of the app sign and mechanism. We don't sign it within the app. We delegate that out maybe to identity servers something. And so it's gonna leave yet. But if for any other reason it's unauthorized, I got a problem and I've got to figure out what I'm going to do about it. And you know what I'm going to do? I'm going to navigate away. That's right. You can from inside an interceptor. You can navigate and go to some safe place like wherever you're supposed to go with. There's some kind of authorization. Now you still have an obligation. Notice we hadn't returned anything at this point. Still have an obligation to return something. And we know we've handled the error and we know that whoever is listening for this shouldn't do anything with a result. But they shouldn't fail either. So we're going to return empty, which simply the empty observable just completes. And that will unsubscribe or do whatever it is that has to be done downstream. But it won't sentiment, Erin, we'll send him results. They'll just be able to keep moving. And then, of course, we've got to provide it. But but where does it go? Where should I put it? Well, I'm going to leave that as an exercise for you to figure out why that's the right place. Over time, you'll be looking at your your your scepter providers and deciding what's the right order for you. So one last time, we're going to go through the flow. It goes down, it goes to read only it goes to off interceptor busy interceptor under the serve her. Back, back, back, back. But wait a minute. How did it get to the server? What's in there? What's the mystery in there? Well, it turns out that HDP client actually has a final interceptor. There is exactly one called HTP back and you can actually replace it. In fact, the injured are in memory. Web API does replace it. But but that's guaranteed to be the last intercept no matter one of their interceptors you set up. And that's the one that makes the eight x h arkel. And so that's what your interceptor gets to, which then goes to the server, comes back here, connects up. There you go. All right. It's back to the quiz. Back to the quiz. What bill is Alexander Hamilton on? It turns out he's on the 10. So you're free to send your ten dollars to me. Now, one of the things, though, in this time of crisis, with two trillion dollars in funding, the government has to make it up some way. So they're thinking of allowing celebrities to put their faces on their bidding it out. So pretty soon we could be seeing Taylor Swift on the tin. And I just think this is a tragedy. To compound the existing tragedy, I am so disgusted by the idea that we might might sell our current our sacred currency out. And I hope you join me in your sense of disgust. And on that pleasant note, I thank you very much for attending the HPV to enter. Sceptered.