
ساختن API کار سختی به نظر نمیآید، تا اینکه شروع به فکرکردن در مورد برگرداند ریسپاسن ها یا خطا ها میکنی.
ما در HTTP یک سری status code داریم که بخشی از آنها نشاندهندهی خطاهای عمومی هستند. امّا مشکل اینجا است که برای کاربردهای واقعی، این چندتا شناسهی خطای محدود و کلّی کافی نیستند. از طرفی اگر مجبور به استفاده از API های دیگران بشوید، میبینید که اکثر اوقات پیام خطایی که برمیگردد هیچ کمکی به شما برای فهمیدن اینکه چطوری میتوان مشکل را حل کرد نمیکند.
در این نوشته میخواهیم راه حل استانداردی که از سال ۲۰۱۶ معرّفی شده ولی هنوز خیلیها ازش استفاده نمیکننند را یادبگیریم.
در حال حاضر هرکسی برای خودش روشی را پیشگرفته و خطاها را به همان روش برمیگرداند. شاید با خودتان فکرکنید که همین که یک مدل انتخاب شود و به همان پایبند بمانیم همهچیز خوب خواهد بود و مشکلی پیش نخواهد آمد. امّا زندگی به همین سادگیها نیست.
اگر همان یک روش، روش ناکارامدی باشد، پایبندی به آن تنها زجرکشیدن ما را طولانیتر میکند. به علاوه ما خیلی وقتها مجبوریم که از API های مختلف استفاده کنیم. حالا اگر هرکدام از اینها برای خودشان یک ساختار را در پیش بگیرند، حتّی اگر آن ساختار خوب هم باشد، باز مصرفکننده باید برای میلیاردها استاندارد مختلف کد مجزا تولید کند. بیایید قبل از اینکه سراغ روش استاندارد برویم، در اینجا دو مشکل اساسی نبود استاندارد برای توصیف خطا در API ها را ببینیم.
این ساختار پیام خطای استاندارد گوگل است:
{ "error": { "errors": [ { "domain": "global", "reason": "invalidParameter", "message": "Invalid string value: 'asdf'. Allowed values: [mostpopular]", "locationType": "parameter", "location": "chart" } ], "code": 400, "message": "Invalid string value: 'asdf'. Allowed values: [mostpopular]" } }
و این هم ساختار پیام خطای استاندارد فیسبوک:
{ "error": { "message": "Invalid OAuth access token.", "type": "OAuthException", "code": 190, "error_subcode": 1234567, "fbtrace_id": "BLBz/WZt8dN" } }
همانطوری که میبینید حتّی در یک فیلد هم با هم اشتراک ندارند. این تفاوت زیاد در روشهای مختلف، توسعهی نرمافزارهایی را که به API های مختلف وابستهاند را به شدّت سخت میکند.
خبر خوش این است که در سال ۲۰۱۶ سازمان فخیمهی Internet Engineering Task Force منّت بر دیدگان توسعهدهندگان سراسر جهان گذاشت و با انتشار RFC 7807 یک روش استاندارد و خیلی مناسب را برای توضیح مشکل در API های HTTP ارائه کرد. با استفاده از این استاندارد، میتوان مشکلات و خطاها را به شکلی در API مشخّص کرد که هم انسانها که کاربر نهایی هستند بتوانند به بهترین شکل متوجّه دلیل خطا بشوند و هم نرمافزارها بتوانند به راحتی خطاها را مدیریت کنند. حالا بیایید با هم ببینم که این استاندارد چه میگوید.
طبق این استاندارد ما میتوانیم خطاها را به شکل یک شئ JSON یا یک مستند XML توصیف کنیم. هرچند که استفاده از JSON ترجیح بیشتری دارد و در این نوشته هم ما تنها به آن میپردازیم. پیامهای HTTP که حامل توصیف مشکل هستند و از این استاندارد پیروی میکنند، باید Content-Type شان یکی از مقادیر: application/problem+json یا application/problem+xml باشد.
اینطوری از همان ابتدای پیام مشخّص است که قرار است با یک خطا که طبق این استاندارد ساخته شده است کار کنیم. مثلاً فرضکنید که ما یک وبسایت مدیریت دروس برای دانشگاه داریم. در این وبسایت یک API وجود دارد که میتوان با فرستادن متد GET به آن لیست تکالیف هفتهی بعد را گرفت.
بگذارید بگوییم آدرس این API این است:
https://university.xyz/api/2/course/<id>/assignments
خب حالا فرضکنید که این API به افرادی که در این درس ثبت نام نکردهاند خطا برمی گرداند. از اینجا به بعد بخشهای مختلف استاندارد توصیف خطا را روی همین مثال جلو میبریم. خب حالا ما میخواهیم به کاربری که در درس مهندسی اینترنت ثبت نام نکرده ولی درخواست گرفتن لیست تکالیف آن را دارد خطا نشان بدهیم (فرضکنید که id این درس عدد ۱ است). پاسخی که کاربر به عنوان یک پیام HTTP میگیرد چیزی شبیه به این است (بخش header میتواند خیلی بیشتر باشد ولی چون اکثر بخشهایش به این نوشته مربوط نیستند، آنها را نادیده میگیریم):
HTTP/1.1 403 Forbidden Content-Type: application/problem+json; charset=UTF-8 Content-Language: fa
خب حالا ببینیم که چطوری محتوای مرتبط با این پیام را میتوانیم بسازیم.
هر خطا میتواند بخشهای مختلفی داشته باشد که وجود هیچکدام از آنها اجباری نیست. امّا چند بخش آن به صورت صریح درون استاندارد مشخّص شده اند. یکی از بخشهای خیلی مهم یک پیام خطا، نوع خطا است. نوع خطا با کلید type در شئ json ما قرار میگیرد. در استانداردی که درون این RFC مشخّص شده است، ما نوع خطا را به جای استفاده از مقادیر عددی یا رشتهها، با یک URI مشخّص میکنیم. این آدرس باید به یک صفحهی HTML ختم بشود که درون آن توضیحات کامل درمورد این خطا و روش حل آن نوشته شده باشد. ما header پاسخ HTTP را در بخش قبل دیدیم و از اینجا به بعد دیگر آن را تکرار نمیکنیم. حالا محتوایی که آن پاسخ HTTP با خودش حمل میکند چیزی شبیه به این میشود:
{ "type": "https://university.xyz/api/2/errors/course-not-accessible" }
خب پیام خطای ما تا اینجا این شکلی است.
حالا اگر کاربر آدرسی که برای type وارد کردهایم را درون مرورگرش وارد کند، باید به صفحهای برسد که به صورت کامل توضیح داده که این خطا برای چه به وجود میآید و برای حل آن چه کاری میتوان کرد.
مثلاً در این مورد باید توضیح بدهد که کاربران تنها به لیست تکالیف دروسی که در آن شرکت میکنند دسترسی دارند و بگوید که برای ثبت نام در درس، کاربر باید چه کاری انجام بدهد.
یکی دیگر از بخشهای خطا، عنوان آن است. عنوان با کلمهی کلیدی title درون شئ خطا مشخّص میشود. عنوان خطا یک متن کوتاه و قابل فهم برای انسانها است که باید برای تمام خطاهایی که نوع یکسانی دارند، مشابه باشد. مگر اینکه سایت شما کاربرانی با زبانهای مختلف داشته باشد و بخواهید عنوان خطا را برای زبانهای مختلف ترجمه کنید.
{ "type": "https://university.xyz/api/2/errors/course-not-accessible", "title": "شما اجازهی دسترسی به این درس را ندارید." }
شناسهی خطا همان status code پیامهای HTTP است. یعنی چیزی نیست که درون شئ json ما قرار بگیرد. هدف از اشاره به آن صرفاً این است که حواستان باشد که باید مرتبطترین status code را به خطایی که رخ داده به عنوان http status code انتخاب کنید. مثلاً در مثالی که ما در این نوشته داریم روی آن کار میکنیم، status را برابر ۴۰۳ گذاشتهایم. چون کاربر اجازهی دسترسی به این بخش را ندارد. یک چیز دیگری که باید حواستان به آن باشد این است که اگر در پاسخی که دارید برمیگردانید به بیش از یک مشکل اشاره شده است (در بخشهای بعدی میبینیم که چطوری میتوان این کار را کرد) باید از شناسهی 207 استفاده کنید.
بخش دیگری که میتواند درون شئ خطا قرار بگیرد مقدار detail است. در این بخش ما باید یک توضیح درمورد مشکلی خاصی که الان پیش آمده است بدهیم. مثلاً در مثال ما، باید توضیح بدهیم که چرا کاربر نمیتواند لیست تکالیف را ببیند:
{ "type": "https://university.xyz/api/2/errors/course-not-accessible", "title": "شما اجازهی دسترسی به این درس را ندارید.", "detail": "شما امکان دریافت لیست تکالیف درس مهندسی اینترنت را ندارید. چون این درس در لیست دروس ثبت نامی شما قرار ندارد." }
استاد : دکتر مریم حاجی اسمعیلی. دکترای علوم کامپیوتر از دانشگاه کینگستون لندن
Dr.Maryam Hajiesmaeili
PhD of computer science from Kingston university of London