NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Package Toolkit for Laravel

Výnimky a retry-after

Všetko, čo toolkit vyhadzuje, dedí z jedného základu, takže akékoľvek zlyhanie toolkitu zachytíte na jednom mieste — alebo konkrétnu podtriedu:

  • PackageToolkitException — abstraktný základ pre všetko, čo toolkit vyhadzuje (RuntimeException).
  • InvalidConfigurationException — konfiguračná hodnota chýba, má nesprávny typ, je mimo rozsahu, nie je prípadom enumu, nie je platnou položkou zoznamu alebo nie je triedou modelu; vyhadzuje ju aj DatabaseDriver::current() pre driver, ktorý enum nemodeluje.
use RoundlyConsulting\PackageToolkit\Exceptions\InvalidConfigurationException;
use RoundlyConsulting\PackageToolkit\Exceptions\PackageToolkitException;
use RoundlyConsulting\PackageToolkit\Support\Config;
use RoundlyConsulting\PackageToolkit\Support\ModelResolver;

try {
    $perPage = Config::integer('comments.per_page', 20, min: 1, max: 100);
} catch (InvalidConfigurationException $e) {
    // Configuration value [comments.per_page] must be between 1 and 100, [500] given.
}

try {
    $class = ModelResolver::for('comments.models.comment', Comment::class);
} catch (PackageToolkitException $e) {
    // any toolkit failure
}

Správy

Každé zlyhanie má pomenovanú továrenskú metódu a pevnú správu, takže logy sa dajú ľahko prehľadávať. Každá správa o chybnej hodnote má rovnaký tvar — Configuration value [key] must be …, [given] given. (pri položke zoznamu must contain …) — kde given je chybná hodnota, pri dvoch továrenských metódach pre položky zoznamu chybná položka, tak, ako bola zapísaná ('' pre prázdnu), int, float či bool ako PHP literál (500, 1.5, true) a čokoľvek iné ako jeho typ (array, null):

Továrenská metódaSpráva
missing($key)Configuration value [{key}] is required but missing.
notAString($key, $value)Configuration value [{key}] must be a non-empty string, [{given}] given.
notAnInteger($key, $value)Configuration value [{key}] must be an integer, [{given}] given.
notAFloat($key, $value)Configuration value [{key}] must be a decimal number, [{given}] given.
outOfRange($key, $min, $max, $value)Configuration value [{key}] must be between {min} and {max}, [{given}] given.
notAList($key, $value)Configuration value [{key}] must be a list of strings (an array or a comma-separated string), [{given}] given.
notAStringItem($key, $item)Configuration value [{key}] must contain only string items, [{item}] given.
notAValidListItem($key, $item)Configuration value [{key}] must contain only valid items, [{item}] given.
notABoolean($key, $value)Configuration value [{key}] must be a boolean (true/false, 1/0, on/off or yes/no), [{given}] given.
notAValidEnum($key, $enum, $value)Configuration value [{key}] must be one of [{values}], [{given}] given.
notOneOf($key, $allowed, $value)Configuration value [{key}] must be one of [{allowed}], [{given}] given.
notAKeyType($key, $value)Configuration value [{key}] must be one of [bigint, uuid, ulid] (case-insensitive), [{given}] given.
notAnImplementation($key, $contract, $value)Configuration value [{key}] must be a class-string of [{contract}], [{given}] given.
notAModel($key, $value, $base)Configuration value [{key}] must be a class-string of [{base}], [{given}] given.
unsupportedDatabaseDriver($driver)Unsupported database driver [{driver}].

Pri jedinej hranici znie outOfRange() „must be at least {min}“ alebo „must be at most {max}“ namiesto „between“. Používa ju integer() aj float().

