در حال بارگذاری ...

Why Rate Limiting0:00

Especially when writing APIs, you may find yourself in situations where you want to limit the number of requests a user or IP address is allowed to make. This is pretty standard practice, and in Laravel 5.2, this is a cinch now. For example, I'm going to switch to my routes file. Let's imagine you're building the next Twitter or something. Okay, well, you want to offer an API to search your service. However, if you don't limit the number of requests a user is allowed to make, well, yeah, that can be pretty dangerous, right? Because if they're not careful and they're not caching things properly or limiting the number of requests they make, they could just hit your server over and over and over and over. And then if you compound this with all the people making requests to your server,

they could just hit your server over and over and over and over. And then if you compound this with all the people making requests to your server, yeah, this can be kind of dangerous. So instead, most APIs will implement a limiter for these exact reasons. So let's experiment. Let's say if you visit the home page, I'm just going to return some page. And remember, with Laravel, this will automatically be cast to JSON. So if I boot up a server and view this in the browser, yeah, exactly what we expect. Pretty boring stuff. Now, alternatively, if you want to test this from the command line,

Testing with HTTPie1:07

Pretty boring stuff. Now, alternatively, if you want to test this from the command line, well, yeah, you can use cURL, but I prefer to use something called HTTPie. You can do if you have Homebrew installed. If not, you definitely want to pull that in. brew install HTTPie, and now you can use a much more natural approach to test these things. For example, I can say HTTP make a GET request to this endpoint. And there you go. We get a 200 status, and here is the JSON payload. Okay, but now if we switch back, well, let's say we have like API/search/a-term, okay?

Adding Throttle Middleware1:32

We get a 200 status, and here is the JSON payload. Okay, but now if we switch back, well, let's say we have like API/search/{term}, okay? And we're not really going to build this, but we'll just say return results for the term. And in fact, let's just keep it simple and do it like that. All right, so we make a GET request to API/search/dogs, and yeah, we get the results. But now I want to limit it so that you can only make X number of requests per minute, let's say. Okay, well, we just need to add a middleware. Let me demonstrate that, and then we'll dig in to see how it works. Now, as you know, you can either do things like this where you say middleware, or you can also do chaining like this.

Now, as you know, you can either do things like this where you say middleware, or you can also do chaining like this. middleware, and we're going to throttle. Now, we can pass arguments to this, or we can stick with the defaults. Now, in this case, what we're saying is we want to throttle the number of requests made by this IP address to this endpoint, and we will limit it to, again, by default, 60 requests per minute. Now, if you want to change that, you could do something like this, 30. Now, it's 30 requests per minute. Or if we say 5, that means you can make 30 requests in 5 minutes. Now, in our case, though, I do want to demonstrate this, so I will decrease it to 3,

But, yeah, we wouldn't even get to that point. Instead, the middleware is going to detect, oh, yeah, you made too many requests here. So instead of actually triggering this callback here, I'm going to return a response. And if you're curious, that's a 429. That's a standard status code for too many requests. Now, what I'll do here just to prove to you that it works is I'm going to sit around for about a minute. Pause the video for a minute, and then we'll make another request to see that, yes, we can now make new requests. Okay, so I paused. If we now make a new request, we've waited for the timeout, and we now can make requests once again to our server and subsequently perform database queries.

Verifying Limit Headers3:51

If we now make a new request, we've waited for the timeout, and we now can make requests once again to our server and subsequently perform database queries. Now, once again, take a note at these headers. So I'm going to do another one. Now we've made two requests, so we have one remaining. If I do another one, we have zero remaining, and we've hit our rate limit. So that means if we do any more before the decay, which is about one minute, yeah, we hit that, so we return a response. Okay, so let's review how exactly does this work, what's going on behind the scenes. All right, we'll take a look at this. We're going to go up to our app directory, into HTTP, middleware.

Finding Throttle in Kernel4:27

