Obmedzenie požiadaviek
Obmedzenie požiadaviek sa nastavuje voliteľným štvrtým argumentom addApi(). Knižnica definuje rozhranie a odpoveď; ako požiadavky počítate (Redis, databáza, súbor), je na vás.
Rozhranie
interface RateLimitInterface
{
public function check(): ?RateLimitResponse;
}check() beží prvý, pred parametrami aj autorizáciou. Vráťte null pre „tu bez limitu" alebo RateLimitResponse:
new RateLimitResponse(int $limit, int $remaining, ?int $retryAfter = null, ?IResponse $errorResponse = null)| Výsledok | Odpoveď |
|---|---|
null | nič sa nepridá, požiadavka pokračuje |
remaining > 0 | hlavičky X-RateLimit-Limit a X-RateLimit-Remaining, požiadavka pokračuje |
remaining = 0 | 429, tie isté hlavičky plus Retry-After a {"status":"error","message":"Too many requests. Retry after N seconds."} alebo vaša $errorResponse. Pri retryAfter null ide prázdna hlavička Retry-After a správa Retry after seconds. bez čísla |
Endpointy bez obmedzenia požiadaviek dostanú NoRateLimit, ktorý vracia null.
Príklad
Príklad autorizácie počíta požiadavky podľa IP v súbore, 3 za minútu:
<?php
declare(strict_types=1);
namespace App;
use Tomaj\NetteApi\Misc\IpDetectorInterface;
use Tomaj\NetteApi\RateLimit\RateLimitInterface;
use Tomaj\NetteApi\RateLimit\RateLimitResponse;
/**
* A fixed window per client IP, counted in a file. Use Redis or a database in production.
*/
final class FileRateLimit implements RateLimitInterface
{
public function __construct(
private IpDetectorInterface $ipDetector,
private string $directory,
private int $limit = 3,
private int $window = 60,
) {
}
public function check(): ?RateLimitResponse
{
$windowStart = intdiv(time(), $this->window) * $this->window;
$file = $this->directory . '/rate-' . md5($this->ipDetector->getRequestIp()) . '-' . $windowStart;
$hits = (int) @file_get_contents($file) + 1;
file_put_contents($file, (string) $hits, LOCK_EX);
$retryAfter = $windowStart + $this->window - time();
// ApiPresenter answers 429 with Retry-After when remaining is 0,
// so the request being checked is not counted in remaining yet
$remaining = max(0, $this->limit - $hits + 1);
return new RateLimitResponse($this->limit, $remaining, $retryAfter);
}
}Zaregistrujte ho s autowired: false. Inak ho Nette DI autowiruje ako štvrtý argument každého volania addApi() v setupe:
services:
rateLimit:
factory: App\FileRateLimit(directory: %tempDir%)
autowired: false
apiDecider:
factory: Tomaj\NetteApi\ApiDecider
setup:
- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'limited'), App\Api\WhoAmIHandler, Tomaj\NetteApi\Authorization\NoAuthorization(), @rateLimit)Časté otázky
Vie obmedzenie požiadaviek, ktorý token volal?
check() nedostane žiadne argumenty, lebo beží pred autorizáciou. Injectnite Nette\Http\IRequest a prečítajte si hlavičku alebo IP sami.
Prečo majú zrazu všetky moje endpointy obmedzenie požiadaviek?
Služba obmedzenia požiadaviek sa autowiruje do addApi(). Nastavte autowired: false, ako je uvedené vyššie.
Logujú sa odpovede 429?
Nie, v 3.x presenter skončí skôr, ako sa spustí logger. Pozrite si Logovanie.
Môžem vrátiť vlastné telo 429?
Áno, odovzdajte ho ako štvrtý argument RateLimitResponse.
Podľa ktorej IP mám počítať?
Použite detektor IP, ktorý sa nedá podvrhnúť; pozrite upozornenie v Autorizácii.