Hodnotu zabalenú do SensitiveParameterValue z PHP opíše správa typom toho, čo obaľuje ([int] given., [string] given.), nikdy jej obsahom — čítače tajných hodnôt takto odovzdávajú každú hodnotu. Každý parameter s hodnotou ($value, $item) má #[SensitiveParameter], takže hodnota sa ukáže jedine v správe, nikdy v argumentoch rámca stack trace. Pozrite časť Tajné hodnoty v konfigurácii a fasádach.

Retry-after

HasRetryAfter je kontrakt pre výnimky (alebo iné signály), ktoré nesú informáciu o čakaní pred ďalším pokusom. Hostiteľ tak zlyhanie rate limitu prevedie na hlavičku Retry-After bez väzby na konkrétnu základnú výnimku. Implementuje ho trait ProvidesRetryAfter: withRetryAfter() uloží oneskorenie v sekundách, orezané na nulu a viac, a vráti výnimku; retryAfterSeconds() ho prečíta, predvolene 0:

use RoundlyConsulting\PackageToolkit\Concerns\ProvidesRetryAfter;
use RoundlyConsulting\PackageToolkit\Contracts\HasRetryAfter;
use RuntimeException;

final class TooManyRequestsException extends RuntimeException implements HasRetryAfter
{
    use ProvidesRetryAfter;
}

$e = (new TooManyRequestsException('Rate limit reached.'))->withRetryAfter(30);
$e->retryAfterSeconds();                                                 // 30

(new TooManyRequestsException)->retryAfterSeconds();                     // 0 — the default
(new TooManyRequestsException)->withRetryAfter(-5)->retryAfterSeconds(); // 0 — clamped

throw $e;
use RoundlyConsulting\PackageToolkit\Contracts\HasRetryAfter;

// Host side — no knowledge of the package's exception classes needed:
try {
    // … call into the package
} catch (HasRetryAfter $e) {
    return response('Too Many Requests', 429)
        ->header('Retry-After', (string) $e->retryAfterSeconds());
}

Jeden handler v hostiteľskej aplikácii

Ak chcete na všetky takéto výnimky odpovedať rovnako, zaregistrujte jeden handler v bootstrap/app.php hostiteľskej aplikácie cez $exceptions->map() alebo $exceptions->respond(). Nepoužívajte $exceptions->render(fn (HasRetryAfter $e) …): Laravel spustí najprv map(), potom vlastnú metódu render() výnimky a až po nej callbacky render(). Mnohé výnimky balíkov majú render() implementovanú samy, a keď vráti odpoveď, callback render() sa už nikdy nespustí.

map() zmení výnimku na ThrottleRequestsException z Laravelu, ktorá sa vykreslí ako 429 s hlavičkami, ktoré odovzdáte. Beží ešte pred vlastnou render() výnimky, takže pokryje každú HasRetryAfter:

use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Http\Exceptions\ThrottleRequestsException;
use RoundlyConsulting\PackageToolkit\Contracts\HasRetryAfter;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->map(fn (HasRetryAfter $e) => new ThrottleRequestsException(
        $e->getMessage(),
        $e,
        ['Retry-After' => (string) $e->retryAfterSeconds()],
    ));
})

Mapovanie platí aj pri reportovaní výnimky. ThrottleRequestsException Laravel nereportuje, takže namapované výnimky sa do vašich logov už nedostanú — pri „príliš veľa pokusov“ zvyčajne presne to, čo chcete. respond() naopak ponechá odpoveď, ktorú výnimka vykreslila, a upraví ju; beží pri každej vykreslenej odpovedi vrátane tej z vlastnej render() výnimky:

use Illuminate\Http\Request;
use RoundlyConsulting\PackageToolkit\Contracts\HasRetryAfter;
use Symfony\Component\HttpFoundation\Response;

$exceptions->respond(function (Response $response, Throwable $e, Request $request): Response {
    if ($e instanceof HasRetryAfter) {
        $response->setStatusCode(429);
        $response->headers->set('Retry-After', (string) $e->retryAfterSeconds());
    }

    return $response;
});

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.