All right, we'll take a look at this. We're going to go up to our app directory, into HTTP, middleware. Here's where all of your default middleware are stored. However, you're not going to see anything related to a rate limiter. This will be through the vendor directory. So instead, if I go down to Kernel, where we register everything, yeah, take a look at our route middleware. We have this new one in Laravel 5.2 called throttle, and that will point to a throttleRequests middleware. Okay, so that means you can apply this to any route to instantly add a route limiter. Now, if we take a look at this class, let's see what's going on. It's standard middleware.

Now, if we take a look at this class, let's see what's going on. It's standard middleware. And the very first thing, whenever you're looking at a new middleware, is to see what options it accepts. So every middleware will receive the request, of course, and then the next function or the next middleware to trigger when it's done. But now, from here to the end, yeah, this will be any parameters you can pass to the middleware. So when I said you could do throttle 3 and then 5, yeah, that means the max number of attempts would be 3, and the decay, basically how long you have before you can make new requests, that would be set to 5 minutes. Or, yeah, you can leave those off and stick with the defaults. Okay, so let's see what's going on. You'll see it's pretty readable. First, we resolve the request signature.

Walking Through Middleware Flow5:44

Okay, so let's see what's going on. You'll see it's pretty readable. First, we resolve the request signature. Not confusing, we're just building up a unique key based upon the URL and the user's IP address. And that basically just encrypts it and does a little SHA-1 on a concatenated version of that. And now you have your unique key. Next, it looks like we're referencing a rate limiter dependency. And we're asking it, do we have too many attempts for this specific key? And notice we're passing in exactly what you would send through your routes.php file. Now, if that turns out to be the case, yeah, we don't continue on. So notice we don't continue on to the next middleware in the onion.

Now, if that turns out to be the case, yeah, we don't continue on. So notice we don't continue on to the next middleware in the onion. We instead just immediately hijack it and return a fresh response. And specifically, we're returning a 429 Too Many Requests along with a few headers, which will give some useful feedback to the user. Now, otherwise, and by the way, we're going to review what happens here in just a minute. But otherwise, if they haven't made too many attempts, then we will hit the limiter. And for that, we're basically just incrementing a key in the cache. And then finally, we continue on to the next request and pass through the headers. So now that we understand the basic flow, well, how do we determine if you've made too many attempts?

How RateLimiter Tracks Attempts6:51

And then finally, we continue on to the next request and pass through the headers. So now that we understand the basic flow, well, how do we determine if you've made too many attempts? Let me take a look. We're going to switch over to the RateLimiter class. Here we go. Okay, so notice we're checking to see if the User is locked out. Well, yeah, there wouldn't be any cache key with that name after the first request, right? But we may eventually. So we check to see, yeah, if you've been locked out, we're immediately going to return true. That means you've made too many requests.

So we check to see, yeah, if you've been locked out, we're immediately going to return true. That means you've made too many requests. But otherwise, if this is our very first request, that wouldn't be applicable. So the only other thing we check is, is the number of attempts for this key greater than the maximum number of attempts that you specified? And let's see how we call that. Okay, yeah, notice we're just referencing a key in the cache. So remember, this key will be a unique SHA-1 version of your IP address and the URL. So we just fetch that, and we check to see if we go back. Yeah, if that return value is greater than in our little example 3, yep, too many attempts.

So we just fetch that, and we check to see if we go back. Yeah, if that return value is greater than in our little example 3, yep, too many attempts. So we add a new key to the cache called lockout, and we make that equal to the new time when they can make additional requests again. And that's it. So now, on the next request, we will hit this, and we'll see, well, do we have anything in the cache with this key lockout? Yes, we do. So if that's the case, we immediately return true. Now, if we go back to throttleRequests, yeah, now we have too many attempts.

So we simply increment that key by 1. So the key, whatever that would be, would be 1. And then you hit it again, it becomes 2. Hit it again, it becomes 3. And eventually, that tooManyAttempts method where we check to see, yeah, is the number of attempts greater than the maximum number of attempts? Well, eventually, that will be true, in which case we return and subsequently cancel the request. That's all there is to it, and this is brand new in Laravel 5.2.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