devdev / in the loop
PHP PHP

Guida a Laravel 10
Routes: rate limiting e caching

Veniamo quindi all’ultimo sottocapitolo dedicato alle Routes. Riguarda una serie di funzioni accessorie e utilities che dimostrano quanto è completo e potente Laravel. Probabilmente nei progetti più semplici non le utilizzeremo, ma è importante capire il toolset che abbiamo a disposizione. Vediamole una per una.

Laravel include nativamente un meccanismo per limitare il numero di richieste che possono essere eseguite verso i nostri URL (endpoint), impostando un limite sia per la singola route, che per un gruppo di esse.

Rate limiting

Cominciamo scrivendo una impostazione per il nostro rate limiter che sia adatta al nostro progetto che poi potremmo utilizzare più avanti. Per farlo, il posto migliore dove piazzare questa configurazione è il metodo boot() della classe App\Providers\RouteServiceProvider. Spulciando questo file, troveremo infatti già un RateLimiter definito, che è applicato di default a tutte le route definite in routes/api.php.

 

Ecco cosa troveremo editando il file app/providers/RouteServiceProvider.php:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

/**
* Define your route model bindings, pattern filters, and other route configuration.
*/

protected function boot(): void
{
    RateLimiter::for('api', function(Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id?:$request->ip());
    });
// ...
}

Un rate limiter viene definito utilizzando il metodo statico for (una facade di Laravel). Questo accetta due parametri, il primo è semplicemente il nome di questo rate limiter (nel nostro esempio è “api”) e come secondo parametro una funzione che ci restituisce la configurazione del limite. Questa configurazione è difatti una istanza della classe Illuminate\Cache\RateLimiting\Limit, che possiede già di default alcuni metodo molto semplici da usare per impostare il limite a nostra discrezione. Ad esempio, se volessimo impostare al massimo 1000 richieste all’ora, dovremmo modificare il codice in questo modo:

protected function boot(): void
{
    RateLimiter::for('api', function(Request $request) {
        return Limit::perHour(1000);
    });
}

Teniamo sempre presente che RateLimiter riceve, come parametro $request della closure, la Request HTTP generale, quindi ci sarà possibile utilizzarla per “decidere” se quella richiesta passa o viene considerata ai fini del limite. Potremmo, per esempio, non applicare nessuna regola di bloccaggio a determinati agli utenti che sono amministratori:

protected function boot(): void
{
    RateLimiter::for('api', function(Request $request) {
        if ($request->user()->isAdmin()) {
            return Limit::none();
        } else {
            return Limit::perHour(1000);
        }

    });

}

Un’altro esempio è quello nel quale blocchiamo sì le richieste, ma applichiamo il limite a ogni singolo IP. Creiamo cioè un “numero massimo di richieste per ciascun IP”: in questo esempio accettiamo 100 richieste/ora da ciascun IP:

protected function boot(): void
{
    RateLimiter::for('api', function(Request $request) {
        return Limit::perHour(100)->by($request->ip());
    });

}

Volendo, potremmo combinare più limiter, aggregando per esempio quelli visti in precedenza. In questo caso dovremmo semplicemente inserire tutti i Limit in un array:

protected function boot(): void
{
    RateLimiter::for('milleora', function(Request $request) {
        return [Limit::perHour(100)->by($request->ip()), Limit::perHour(1000)];
    });

}

Collegare i limite alle route

Fin’ora abbiamo visto che come creare rate limiter “generici”, ovvero che funzionassero indistintamente per tutta la nostra App. In un progetto più complesso potremmo però creare più di un rate limiter, con specifiche diverse, ed associarli alle singole route. Per farlo, dobbiamo usare il middleware throttle, indicando dopo i due punti : il nome del rate limiter che abbiamo creato come negli esempi precedenti. In questo esempio abbiamo utilizzato quello denominato “milleora” appena creato:

Route::middleware(['throttle:milleora'])->group(function() {
    Route::post('/audio', function() {
    // ...
    });

    Route::post('/video', function() {
    // ...
    });
});

Cache delle route

Quando saremo pronti per portare il nostro progetto in produzione, possiamo avvalerci del sistema interno di caching di Laravel. Questo meccanismo darà un boost molto evidente alla nostra app e ridurrà drasticamente i tempi di loading. Per generare la cache, possiamo eseguire il comando:
php artisan route:cache
In questo modo verranno eseguiti i file di cache e non verranno “lette” le route ad ogni richiesta: per questo motivo ricordiamoci di pulire la cache quando aggiungiamo delle route! Per farlo utilizziamo il comando:
php artisan route:clear
Precedente ← Guida a Laravel 10
if (weekend) {
    relax();
}
la nostra newsletter, ogni tanto.