298 lines
6.8 KiB
Markdown
298 lines
6.8 KiB
Markdown
# Rate Limiting
|
||
|
||
* Introduction
|
||
* Cache Configuration
|
||
* Basic Usage
|
||
* Manually Incrementing Attempts
|
||
* Clearing Attempts
|
||
|
||
## Introduction
|
||
|
||
Laravel includes a simple to use rate limiting abstraction which, in
|
||
conjunction with your application's [cache](cache), provides an easy way to
|
||
limit any action during a specified window of time.
|
||
|
||
If you are interested in rate limiting incoming HTTP requests, please consult
|
||
the [rate limiter middleware documentation](/docs/12.x/routing#rate-limiting).
|
||
|
||
### Cache Configuration
|
||
|
||
Typically, the rate limiter utilizes your default application cache as defined
|
||
by the `default` key within your application's `cache` configuration file.
|
||
However, you may specify which cache driver the rate limiter should use by
|
||
defining a `limiter` key within your application's `cache` configuration file:
|
||
|
||
|
||
|
||
1'default' => env('CACHE_STORE', 'database'),
|
||
|
||
2
|
||
|
||
3'limiter' => 'redis',
|
||
|
||
|
||
'default' => env('CACHE_STORE', 'database'),
|
||
|
||
'limiter' => 'redis',
|
||
|
||
## Basic Usage
|
||
|
||
The `Illuminate\Support\Facades\RateLimiter` facade may be used to interact
|
||
with the rate limiter. The simplest method offered by the rate limiter is the
|
||
`attempt` method, which rate limits a given callback for a given number of
|
||
seconds.
|
||
|
||
The `attempt` method returns `false` when the callback has no remaining
|
||
attempts available; otherwise, the `attempt` method will return the callback's
|
||
result or `true`. The first argument accepted by the `attempt` method is a
|
||
rate limiter "key", which may be any string of your choosing that represents
|
||
the action being rate limited:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
2
|
||
|
||
3$executed = RateLimiter::attempt(
|
||
|
||
4 'send-message:'.$user->id,
|
||
|
||
5 $perMinute = 5,
|
||
|
||
6 function() {
|
||
|
||
7 // Send message...
|
||
|
||
8 }
|
||
|
||
9);
|
||
|
||
10
|
||
|
||
11if (! $executed) {
|
||
|
||
12 return 'Too many messages sent!';
|
||
|
||
13}
|
||
|
||
|
||
use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
$executed = RateLimiter::attempt(
|
||
'send-message:'.$user->id,
|
||
$perMinute = 5,
|
||
function() {
|
||
// Send message...
|
||
}
|
||
);
|
||
|
||
if (! $executed) {
|
||
return 'Too many messages sent!';
|
||
}
|
||
|
||
If necessary, you may provide a fourth argument to the `attempt` method, which
|
||
is the "decay rate", or the number of seconds until the available attempts are
|
||
reset. For example, we can modify the example above to allow five attempts
|
||
every two minutes:
|
||
|
||
|
||
|
||
1$executed = RateLimiter::attempt(
|
||
|
||
2 'send-message:'.$user->id,
|
||
|
||
3 $perTwoMinutes = 5,
|
||
|
||
4 function() {
|
||
|
||
5 // Send message...
|
||
|
||
6 },
|
||
|
||
7 $decayRate = 120,
|
||
|
||
8);
|
||
|
||
|
||
$executed = RateLimiter::attempt(
|
||
'send-message:'.$user->id,
|
||
$perTwoMinutes = 5,
|
||
function() {
|
||
// Send message...
|
||
},
|
||
$decayRate = 120,
|
||
);
|
||
|
||
### Manually Incrementing Attempts
|
||
|
||
If you would like to manually interact with the rate limiter, a variety of
|
||
other methods are available. For example, you may invoke the `tooManyAttempts`
|
||
method to determine if a given rate limiter key has exceeded its maximum
|
||
number of allowed attempts per minute:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
2
|
||
|
||
3if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
|
||
|
||
4 return 'Too many attempts!';
|
||
|
||
5}
|
||
|
||
6
|
||
|
||
7RateLimiter::increment('send-message:'.$user->id);
|
||
|
||
8
|
||
|
||
9// Send message...
|
||
|
||
|
||
use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
|
||
return 'Too many attempts!';
|
||
}
|
||
|
||
RateLimiter::increment('send-message:'.$user->id);
|
||
|
||
// Send message...
|
||
|
||
Alternatively, you may use the `remaining` method to retrieve the number of
|
||
attempts remaining for a given key. If a given key has retries remaining, you
|
||
may invoke the `increment` method to increment the number of total attempts:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
2
|
||
|
||
3if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) {
|
||
|
||
4 RateLimiter::increment('send-message:'.$user->id);
|
||
|
||
5
|
||
|
||
6 // Send message...
|
||
|
||
7}
|
||
|
||
|
||
use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
if (RateLimiter::remaining('send-message:'.$user->id, $perMinute = 5)) {
|
||
RateLimiter::increment('send-message:'.$user->id);
|
||
|
||
// Send message...
|
||
}
|
||
|
||
If you would like to increment the value for a given rate limiter key by more
|
||
than one, you may provide the desired amount to the `increment` method:
|
||
|
||
|
||
|
||
1RateLimiter::increment('send-message:'.$user->id, amount: 5);
|
||
|
||
|
||
RateLimiter::increment('send-message:'.$user->id, amount: 5);
|
||
|
||
#### Determining Limiter Availability
|
||
|
||
When a key has no more attempts left, the `availableIn` method returns the
|
||
number of seconds remaining until more attempts will be available:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
2
|
||
|
||
3if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
|
||
|
||
4 $seconds = RateLimiter::availableIn('send-message:'.$user->id);
|
||
|
||
5
|
||
|
||
6 return 'You may try again in '.$seconds.' seconds.';
|
||
|
||
7}
|
||
|
||
8
|
||
|
||
9RateLimiter::increment('send-message:'.$user->id);
|
||
|
||
10
|
||
|
||
11// Send message...
|
||
|
||
|
||
use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
if (RateLimiter::tooManyAttempts('send-message:'.$user->id, $perMinute = 5)) {
|
||
$seconds = RateLimiter::availableIn('send-message:'.$user->id);
|
||
|
||
return 'You may try again in '.$seconds.' seconds.';
|
||
}
|
||
|
||
RateLimiter::increment('send-message:'.$user->id);
|
||
|
||
// Send message...
|
||
|
||
### Clearing Attempts
|
||
|
||
You may reset the number of attempts for a given rate limiter key using the
|
||
`clear` method. For example, you may reset the number of attempts when a given
|
||
message is read by the receiver:
|
||
|
||
|
||
|
||
1use App\Models\Message;
|
||
|
||
2use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
3
|
||
|
||
4/**
|
||
|
||
5 * Mark the message as read.
|
||
|
||
6 */
|
||
|
||
7public function read(Message $message): Message
|
||
|
||
8{
|
||
|
||
9 $message->markAsRead();
|
||
|
||
10
|
||
|
||
11 RateLimiter::clear('send-message:'.$message->user_id);
|
||
|
||
12
|
||
|
||
13 return $message;
|
||
|
||
14}
|
||
|
||
|
||
use App\Models\Message;
|
||
use Illuminate\Support\Facades\RateLimiter;
|
||
|
||
/**
|
||
* Mark the message as read.
|
||
*/
|
||
public function read(Message $message): Message
|
||
{
|
||
$message->markAsRead();
|
||
|
||
RateLimiter::clear('send-message:'.$message->user_id);
|
||
|
||
return $message;
|
||
}
|
||
|