HTTP در وب: درخواست، پاسخ و پیادهسازی
منبع: Coding Clean, Reliable, and Safe REST APIs with ASP.NET Core 8 — Anthony Giretti
اعتبار ترجمه: ترجمه با کمک هوش مصنوعی
فروش یا انتشار این ترجمه منوط به داشتن مجوز لازم از صاحب حقوق اثر است.
فصل ۱: معرفی HTTP و REST
پیش از آنکه وارد ASP.NET Core 8 و توسعهٔ API شویم، ابتدا به مبانی هر برنامهٔ وب برمیگردیم. چه یک وبسایت از مرورگر اجرا شود و چه یک سرویس وب (Web API)، اصل کار یکسان است: یک کلاینت و یک سرور با یکدیگر ارتباط برقرار میکنند؛ کلاینت درخواستی برای سرور میفرستد و سرور به آن پاسخ میدهد. همهٔ این فرایند با پروتکل ارتباطی HTTP امکانپذیر است. در چارچوب این پروتکل، داده میتواند با قالبها و محدودیتهای گوناگون منتقل شود. اینجا REST وارد میشود. REST یک مفهوم معماری برای نمایش داده است و البته نباید این دو را با هم اشتباه گرفت. در این فصل دو موضوع زیر را بررسی میکنیم:
آشکار کردن HTTP در پشت وب
HyperText Transfer Protocol یا HTTP یک پروتکل شبکه برای تبادل داده میان کلاینتها و سرورها است. این پروتکل در سال ۱۹۹۰ بهوسیلهٔ دانشمند رایانهٔ بریتانیایی Tim Berners-Lee برای دسترسی به World Wide Web (WWW) ابداع شد. WWW امکان مشاهدهٔ صفحات وب را از طریق مرورگر، با استفاده از HyperText Markup Language (HTML) و نشانیهای وب از نوع Uniform Resource Identifier (URI)، فراهم میکند.
doi.org/10.1007/978-1-4842-9979-1_1
در آغاز تاریخ HTTP، HTML زبانی بود که برای ساخت صفحهها استفاده میشد، اما HTTP از آن زمان تکامل یافته است و وبسرور میتواند قالبهای دادهای دیگری غیر از HTML را نیز پردازش کند. برای نمونه، یک وبسرور میتواند Extensible Markup Language (XML) را که زبانی ساختاریافته است یا JavaScript Object Notation (JSON) را ارائه کند و همچنین آنها را بهعنوان ورودی بپذیرد. وبسرور میتواند انواع دیگری از قالبهای داده را نیز ارائه کند که تحت عنوان Multipurpose Internet Mail Extensions (MIME) type دستهبندی میشوند؛ در ادامه دوباره به این موضوع بازمیگردم.
HTTP از مشخصات فنیای با عنوان Request for Comments (RFC) پیروی میکند که توسط Internet Engineering Task Force (IETF) تدوین میشوند. RFCهای بسیار زیادی وجود دارند که با شماره شناسایی میشوند. وجه مشترک آنها این است که مشخصات مربوط به اینترنت را تعریف میکنند. HTTP در RFC 7231 تعریف شده است. این RFC در نشانی متنی www.rfc-editor.org/rfc/rfc7231 در دسترس است.
HTTP نسخههای مختلفی هم دارد و در طول زمان تکامل یافته است. وارد جزئیات تاریخچه نمیشوم؛ نسخههای منتشرشده عبارتاند از:
- HTTP/0.9 — منسوخ
- HTTP/1.0 — منسوخ
- HTTP/1.1 — همچنان استفاده میشود
- HTTP/2 — در حال استفاده است، ولی همهگیر نیست
- HTTP/3 — جدید و هنوز کماستفاده
در این کتاب عمدتاً به HTTP/1.1 و گاهی، هنگام بررسی Performance (کارایی)، به HTTP/2 و HTTP/3 اشاره میکنم.
ویژگیهای HTTP
HTTP سه ویژگی اساسی دارد:
- Stateless (بدون حالت) است: پس از ارسال درخواست به سرور و دریافت پاسخ، نه کلاینت و نه سرور اطلاعاتی دربارهٔ درخواست مبادلهشده نزد خود نگه نمیدارند.
- Connectionless (بدون اتصال پایدار) است: میان کلاینت و سرور یک اتصال HTTP باز میشود. پس از دریافت پاسخ سرور، اتصال بسته میشود و ارتباط بین دو سامانه دائمی نیست.
- از رسانه مستقل است: سرور میتواند هر نوع رسانهای را منتقل کند، به شرط آنکه کلاینت و سرور دربارهٔ قالب محتوای مبادلهشده «توافق» داشته باشند. هنگام بحث دربارهٔ Headerها به این موضوع برمیگردم.
با وجود Stateless بودن HTTP، گاهی انتقال اطلاعات میان درخواستها لازم است. بیشتر برنامههای وب باید بتوانند یک کاربر را در طول یک نشست مرور شناسایی کنند؛ یعنی همان کاربر را هنگام جابهجایی بین چند صفحهٔ وب تشخیص دهند. برای این کار RFC 6265، HTTP Cookieها را توصیف میکند که برای نگهداری دادهٔ کاربر در سمت مرورگر طراحی شدهاند. در این کتاب وارد این نوع «ماندگاری» نمیشوم و از روشهای جدیدتری استفاده خواهم کرد. در صورت علاقه، RFC 6265 در www.rfc-editor.org/rfc/rfc6265 قابل مطالعه است.
این ویژگیها ممکن است در ابتدا انتزاعی به نظر برسند، اما با ادامهٔ کتاب روشنتر خواهند شد. در بخش بعد نمایی کلی از درخواستها و پاسخهای HTTP ارائه میکنم تا پیش از ورود به جزئیات، سازوکار HTTP را بهتر درک کنید.
درخواستها و پاسخهای HTTP
یک اتصال HTTP به این صورت کار میکند: هر درخواست، تا زمانی که اتصال قطع نشود، یک پاسخ دریافت میکند. ساختار کلی همهٔ درخواستها و پاسخها مشابه است و در بخش بعد جزئیات بیشتری ارائه خواهم کرد.
یک درخواست HTTP از اجزای زیر تشکیل میشود:
- یک کلاینت، مانند مرورگر یا برنامه، با فراخوانی یک URI درخواست HTTP را آغاز میکند. URI نشانی منبع موردنظر روی سرور است.
- برای URI از یک Verb (فعل HTTP) استفاده میشود که عملی را که باید انجام شود مشخص میکند.
- Metadata (فراداده) در قالب Headerها همراه درخواست HTTP ارسال میشود. Headerها امکان کنترل محتوای مورد مذاکره با سرور، ارسال اطلاعات احراز هویت و موارد بسیار دیگر را فراهم میکنند.
- برای مبادلهٔ محتوا با سرور و دریافت پاسخ موردنظر، Parameterها لازماند. این پارامترها میتوانند در Body درخواست، Route یا URI قرار بگیرند.
یک پاسخ HTTP نیز از اجزای زیر تشکیل میشود:
- سرور یک پاسخ را با HTTP Status Code (کد وضعیت HTTP) برمیگرداند تا نتیجهٔ پردازش درخواست مشخص شود.
- سرور Headerهایی را نیز در پاسخ برای ارائهٔ فرادادههای گوناگون به کلاینت برمیگرداند.
- در نهایت، سرور ممکن است Payload (بار داده) را در MIME type درخواستی کلاینت برگرداند یا اصلاً Payload نداشته باشد.
تا اینجا سازوکار HTTP را بهشکل خلاصه و ساده توضیح دادم. شکل ۱-۱ آنچه را تاکنون بررسی کردهایم جمعبندی میکند.
شکل ۱-۱. یک درخواست HTTP پایه و پاسخ آن
در ادامه، Verbهای HTTP، Headerهای درخواست، قالب URI، پارامترهای مختلف ارسالشده در درخواست، کدهای وضعیت HTTP، Headerهای پاسخ و قالب Payload برگشتی به کلاینت را با جزئیات بررسی میکنم. پس از اتمام این موارد، شکل ۱-۱ را با جزئیات بیشتری تکمیل خواهیم کرد.
پیادهسازی HTTP
اکنون عمیقتر میشویم تا ببینیم Verbهای HTTP، Headerهای درخواست و پاسخ، کدهای وضعیت HTTP و شیوهٔ ارسال پارامترهای کلاینت در درخواستهای HTTP، همراه با فراخوانی URI، چگونه کار میکنند.
Verbهای HTTP
RFC 7231 Verbهای زیر را تعریف میکند:
- GET: شناختهشدهترین Verb است. برای درخواست یک منبع از سرور و دریافت پاسخ در قالب دلخواه بهکار میرود؛ قالب پاسخ را Headerها تعیین میکنند. پاسخ GET قابلیت Cache شدن دارد. بعداً دربارهٔ Cache بحث میکنیم.
- HEAD: شبیه GET است، اما Payload برنمیگرداند و برای درخواست Metadata دربارهٔ نشانی موردنظر استفاده میشود. چون معمولاً توسعهدهندگان بهندرت از آن استفاده میکنند، در این کتاب از آن استفاده نخواهم کرد؛ بااینحال دانستن کاربردش مفید است. پاسخ سرور همانند GET قابلیت Cache شدن دارد.
- POST: چند کاربرد دارد. با آن میتوان منبع جدید ایجاد کرد و Payload در Body درخواست قرار میگیرد. بعداً Body درخواست را دقیقتر توضیح میدهم. راه دیگر ارسال داده، تکنیک form-data است که در ادامهٔ کتاب بررسی میشود. POST همچنین اجازه میدهد داده با افزودن محتوا به نمایش فعلی منبع تغییر کند. طبق RFC، این کار به معنای Append کردن داده به Resource Representation است. پاسخ سرور بهطور پیشفرض Cache نمیشود، مگر آنکه اطلاعات Freshness مانند Headerهای
max-age یا Expires در پاسخ اضافه شود.
- PUT: ممکن است گیجکننده باشد، زیرا RFC میگوید این Verb منبعی را روی سرور جایگزین میکند. توسعهدهندگان اغلب PUT و POST را با هم اشتباه میگیرند: جایگزینی منبع در برابر افزودن داده به نمایش منبع. پاسخ PUT قابل Cache شدن نیست. بااینحال، اگر منبع وجود نداشته باشد، PUT باید مانند POST رفتار کرده و آن را ایجاد کند.
- DELETE: برای حذف یک منبع استفاده میشود و پاسخ سرور قابل Cache شدن نیست.
- CONNECT: از طریق Proxy یک Tunnel به سرور ایجاد میکند؛ یعنی درخواست HTTP به یک سرور واسط واگذار میشود تا به سرور مقصد دسترسی پیدا کند. این Verb برای درخواستهای امن مبتنی بر Transport Layer Security (TLS)، یعنی HTTPS، کاربرد دارد. در این کتاب از CONNECT استفاده نمیکنم. پاسخ آن قابل Cache شدن نیست.
- OPTIONS: برای دانستن Verbهای پشتیبانیشده برای یک URI مفید است. همچنین در Cross-Origin Resource Sharing (CORS) استفاده میشود که بعداً بخش مستقلی دارد. در دنیای API معمولاً الزام چندانی به استفادهٔ مستقیم از OPTIONS وجود ندارد، زیرا URIها و Endpointهای موجود را معمولاً از طریق مشخصات OpenAPI میشناسید. در فصل ۴ هنگام مستندسازی API نیز به آن بازمیگردیم. پاسخ OPTIONS قابل Cache شدن نیست.
- TRACE: درخواستی بدون Payload مشخص به سرور میفرستد تا بتوان بررسی کرد آیا سرورهای واسط، مانند Proxyها، درخواست اصلی را تغییر دادهاند یا خیر. در APIها معمولاً استفاده نمیشود و پاسخ آن قابل Cache شدن نیست.
RFC 7231 همهٔ Verbهای موجود را توصیف نمیکند. RFC 5789 Verb دیگری به نام PATCH را تعریف میکند؛ متن آن در www.rfc-editor.org/rfc/rfc5789.html قرار دارد.
PATCH میتواند با PUT و POST اشتباه گرفته شود، زیرا هر سه امکان تغییر منبع روی سرور را فراهم میکنند. PATCH منبع را بهصورت جزئی بهروزرسانی میکند، درحالیکه PUT معمولاً برای جایگزینی منبع در نظر گرفته میشود.
بسیاری از توسعهدهندگان این Verbها را با هم اشتباه میگیرند. RFCها معنای دقیق آنها را مشخص میکنند، اما در عمل پذیرفته شده است که POST برای ایجاد منبع استفاده شود یا وقتی پارامترهای URI بیش از حد زیادند، بهجای GET از POST استفاده شود تا پارامترها در Body قرار بگیرند. همچنین استفاده از PUT برای جایگزینی کامل یا جزئی یک منبع رایج است، هرچند PATCH دقیقاً برای بهروزرسانی جزئی طراحی شده است. من شخصاً PATCH را بهندرت استفاده میکنم؛ فقط وقتی بخواهم یک Property منفرد، مانند تاریخ، را تغییر دهم. وقتی چند Property مانند تاریخ، وضعیت و توضیح تغییر میکنند، ترجیح میدهم PUT را پیادهسازی کنم.
پیشتر به HTTP Status Codeها اشاره کردم. در بخش بعد رابطهٔ آنها را با Verbها بررسی میکنیم.
HTTP Status Codeها
کدهای وضعیت HTTP در ارتباط درخواست/پاسخ بین سرور و کلاینت ضروریاند. کلاینت پس از دریافت پاسخ میتواند از نتیجهٔ پردازش درخواست توسط سرور آگاه شود. این کدها نیز تحت RFC 7231 قرار دارند. همهٔ کلاسها و کدها را بهصورت جامع شرح نمیدهم، زیرا RFC 7231 این کار را بهخوبی انجام داده و در این کتاب نیز از همهٔ آنها استفاده نخواهیم کرد.
در APIها کد وضعیت برای فهم پیام سرور بسیار مهم است و به کلاینت سرنخ میدهد که در مرحلهٔ بعد چه کاری باید انجام دهد. هر کد وضعیت HTTP سه رقم دارد و رقم اول دستهٔ آن را مشخص میکند. پنج دستهٔ اصلی عبارتاند از:
- 1xx: صرفاً اطلاعرسانی.
- 2xx: سرور درخواست را دریافت کرده و با موفقیت پردازش کرده است.
- 3xx: سرور Redirect انجام داده است؛ یعنی منبع در نشانی اعلامشده نیست و درخواست بهصورت خودکار به نشانی دیگری هدایت میشود.
- 4xx: درخواست در سمت کلاینت نادرست است و/یا کاربر ورودی اشتباهی فرستاده است. این خطاها معمولاً توسط کلاینت قابل اصلاحاند.
- 5xx: درخواست به علت خطایی در سمت سرور تکمیل نشده است.
RFC 7231 تنها RFC مربوط به کدهای وضعیت HTTP نیست. RFC 4918 و RFC 6585 نیز کدهای دیگری برای سناریوهای دیگر اضافه میکنند. جدول ۱-۱ با تکیه بر RFCهای 7231، 4918 و 6585، ارتباط رایج بین کدهای وضعیت و Verbهای HTTP را نشان میدهد. لازم نیست همهٔ این کدها را حفظ کنید؛ مهم این است که از وجودشان و محل استفادهٔ آنها آگاه باشید.
جدول ۱-۱. کدهای وضعیت HTTP و Verbهایی که معمولاً همراه آنها استفاده میشوند| کد | عبارت وضعیت | RFC | Verb مرتبط |
| 100 | Continue | 7231 | همهٔ Verbها |
| 101 | Switching Protocols | 7231 | همهٔ Verbها |
| 200 | OK | 7231 | GET, HEAD |
| 201 | Created | 7231 | POST |
| 202 | Accepted | 7231 | همهٔ Verbها |
| 203 | Non-Authoritative Information | 7231 | GET |
| 204 | No Content | 7231 | POST, PUT, PATCH |
| 205 | Reset Content | 7231 | POST, PUT, PATCH |
| 206 | Partial Content | 7231 | GET |
| 207 | Multi-Status | 4918 | همهٔ Verbها |
| 300 | Multiple Choices | 7231 | همهٔ Verbها |
| 301 | Moved Permanently | 7231 | GET, HEAD, DELETE |
| 302 | Found | 7231 | GET, HEAD, DELETE |
| 303 | See Other | 7231 | GET, HEAD, DELETE |
| 304 | Not Modified | 7231 | GET, HEAD |
| 305 | Use Proxy (منسوخ) | 7231 | همهٔ Verbها |
| 307 | Temporary Redirect | 7231 | همهٔ Verbها |
| 400 | Bad Request | 7231 | POST, PUT, PATCH |
| 401 | Unauthorized | 7231 | همهٔ Verbها |
| 402 | Payment Required | 7231 | هنوز استفاده نمیشود |
| 403 | Forbidden | 7231 | همهٔ Verbها |
| 404 | Not Found | 7231 | همه بهجز POST |
| 405 | Method Not Allowed | 7231 | همهٔ Verbها |
| 406 | Not Acceptable | 7231 | همهٔ Verbها |
| 407 | Proxy Authentication Required | 7231 | همهٔ Verbها |
| 408 | Request Timeout | 7231 | همهٔ Verbها |
| 409 | Conflict | 7231 | POST, PUT, PATCH |
| 410 | Gone | 7231 | همه بهجز POST |
| 411 | Length Required | 7231 | POST, PUT, PATCH |
| 412 | Precondition Failed | 4918 | همهٔ Verbها |
| 413 | Payload Too Large | 7231 | POST, PUT, PATCH |
| 414 | URI Too Long | 7231 | GET، ولی بر همهٔ Verbها قابل اعمال است |
| 415 | Unsupported Media Type | 7231 | POST, PUT, PATCH |
| 417 | Expectation Failed | 7231 | همهٔ Verbها |
| 422 | Unprocessable Entity | 4918 | POST, PUT, PATCH |
| 423 | Locked | 4918 | GET, HEAD, POST, PUT, PATCH |
| 424 | Failed Dependency | 4918 | همهٔ Verbها |
| 426 | Upgrade Required | 4918 | همهٔ Verbها |
| 500 | Internal Error | 7321 | همهٔ Verbها |
| 501 | Not Implemented | 7231 | همهٔ Verbها |
| 502 | Bad Gateway | 7231 | همهٔ Verbها |
| 503 | Service Unavailable | 7231 | همهٔ Verbها |
| 504 | Gateway Timeout | 7231 | همهٔ Verbها |
| 505 | HTTP Version Not Supported | 7231 | همهٔ Verbها |
| 507 | Insufficient Storage | 4918 | POST, PUT, PATCH |
این فهرست ممکن است طولانی به نظر برسد، اما در ۹۹ درصد موارد فقط از تعداد محدودی از این کدها استفاده خواهید کرد. بعداً به برخی از آنها بازمیگردیم و با مثال کاربردشان را توضیح میدهم.
URI، URL و موارد مرتبط
URI
در ابتدای فصل مفهوم URI را معرفی کردم. طبق RFC 3986، Uniform Resource Identifier (URI) «دنبالهای فشرده از نویسهها است که یک منبع انتزاعی یا فیزیکی را شناسایی میکند». به زبان ساده، همان نشانیای است که برای دسترسی به منبعی مانند www.google.com در مرورگر وارد میکنید؛ هرچند ساختار URI از این خلاصه پیچیدهتر است.
URI شامل اجزای زیر است یا میتواند شامل آنها باشد:
- Scheme — اجباری؛ روش دسترسی به منبع راه دور را تعیین میکند.
- Authority — اجباری؛ از User Information اختیاری، Host و Port اختیاری تشکیل میشود و پس از
:// قرار میگیرد. در این کتاب User Information را بررسی نمیکنم. - Path — اختیاری؛ دادهای برای شناسایی منبع در یک Scope مشخص است و با
/ آغاز میشود.
- Query — اختیاری؛ دادهای برای شناسایی/فیلتر منبع در Scope مشخص است و با
? آغاز میشود. - Fragment — اختیاری؛ برای شناسایی زیرمجموعهای از منبع استفاده میشود و در صفحات HTML معمولاً Anchor را مشخص میکند. توضیح بیشتر در html.com/anchors-links/ آمده است. چون این کتاب دربارهٔ APIها است، وارد جزئیات Fragment در HTML نمیشوم.
شکل ۱-۲ ساختار URI را نشان میدهد. Scheme، نویسههای :// و Authority اجباریاند؛ اجزای بعدی مانند Path، Query و Fragment اختیاریاند.
شکل ۱-۲. ساختار URI
Authority نیز از چند جزء اجباری و اختیاری تشکیل میشود. شکل ۱-۳ ساختار آن را نشان میدهد؛ فقط Host اجباری است.
شکل ۱-۳. ساختار Authority
چند نمونهٔ URI:
- مثال ۱: صفحهٔ اصلی وبلاگ بدون Path، Query یا Fragment و فقط با Scheme و Authority: anthonygiretti.com.
- مثال ۲: صفحهٔ جستوجوی وبلاگ با Query: anthonygiretti.com/?s=http.
- مثال ۳: همان جستوجو با Query و Fragment: anthonygiretti.com/?s=http#book.
- مثال ۴: صفحهای مشخص از وبلاگ با Path: anthonygiretti.com/2021/08/12/asp-net-core-6-working-with-minimal-apis/.
- مثال ۵: همان صفحهٔ HTML در ماشین توسعهٔ محلی: localhost:2222/2021/08/12/asp-net-core-6-working-with-minimal-apis/.
بعداً در بخش REST دوباره به URI بازمیگردیم. برای مطالعهٔ عمیقتر، RFC 3986 مرجع اصلی است.
URL
Uniform Resource Locator یا URL اصطلاحی بسیار آشنا است و URI و URL اغلب با هم اشتباه گرفته میشوند.
تفاوت ظریف آنها این است که URI هویت یک منبع را تعریف میکند، درحالیکه URL منبع را به روش دسترسی مشخصی که Scheme تعیین میکند پیوند میدهد. نمونههای قبلی همگی از Scheme مربوط به HTTP استفاده میکردند. URL میتواند پروتکلهای دیگری را نیز فراخوانی کند:
- File Transfer Protocol با Scheme برابر
ftp. - Gopher با Scheme برابر
gopher. - پست الکترونیکی با Scheme برابر
mailto. - Usenet با Scheme برابر
news. - NNTP با Scheme برابر
nntp. - Telnet با Scheme برابر
telnet. - Wide Area Information Server با Scheme برابر
wais. - Host-specific file names با Scheme برابر
file. - Prospero Directory Service با Scheme برابر
prospero.
برای جزئیات بیشتر RFC 1738 در www.rfc-editor.org/rfc/rfc1738 قابل مطالعه است.
موارد دیگر
دو مخفف دیگر نیز ممکن است با URI و URL همراه یا با آنها اشتباه شوند:
- Uniform Resource Names (URN) که در RFC 1737 تعریف شده است: www.rfc-editor.org/rfc/rfc1737.
- Uniform Resource Characteristics (URC) که RFC مشخصی ندارد؛ اطلاعات معرفی آن در datatracker.ietf.org/wg/urc/about/ موجود است.
این دو در این کتاب کاربرد ویژهای ندارند، اما منابع آنها برای مطالعهٔ بیشتر ارائه شد.
پارامترها
از آغاز فصل عملاً با پارامترها سروکار داشتهایم. پارامترها برای پیدا کردن یک منبع مشخص روی سرور بهکار میروند، هرچند همیشه نتیجه فقط یک منبع نیست. چند روش استفاده عبارتاند از:
- از طریق Header سفارشی؛ مانند
myHeader: myValue. این روش توصیهشده نیست، اما در صورت نیاز امکانپذیر است. - از طریق Path در URL؛ برای نمونه www.rfc-editor.org/rfc/rfc1738 که در آن
rfc1738 شناسهٔ منبع هدف است و یک صفحهٔ HTML حاوی RFC 1738 را ارائه میکند. - از طریق Query؛ برای نمونه anthonygiretti.com/?s=http. Query الزاماً یک منبع مشخص را هدف نمیگیرد و ممکن است مجموعهای از منابع مطابق معیار جستوجو را برگرداند.
دو روش آخر برای REST مناسبترند و در بخش سبک معماری REST علت آن را خواهیم دید.
مدیریت خطا
HTTP بهگونهای طراحی شده است که مدیریت خطا را بهشکلی منظم ممکن میکند و درج درست خطا در پاسخ HTTP کاملاً حیاتی است. RFC 7807 این موضوع را توصیف میکند. این RFC یک قرارداد JSON با نام Problem Details تعریف میکند که هنگام وقوع خطا در پاسخ به کلاینت API برگردانده میشود. اجزای Problem Details طبق RFC 7807 در جدول ۱-۳ آمدهاند.
جدول ۱-۳. اجزای Problem Details طبق RFC 7807| Property | توضیح |
type (string) | یک URI reference طبق RFC 3986 که نوع Problem را شناسایی میکند. توصیه میشود در صورت Dereference شدن، مستندات قابل خواندن برای انسان دربارهٔ نوع Problem ارائه کند. اگر این عضو وجود نداشته باشد، مقدار آن about:blank فرض میشود. |
title (string) | خلاصهای کوتاه و قابل فهم از نوع Problem. جز برای Localization نباید از یک رخداد Problem به رخداد دیگر تغییر کند؛ RFC 7231 بخش 3.4 دربارهٔ Content Negotiation مرتبط است. |
status (number) | HTTP Status Code تولیدشده توسط Origin Server برای همین رخداد Problem. |
detail (string) | توضیح قابل فهم برای انسان که مخصوص همین رخداد Problem است. |
instance (string) | URI reference که رخداد مشخص Problem را شناسایی میکند و در صورت Dereference شدن ممکن است اطلاعات بیشتری بدهد یا ندهد. |
Problem Details بسیار کاربردی است، زیرا قرارداد خطایی با جزئیات مناسب ارائه میکند. اینجا مثال جداگانهای نمیآورم چون RFC 7807 نمونههایی دارد. در ادامهٔ کتاب، هنگام نمایش مدیریت خطا در ASP.NET Core 8 از Problem Details استفاده خواهم کرد.
HTTPS، TLS و HSTS
تا اینجا دربارهٔ امنیت HTTP صحبت نکرده بودیم. مشکل HTTP این است که اجازه میدهد داده بین کلاینت و سرور بهصورت Clear Text روی اینترنت جابهجا شود. انتقال دادهٔ رمزنگارینشده میتواند این مشکلات را ایجاد کند:
- مهاجم میتواند ارتباط را «شنود» کرده و دادهٔ تبادلشده را سرقت کند.
- مهاجم میتواند داده را دستکاری کند.
- هیچ احراز هویت امنیتی تضمین نمیکند کلاینت واقعاً با وبسایتی که درخواست کرده ارتباط دارد.
اینجاست که HTTPS وارد میشود. HTTPS نسخهٔ امن HTTP است؛ حرف S مخفف Secure است. Scheme آن https است. HTTP بهطور پیشفرض روی Port 80 و HTTPS روی Port 443 اجرا میشود. در ASP.NET Core میتوان شمارهٔ Port را با Configuration تغییر داد.
HTTPS بر Transport Layer Security (TLS) تکیه دارد و مشکلات بالا را برطرف میکند. HTTPS سه ویژگی اصلی فراهم میکند:
- Encryption (رمزنگاری): داده دیگر بهصورت خوانا روی اینترنت دیده نمیشود.
- حفاظت از یکپارچگی داده: داده دیگر بهسادگی قابل جعل یا تغییر نیست.
- Authentication (احراز هویت): اطمینان میدهد کلاینت با وبسایتی که درخواست کرده ارتباط برقرار میکند.
SSL/TLS چگونه کار میکند؟ بین کلاینت و سرور تبادل کلید انجام میشود و یک اتصال رمزنگاریشده با نام TLS Handshake شکل میگیرد. برای ادامهٔ این کتاب دانستن جزئیات TLS Handshake لازم نیست؛ توضیح تکمیلی در www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/ آمده است.
فقط کلاینت و سروری که کلید رمزگشایی را در اختیار دارند میتوانند دادهٔ مبادلهشده را رمز و رمزگشایی کنند. برای این کار، سرور به SSL Certificate نیاز دارد که مدیر سرور یا توسعهدهنده آن را از Certificate Authority (CA) دریافت و نصب میکند. این کتاب روی پیادهسازی API با ASP.NET Core تمرکز دارد. ASP.NET Core هنگام اولین اجرا در Visual Studio 2022 با رضایت شما از SSL Certificate استفاده میکند. بعداً نشان میدهم چگونه با پیکربندی HTTP Strict Transport Security (HSTS) API را مجبور کنید وقتی کلاینت URL را فراخوانی میکند از HTTPS استفاده کند.
RFCهای مرتبط عبارتاند از RFC 2818 برای HTTPS، RFC 8446 برای TLS و RFC 6797 برای HSTS.
مطالعهٔ کامل این RFCها زمانی لازم است که بخواهید همهٔ جنبههای HTTPS را عمیقاً بدانید. در استفادهٔ روزمره کافی است به یاد داشته باشید که نصب SSL Certificate روی سرور امکان تبادل کلید رمزنگاری/رمزگشایی میان سرور و کلاینت را فراهم میکند، بهطوریکه داده در اینترنت دیگر به صورت Clear Text خوانا یا قابل تغییر نباشد. شکل ۱-۴ این وضعیت را خلاصه میکند.
شکل ۱-۴. یک درخواست HTTPS پایه و پاسخ آن
این شکل، با وجود سادگی، تقریباً همان چیزی را نشان میدهد که فردی در حال جاسوسی روی یک درخواست HTTPS مشاهده خواهد کرد.
کنار هم گذاشتن قطعات پازل
اکنون مجموعهای از مفاهیم داریم که RFCها آنها را تعریف کردهاند. HTTP را با یک نمودار پایه معرفی کردم که نقش هر مفهوم را نشان میداد. این کتاب دربارهٔ APIهایی است که داده را در قالب JSON ارائه میکنند—MIME type برابر application/json را به خاطر داشته باشید—نه صفحات HTML یا قالبهای دیگر، اما اصل ارتباط همان است. برای دیدن و درک کاملتر یک درخواست HTTP، شکل ۱-۱ را در شکل ۱-۵ با جزئیات بیشتری بازسازی میکنم. شکل ۱-۵ فراخوانی URL برابر www.myServiceApi.com/scope/someId با Verb برابر GET را نشان میدهد؛ Request فقط قالب application/json و زبان en-CA را میپذیرد، Encodingهای gzip، deflate و br (Brotli) را قبول میکند و در نهایت با Basic Authentication احراز هویت میشود.
Response کد وضعیت 200 OK را همراه با Content-Type درخواستی یعنی application/json، Header مربوط به Content-Length، فناوری سرور یعنی Kestrel و یک Payload از نوع JSON برمیگرداند. با وجود آنکه در شکل ۱-۵ HTTPS نمایش داده شده است، دادههای Request و Response را عمداً بهصورت خوانا نشان دادهام.
شکل ۱-۵. یک درخواست HTTPS «در دنیای واقعی» و پاسخ آن
این شکل ساده تقریباً همان چیزی را نشان میدهد که شخصی در حال جاسوسی روی یک درخواست HTTPS میبیند.
گسترش مهارت شما در وب با سبک معماری REST
در سال ۲۰۰۰، دانشمند رایانهٔ آمریکایی Roy Fielding سبک معماری مورد استفاده برای توسعهٔ سرویسهای وب را تعریف کرد: Representational State Transfer (REST). HTTP یک پروتکل است که توسط یک کمیته و از طریق RFCها تعریف میشود؛ REST یک مفهوم معماری است. این مفهوم HTTP را دوباره تعریف نمیکند و قابلیت اضافهای به HTTP نمیافزاید. REST از HTTP مستقل است.