خطاها
هر شکستی raise میشود. یک کلاس برای رد شدن، یکی برای نرسیدن پاسخ، و یک شناسهٔ درخواست روی هر خطای API.
گرفتن یک خطا
message = {from: "[email protected]", to: "[email protected]", subject: "Your September invoice", text: "Invoice attached."} begin client.emails.send(message)rescue OpenEmail::ApiError => error warn "#{error.code} #{error.param} #{error.message}" if error.validation? warn client.addresses.list_all.addresses.inspect if error.permission? warn "try again in #{error.retry_after_seconds} seconds" if error.rate_limited? warn "#{error.status} #{error.request_id}" raiserescue OpenEmail::NetworkError => error warn "no answer in time" if error.timeout? raiseendیک permission_error روی ارسال معمولاً به اسکوپ ارسالِ خودِ کلید برمیگردد، به دامنه یا آدرسی که به آن داده نشده، نه به فضای کاری، و به همین دلیل است که نمونه چاپ میکند addresses.list_all میگوید این کلید از چه آدرسهایی میتواند بفرستد.
هر نوع رد شدن زیرکلاس خودش را دارد، پس یک rescue میتواند آنهایی را که رسیدگی میکند بر اساس کلاس انتخاب کند و بگذارد بقیه به بالا بروند.
message = {from: "[email protected]", to: "[email protected]", subject: "Your September invoice", text: "Invoice attached."} begin client.emails.send(message)rescue OpenEmail::ValidationError => error warn "#{error.param}: #{error.message}"rescue OpenEmail::AuthenticationError, OpenEmail::PermissionError => error warn "the key cannot do this: #{error.code}" raiserescue OpenEmail::Error => error warn "#{error.class}: #{error.message}" raiseendکلاسها
| کلاس | چه زمانی |
|---|---|
| OpenEmail::Error | پایهٔ همهٔ خطاهایی که gem تعریف میکند، پس rescue OpenEmail::Error همهٔ آنها را میگیرد. ArgumentError را نمیگیرد. |
| OpenEmail::ApiError | API پاسخ داده است، اما نه با موفقیت. حامل status، type، code، param، doc_url، request_id، retry_after_seconds، fields و body است. وقتی type برابر api_error باشد، مانند خطای سرور، با همین کلاس raise میشود، و در غیر این صورت با زیرکلاسِ متناظر با type آن. |
| OpenEmail::InvalidRequestError، AuthenticationError، PermissionError، NotFoundError، ConflictError، ValidationError و RateLimitError | زیرکلاسهای ApiError، یکی برای هر type: invalid_request_error، authentication_error، permission_error، not_found_error، conflict_error، validation_error و rate_limit_error. |
| OpenEmail::NetworkError | هیچ پاسخی نرسیده است: DNS، TLS، اتصالی که رد یا قطع شده، یا پایان مهلت. حامل original است، یعنی استثنای زیرین که cause آن هم هست، و وقتی دلیل، پایان مهلت بوده باشد timeout? برابر true است. |
| OpenEmail::WebhookSignatureError | OpenEmail.verify_webhook_signature یک تحویل را رد کرده است. |
| ArgumentError | پیش از آنکه چیزی فرستاده شود raise میشود: کلیدِ نبوده یا بدشکل، یک base_url: غیرقابلاستفاده، یک شناسهٔ خالی. همان کلاس سادهٔ Ruby است، نه یک OpenEmail::Error، چون یعنی خودِ فراخوانی نادرست است. |
آنچه یک ApiError با خود دارد
messageString- جملهٔ خودِ API، نوشتهشده برای آدم، که مقدار خطاساز را در جایی که وجود دارد نام میبرد. شناسهای پایدار نیست، پس روی `code` شاخه بزنید.
statusInteger- وضعیت HTTP پاسخ.
typeString- یکی از هشت مقدار در `OpenEmail::ERROR_TYPES`، مجموعهای که ثابت است و بزرگتر نخواهد شد. وقتی بدنه هیچکدام را نام نبرد، از وضعیت استنتاج میشود.
codeString- شکست مشخص، مانند `from_address_forbidden` یا `invalid_email_address`. باز و افزودنی است، پس با کدی که نمیشناسید مثل `type` خودش رفتار کنید. وقتی بدنه پاکت خطای API نبوده باشد، برابر `unrecognised_response` است.
paramString or nil- فیلدی که رد شده، بهصورت مسیری نقطهدار مانند `to.0`، وقتی شکست فیلدی را نام ببرد.
doc_urlString or nil- صفحهای دربارهٔ این شکست، وقتی API صفحهای را نام ببرد.
request_idString or nil- شناسهای که سرور درخواست را با آن در لاگ ثبت کرده، از بدنه یا از سرآیند `x-request-id`.
retry_after_secondsInteger, Float or nil- انتظاری که سرور در `Retry-After` خواسته، برحسب ثانیه، چه عدد فرستاده باشد چه تاریخ. وقتی چیزی نفرستاده باشد nil است.
fieldsArray<Hash> or nil- یک Hash برای هر مشکل، هرکدام با `key` و `error`، مانند `{key: "email", error: "email"}` وقتی `forms.subscribe` پاسخها را با یک 422 `invalid_form_submission` رد کرده باشد. وقتی خطا هیچ موردی را فهرست نکند nil است.
bodyHash or nil- کل پاسخ خطا، تجزیهشده، با کلیدهای Symbol. وقتی خالی بوده یا JSON نبوده nil است.
| متد پرسشی | چه زمانی true است |
|---|---|
| auth? | type برابر authentication_error است، یک 401 Unauthorized: نبودن کلید، نوع نادرست اعتبارنامه، یا کلیدی که ما صادرش نکردهایم. |
| permission? | permission_error، یک 403 Forbidden: کلیدی واقعی بدون اسکوپ یا بدون آدرس From مورد نیازش. |
| scope_missing? | code برابر insufficient_scope است، همان 403 Forbidden که اسکوپ غایب را نام میبرد. |
| invalid_request? | invalid_request_error، یک 400: درخواستی که قابل فهم نبوده است. پیامی فراتر از سقف اندازه بهصورت یک 422 با message_too_large برمیگردد، پس validation? متد پرسشیای است که آن را میگیرد. |
| validation? | validation_error، یک 422: اسکیما آن را نپذیرفته است و param نام فیلد را میگوید. |
| not_found? | not_found_error، یک 404 Not Found: چنین منبعی وجود ندارد. |
| conflict? | conflict_error، یک 409 Conflict: منبع از نقطهای گذشته است که بتوان این کار را روی آن انجام داد. |
| rate_limited? | rate_limit_error، یک 429. وقتی سرور مدتی را نام برده باشد، retry_after_seconds آن انتظار را نگه میدارد. |
| server_error? | status برابر 500 یا بالاتر است. اگر با پشتیبانی تماس گرفتید، request_id را ذکر کنید. |
| retryable? | status یکی از 408، 429، 500، 502، 503 یا 504 است. |
| step_up_required? | code برابر step_up_required است، همان 403 که توکن دسترسی OAuth پیش از یک تغییر حساس میگیرد تا وقتی شخص کدی را تأیید کند. |
بیشتر متدهای پرسشی type را میخوانند، یعنی نیمهٔ ثابتِ پاکت، و هر زیرکلاس نمایندهٔ یک type است. code یک String باقی میماند، چون API تضمین میکند که باز و افزودنی است، پس با کدی که نمیشناسید مطابق type آن رفتار کنید. یک فهرست بسته، بهای خواندن یک حالت شکست تازه را به ارتقای gem تبدیل میکرد.
بدنهای که پاکت خطای API نباشد باز هم یک OpenEmail::ApiError را raise میکند، با type استنتاجشده از وضعیت و code برابر unrecognised_response. پاسخ موفقی که بدنهاش JSON نباشد نیز همین خطا را raise میکند.
retryable? وضعیت را توصیف میکند، نه فراخوانی شما را. فراخوانیای که تکرارش بیخطر است، تا زمانی که خطا raise کند دوباره تلاش شده است، و یک 429 مانند send_quota_exceeded یا ai_quota_exceeded تا وقتی سهمیهاش بازنشانی نشود به همان شکل شکست میخورد، پس آن را به یک شخص نشان دهید، نه اینکه روی آن حلقه بزنید.
استثنایی که آداپتور شما raise کند، پس از مصرف شدن تلاشهای دوبارهای که فراخوانی اجازه میدهد، به یک OpenEmail::NetworkError تبدیل میشود که استثنای اصلی روی original و cause آن است. NameError، TypeError و ArgumentError مستثنا هستند: یعنی باگی در آداپتور، پس بیتغییر raise میشوند و هرگز دوباره تلاش نمیشوند.
request_id
هر OpenEmail::ApiError شناسهٔ درخواستی را که سرور فرستاده است با خود دارد، از بدنهٔ خطا یا از سرآیند x-request-id، و تنها چیزی است که شکست شما را به یک سطر در لاگ سرور گره میزند. یک پاسخ موفق فقط بدنهٔ تجزیهشده را برمیگرداند، پس روی آن شناسهٔ درخواستی برای خواندن وجود ندارد.