Klientët HTTP
cURL si parazgjedhje, çdo klient PSR-18 kur preferoni të përdorni tuajin, dhe një imitim për testet.
Parazgjedhja
Pa httpClient:, kërkesat kalojnë përmes OpenEmail\Http\CurlHttpClient, që nuk ka nevojë për asgjë tjetër përveç zgjerimit curl. Mban një handle cURL për çdo klient, ndaj një kërkesë e dytë ripërdor lidhjen që hapi e para, dhe faqja Konfigurimi rendit opsionet e tij për proxy-t, autoritetet e certifikimit dhe cilësimet shtesë të cURL.
Çfarëdo që i lëviz bajtet, klienti vetë bën gjithçka tjetër: kredencialin, çelësat e idempotencës, riprovat dhe backoff-in e tyre, afatin e skadimit për çdo përpjekje dhe përjashtimin për çdo dështim. Një klient HTTP vetëm dërgon një kërkesë dhe kthen përgjigjen.
Çdo klient PSR-18
OpenEmail\Http\Psr18HttpClient mbështjell çdo klient që implementon PSR-18, si Guzzle ose Symfony HttpClient, ndaj kërkesat kalojnë përmes klientit që aplikacioni juaj e konfiguron tashmë, me middleware-in, regjistrimin në log dhe cilësimet e proxy-t të tij.
use GuzzleHttp\Client;use OpenEmail\Http\Psr18HttpClient;use OpenEmail\OpenEmail; $client = new OpenEmail(httpClient: new Psr18HttpClient(new Client())); $client->me->ping();use OpenEmail\Http\Psr18HttpClient;use OpenEmail\OpenEmail;use Symfony\Component\HttpClient\HttpClient;use Symfony\Component\HttpClient\Psr18Client; $http = new Psr18Client(HttpClient::create(['timeout' => 30, 'max_redirects' => 0])); $client = new OpenEmail(httpClient: new Psr18HttpClient($http)); $client->me->ping();PSR-18 i ndërton kërkesat e tij me fabrika PSR-17. Adapteri përdor vetë klientin kur ai është edhe fabrikë, siç është Psr18Client i Symfony-t, pastaj php-http/discovery kur është i instaluar, pastaj nyholm/psr7 ose fabrikat e vetë Guzzle-it, dhe hedh InvalidArgumentException duke përmendur çfarë duhet instaluar kur nuk gjen asnjë. Jepni requestFactory: dhe streamFactory: për t’i zgjedhur vetë. Psr18Client i Symfony-t ka nevojë edhe për ndërfaqet PSR-18 dhe për një implementim të PSR-17, ndaj instalojeni me composer require symfony/http-client psr/http-client nyholm/psr7.
use GuzzleHttp\Client;use Nyholm\Psr7\Factory\Psr17Factory;use OpenEmail\Http\Psr18HttpClient;use OpenEmail\OpenEmail; $factory = new Psr17Factory(); $client = new OpenEmail(httpClient: new Psr18HttpClient(new Client(), requestFactory: $factory, streamFactory: $factory)); $client->me->ping();| Çështja | Çfarë ndodh |
|---|---|
| Afatet e skadimit | Guzzle e merr timeout: të klientit në çdo kërkesë. PSR-18 nuk ka mënyrë për t’i kaluar një afat skadimi ndonjë klienti tjetër, ndaj vendosni një te klienti që mbështillni, siç bën HttpClient::create(['timeout' => 30, 'max_redirects' => 0]) më sipër, përndryshe një kërkesë që nuk përgjigjet kurrë mund të presë përgjithmonë. |
| Statuset e gabimit | Një klient PSR-18 i kthen përgjigjet 4xx dhe 5xx në vend që të hedhë përjashtim, dhe adapteri i thotë Guzzle-it të bëjë të njëjtën gjë, ndaj çdo refuzim bëhet prapë ApiException i duhur. |
| Pa përgjigje | Një ClientExceptionInterface nga klienti i mbështjellë bëhet një NetworkException që mban mesazhin e tij, por jo vetë përjashtimin, sepse ai mban kërkesën dhe header-in e saj Authorization. isTimeout() është true kur mesazhi thotë se kërkesës i skadoi afati, dhe për çdo klient përveç Guzzle-it mesazhi thotë se ishte afati i vetë klientit të mbështjellë. |
| Ridrejtimet | Guzzle-it i thuhet të mos i ndjekë, dhe shembulli i Symfony-t e vendos max_redirects në 0, ndaj një ridrejtim bëhet ApiException, ashtu si me klientin cURL të parazgjedhur. |
Klienti juaj
httpClient: pranon çdo gjë që implementon OpenEmail\Http\HttpClient, një ndërfaqe me një metodë të vetme: send(HttpRequest $request): HttpResponse. Ktheni përgjigjen për çdo status, përfshirë 4xx dhe 5xx, dhe hidhni përjashtim vetëm kur nuk mbërriti asnjë përgjigje.
| Klasa | Çfarë mbart |
|---|---|
| HttpRequest | method, url, headers si një array emrash dhe vlerash, body si string ose null, dhe timeout në sekonda, ose null kur nuk ka. header($name) lexon një header pa marrë parasysh shkronjat e mëdha a të vogla. var_dump(), print_r() dhe json_encode() e shfaqin header-in e saj Authorization si [redacted], por var_export() dhe dump() i Symfony-t i shtypin headers ashtu siç janë, ndaj mos e regjistroni kurrë një kërkesë me këto të dyja. |
| HttpResponse | Ndërtohet si new HttpResponse($status, $headers, $body). I kthen emrat e header-ave në shkronja të vogla, dhe header($name) lexon një të tillë pa marrë parasysh shkronjat e mëdha a të vogla. |
| HttpClientException | Hidheni nga send kur nuk mbërriti asnjë përgjigje. new HttpClientException($message, timeout: true) shënon një skadim afati. |
Testimi pa rrjet
Një klient imitim regjistron atë që do të kishte dalë dhe përgjigjet me çfarëdo që i duhet testit, ndaj asgjë nuk largohet nga makina dhe testi mund të kontrollojë saktësisht çfarë u dërgua.
use OpenEmail\Http\HttpClient;use OpenEmail\Http\HttpRequest;use OpenEmail\Http\HttpResponse;use OpenEmail\OpenEmail; $fake = new class implements HttpClient { public array $requests = []; public function send(HttpRequest $request): HttpResponse { $this->requests[] = $request; return new HttpResponse( 200, ['content-type' => 'application/json'], json_encode(['id' => 'msg_test', 'status' => 'sent', 'replayed' => false], JSON_THROW_ON_ERROR), ); }}; $testClient = new OpenEmail(apiKey: 'oe_test_fake', httpClient: $fake, maxRetries: 0, disableUpdateNotice: true); $sent = $testClient->emails->send([ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => 'Hi', 'text' => 'Hello',]); $request = $fake->requests[0]; echo $sent['status'], ' ', $request->method, ' ', $request->url, ' ', $request->header('Idempotency-Key'), PHP_EOL;var_dump($request);- Ktheni një status jashtë 2xx me zarfin e gabimit të API-së si trup, si p.sh.
{"error": {"type": "validation_error", "code": "invalid_parameter", "message": "..."}}, për të marrë nënklasën përkatëse tëApiException. - Hidhni
new HttpClientException('timed out', timeout: true)ngasendpër të marrë njëNetworkExceptionmeisTimeout()true. ÇdoExceptiontjetër, si njëRuntimeException, bëhet njëNetworkExceptionmeisTimeout()false. - Një
LogicExceptiondhe çdoError, si njëTypeError, llogariten si defekte të imitimit. Hidhen të pandryshuara dhe nuk riprovohen kurrë.
Ndërtojeni klientin e testimit me maxRetries: 0 kur programoni dështime. Përndryshe, një status i riprovueshëm ose një dështim rrjeti te një thirrje që mund të përsëritet pa rrezik provohet tri herë, me pritje të vërteta ndërmjet.