त्रुटियाँ
हर विफलता throw होती है। अस्वीकार के लिए एक क्लास, जवाब न आने के लिए एक, और हर API error पर एक request id।
इसे पकड़ना
use OpenEmail\Exception\ApiException;use OpenEmail\Exception\NetworkException; $message = [ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => 'Your September invoice', 'text' => 'Invoice attached.',]; try { $client->emails->send($message);} catch (ApiException $error) { if ($error->isValidation()) { error_log($error->errorCode . ' ' . $error->param . ' ' . $error->getMessage()); } if ($error->isPermission()) { $book = $client->addresses->listAll(); error_log('this key may send as ' . implode(', ', array_column($book->addresses, 'address'))); } if ($error->isRateLimited()) { error_log('try again in ' . $error->retryAfterSeconds . ' seconds'); } error_log($error->status . ' ' . $error->requestId); throw $error;} catch (NetworkException $error) { if ($error->isTimeout()) { error_log('no answer in time'); } throw $error;}किसी send पर आया permission_error आमतौर पर वर्कस्पेस का नहीं बल्कि कुंजी के send scope का मामला होता है, यानी कोई डोमेन या पता जो उसे दिया ही नहीं गया, इसीलिए यह नमूना वह log करता है जो addresses->listAll() बताता है कि यह कुंजी किस पते से भेज सकती है।
हर तरह के अस्वीकार की अपनी subclass है, इसलिए एक catch क्लास के आधार पर उन्हें चुन सकता है जिन्हें वह संभालता है और बाक़ी को ऊपर जाने दे सकता है।
use OpenEmail\Exception\AuthenticationException;use OpenEmail\Exception\OpenEmailException;use OpenEmail\Exception\PermissionException;use OpenEmail\Exception\ValidationException; $message = [ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => 'Your September invoice', 'text' => 'Invoice attached.',]; try { $client->emails->send($message);} catch (ValidationException $error) { error_log($error->param . ': ' . $error->getMessage());} catch (AuthenticationException|PermissionException $error) { error_log('the key cannot do this: ' . $error->errorCode); throw $error;} catch (OpenEmailException $error) { error_log($error::class . ': ' . $error->getMessage()); throw $error;}क्लास
हर क्लास OpenEmail\Exception में है।
| क्लास | कब |
|---|---|
| OpenEmailException | वह interface जिसे पैकेज द्वारा throw होने वाला हर exception implement करता है, इसलिए catch (OpenEmailException $error) उन सभी को catch करता है, InvalidArgumentException समेत। |
| ApiException | API ने जवाब दिया, पर सफलता के साथ नहीं। इसमें status, type, errorCode, param, docUrl, requestId, retryAfterSeconds, fields और body होते हैं। जब type का मान api_error हो, जैसा सर्वर की गड़बड़ी में होता है, तो यह ख़ुद throw होता है, और बाकी मामलों में उसके type की subclass के रूप में। यह RuntimeException को extend करता है। |
| InvalidRequestException, AuthenticationException, PermissionException, NotFoundException, ConflictException, ValidationException और RateLimitException | ApiException की subclasses, हर type के लिए एक: invalid_request_error, authentication_error, permission_error, not_found_error, conflict_error, validation_error और rate_limit_error। |
| NetworkException | कोई जवाब नहीं आया: DNS, TLS, अस्वीकार या टूटा हुआ कनेक्शन, या टाइमआउट। getPrevious() में नीचे का exception होता है, और जब कारण टाइमआउट हो तो isTimeout() true होता है। यह RuntimeException को extend करता है। |
| WebhookSignatureException | OpenEmail::verifyWebhookSignature() ने किसी delivery को अस्वीकार किया। यह UnexpectedValueException को extend करता है। |
| InvalidArgumentException | कुछ भी भेजे जाने से पहले throw होता है: ग़ायब या ग़लत रूप वाली कुंजी, इस्तेमाल न किया जा सकने वाला baseUrl:, ख़ाली id। यह PHP की अपनी InvalidArgumentException को extend करता है, क्योंकि इसका मतलब है कि कॉल ख़ुद ग़लत है। |
ApiException में क्या होता है
getMessage()string- API का अपना वाक्य, किसी व्यक्ति के लिए लिखा गया, और जहाँ कोई ग़लत मान हो वहाँ उसका नाम लेता हुआ। यह स्थिर पहचानकर्ता नहीं है, इसलिए `errorCode` पर branch करें।
statusint or null- जवाब का HTTP status, जिसे `getCode()` भी लौटाता है। null सिर्फ़ तब होता है जब कोई सफल जवाब ऐसे रूप में आया हो जिसे क्लाइंट पढ़ नहीं सका।
typestring- `OpenEmail\Constants\ErrorTypes` के आठ मानों में से एक, एक ऐसा समूह जो तय है और बढ़ेगा नहीं। जब बॉडी कोई मान नहीं बताती, तो इसे status से अनुमानित किया जाता है।
errorCodestring- विशिष्ट विफलता, जैसे `from_address_forbidden` या `invalid_email_address`। इसका नाम `errorCode` है क्योंकि PHP `code` को उस संख्या के लिए रखता है जो `getCode()` लौटाता है। यह खुला है और इसमें नए मान जुड़ सकते हैं, इसलिए जिसे आप न पहचानें उसे उसके `type` की तरह मानें। जब बॉडी API का error envelope नहीं थी, तब यह `unrecognised_response` होता है।
paramstring or null- वह फ़ील्ड जिसे अस्वीकार किया गया, `to.0` जैसे बिंदु वाले path के रूप में, जब विफलता किसी का नाम लेती है।
docUrlstring or null- इस विफलता के बारे में एक पेज, जब API किसी का नाम लेता है।
requestIdstring or null- वह id जिसके तहत सर्वर ने रिक्वेस्ट लॉग की, बॉडी से या `x-request-id` हेडर से।
retryAfterSecondsint, float or null- सर्वर ने `Retry-After` में जितना इंतज़ार माँगा, सेकंड में, चाहे उसने संख्या भेजी हो या तारीख़। जब उसने कुछ नहीं भेजा तो null।
fieldsarray or null- हर समस्या के लिए एक array, हर एक में `key` और `error`, जैसे `['key' => 'email', 'error' => 'email']` जब `forms->subscribe` ने जवाबों को 422 `invalid_form_submission` के साथ अस्वीकार किया हो। जब error कोई समस्या सूचीबद्ध न करे तो null।
bodymixed- पूरा error जवाब, डिकोड किया हुआ। जब वह ख़ाली था या JSON नहीं था तो null।
| मेथड | true कब |
|---|---|
| isAuth() | type authentication_error है, यानी 401: कोई key नहीं, गलत किस्म का क्रेडेंशियल, या ऐसी key जो हमने जारी नहीं की। |
| isPermission() | permission_error, यानी 403: असली key, पर उसके पास ज़रूरी scope या From पता नहीं। |
| isScopeMissing() | errorCode का मान insufficient_scope है, यानी वह 403 जो किसी ग़ायब scope का नाम लेता है। |
| isInvalidRequest() | invalid_request_error, एक 400: ऐसी रिक्वेस्ट जिसे समझा नहीं जा सका। आकार की सीमा से बड़ा संदेश 422 message_too_large के रूप में लौटता है, इसलिए उसे पकड़ने वाला मेथड isValidation() है। |
| isValidation() | validation_error, यानी 422: schema ने इसे अस्वीकार किया, और param उस फ़ील्ड का नाम बताता है। |
| isNotFound() | not_found_error, यानी 404: ऐसा कोई संसाधन नहीं। |
| isConflict() | conflict_error, यानी 409: संसाधन उस बिंदु से आगे निकल चुका है जहाँ उसके साथ यह किया जा सकता था। |
| isRateLimited() | rate_limit_error, यानी 429। जब सर्वर प्रतीक्षा बताता है तो वह retryAfterSeconds में होती है। |
| isServerError() | status 500 या उससे ऊपर है। सपोर्ट से संपर्क करें तो requestId बताएँ। |
| isRetryable() | status 408, 429, 500, 502, 503 या 504 है। |
| isStepUpRequired() | errorCode का मान step_up_required है, यानी वह 403 जो किसी संवेदनशील बदलाव से पहले OAuth access टोकन को तब तक मिलता है जब तक व्यक्ति कोई कोड सत्यापित न कर दे। |
इनमें से ज़्यादातर type पढ़ते हैं, जो envelope का तय हिस्सा है, और हर subclass एक type के लिए है। errorCode एक स्ट्रिंग ही रहता है, क्योंकि API गारंटी देता है कि यह खुला है और इसमें नए मान जुड़ सकते हैं, इसलिए जिसे आप न पहचानें उसे उसके type की तरह मानें। बंद सूची होने पर किसी नए विफलता प्रकार को पढ़ने की क़ीमत पैकेज अपग्रेड होती।
जो बॉडी API का error envelope नहीं है, वह भी एक ApiException throw करती है, जिसमें type status से अनुमानित होता है और errorCode का मान unrecognised_response होता है। जिस सफल जवाब की बॉडी JSON नहीं है, वह भी इसे throw करता है।
isRetryable() status का वर्णन करता है, आपकी कॉल का नहीं। दोहराने में सुरक्षित कॉल throw होने तक पहले ही दोबारा आज़माई जा चुकी होती है, और send_quota_exceeded या ai_quota_exceeded जैसा 429 तब तक उसी तरह विफल होता रहता है जब तक उसकी सीमा रीसेट न हो, इसलिए उस पर लूप चलाने के बजाय उसे किसी व्यक्ति को दिखाएँ।
आपके HTTP क्लाइंट द्वारा throw किया गया exception, कॉल को मिले सभी retry ख़त्म होने के बाद NetworkException बन जाता है, और मूल exception getPrevious() के रूप में मिलता है। LogicException और कोई भी Error, जैसे TypeError, उसमें किसी bug का संकेत हैं, इसलिए वे बिना बदले throw होते हैं और कभी retry नहीं होते। Psr18HttpClient लपेटे गए क्लाइंट के exception का सिर्फ़ संदेश रखता है, क्योंकि उस exception में रिक्वेस्ट और उसका Authorization हेडर होता है।
requestId
हर ApiException में सर्वर द्वारा भेजी गई request id होती है, error बॉडी से या x-request-id हेडर से, और यही एकमात्र चीज़ है जो आपकी विफलता को सर्वर के log की किसी पंक्ति से जोड़ती है। सफल जवाब सिर्फ़ डिकोड की गई बॉडी लौटाता है, इसलिए उस पर पढ़ने के लिए कोई request id नहीं होती।