Preskočiť na obsah

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.

Otvoriť diagram v plnej veľkosti

Rozhranie ​

php
interface RateLimitInterface
{
    public function check(): ?RateLimitResponse;
}

check() beží prvý, pred parametrami aj autorizáciou. Vráťte null pre „tu bez limitu" alebo RateLimitResponse:

php
new RateLimitResponse(int $limit, int $remaining, ?int $retryAfter = null, ?IResponse $errorResponse = null)
VýsledokOdpoveď
nullnič sa nepridá, požiadavka pokračuje
remaining > 0hlavičky X-RateLimit-Limit a X-RateLimit-Remaining, požiadavka pokračuje
remaining = 0429, 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
<?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:

yaml
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.

Vydané pod licenciou MIT.