HTTP در وب: درخواست، پاسخ و پیاده‌سازی

HTTP در وب: درخواست، پاسخ و پیاده‌سازی

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
  • سبک معماری 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 سه ویژگی اساسی دارد:

  1. Stateless (بدون حالت) است: پس از ارسال درخواست به سرور و دریافت پاسخ، نه کلاینت و نه سرور اطلاعاتی دربارهٔ درخواست مبادله‌شده نزد خود نگه نمی‌دارند.
  2. Connectionless (بدون اتصال پایدار) است: میان کلاینت و سرور یک اتصال HTTP باز می‌شود. پس از دریافت پاسخ سرور، اتصال بسته می‌شود و ارتباط بین دو سامانه دائمی نیست.
  3. از رسانه مستقل است: سرور می‌تواند هر نوع رسانه‌ای را منتقل کند، به شرط آن‌که کلاینت و سرور دربارهٔ قالب محتوای مبادله‌شده «توافق» داشته باشند. هنگام بحث دربارهٔ 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 نیز از اجزای زیر تشکیل می‌شود:

تصویر منبع — صفحهٔ 18Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 18.
  • سرور یک پاسخ را با 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هایی که معمولاً همراه آن‌ها استفاده می‌شوند
کدعبارت وضعیتRFCVerb مرتبط
100Continue7231همهٔ Verbها
101Switching Protocols7231همهٔ Verbها
200OK7231GET, HEAD
201Created7231POST
202Accepted7231همهٔ Verbها
203Non-Authoritative Information7231GET
204No Content7231POST, PUT, PATCH
205Reset Content7231POST, PUT, PATCH
206Partial Content7231GET
207Multi-Status4918همهٔ Verbها
300Multiple Choices7231همهٔ Verbها
301Moved Permanently7231GET, HEAD, DELETE
302Found7231GET, HEAD, DELETE
303See Other7231GET, HEAD, DELETE
304Not Modified7231GET, HEAD
305Use Proxy (منسوخ)7231همهٔ Verbها
307Temporary Redirect7231همهٔ Verbها
400Bad Request7231POST, PUT, PATCH
401Unauthorized7231همهٔ Verbها
402Payment Required7231هنوز استفاده نمی‌شود
403Forbidden7231همهٔ Verbها
404Not Found7231همه به‌جز POST
405Method Not Allowed7231همهٔ Verbها
406Not Acceptable7231همهٔ Verbها
407Proxy Authentication Required7231همهٔ Verbها
408Request Timeout7231همهٔ Verbها
409Conflict7231POST, PUT, PATCH
410Gone7231همه به‌جز POST
411Length Required7231POST, PUT, PATCH
412Precondition Failed4918همهٔ Verbها
413Payload Too Large7231POST, PUT, PATCH
414URI Too Long7231GET، ولی بر همهٔ Verbها قابل اعمال است
415Unsupported Media Type7231POST, PUT, PATCH
417Expectation Failed7231همهٔ Verbها
422Unprocessable Entity4918POST, PUT, PATCH
423Locked4918GET, HEAD, POST, PUT, PATCH
424Failed Dependency4918همهٔ Verbها
426Upgrade Required4918همهٔ Verbها
500Internal Error7321همهٔ Verbها
501Not Implemented7231همهٔ Verbها
502Bad Gateway7231همهٔ Verbها
503Service Unavailable7231همهٔ Verbها
504Gateway Timeout7231همهٔ Verbها
505HTTP Version Not Supported7231همهٔ Verbها
507Insufficient Storage4918POST, PUT, PATCH

این فهرست ممکن است طولانی به نظر برسد، اما در ۹۹ درصد موارد فقط از تعداد محدودی از این کدها استفاده خواهید کرد. بعداً به برخی از آن‌ها بازمی‌گردیم و با مثال کاربردشان را توضیح می‌دهم.

Headerهای درخواست و پاسخ

HTTP Headerها فراداده‌ای هستند که اجازه می‌دهند هنگام جریان درخواست/پاسخ، اطلاعات بین کلاینت و سرور منتقل شود. این اطلاعات فقط به دادهٔ احراز هویت یا اطلاعات مرورگر کلاینت محدود نیست. Headerهای Request و Response اهداف متفاوتی دارند و RFC 7231 بسیاری از آن‌ها را تعریف می‌کند یا به RFCهای تخصصی‌تر ارجاع می‌دهد.

همهٔ کاربردهای ممکن را پوشش نمی‌دهیم. بعضی Headerها توسط مرورگر در هنگام درخواست و بعضی توسط سرور هنگام پاسخ به‌صورت خودکار تولید می‌شوند. لازم نیست آن‌ها را حفظ کنید؛ کافی است بدانید وجود دارند تا در صورت نیاز بتوانید رفتارشان را سفارشی کنید.

Headerهای Request

Headerهای Request در پنج کلاس قرار می‌گیرند:

  • Headerهای Controls
  • Headerهای Conditional
  • Headerهای Content Negotiation
  • Headerهای Authentication Credentials
  • Headerهای Request Context

Controls Headers

در کلاس Controls هفت Header وجود دارد و بعضی از آن‌ها Directiveهای گوناگون دارند:

  • Cache-Control: مدت و نحوهٔ Cache را در زنجیرهٔ Request/Response مشخص می‌کند. Directiveهای مهم آن عبارت‌اند از: no-cache و no-store بدون مقدار؛ max-age با مقدار ثانیه مانند Cache-Control: max-age=302400؛ max-stale مانند Cache-Control: max-stale=1800؛ min-fresh مانند Cache-Control: min-fresh=600؛ و no-transform و only-if-cached بدون مقدار. جزئیات در RFC 7234 آمده است.
  • Expect: انتظار کلاینت از سرور برای پردازش درست درخواست را مشخص می‌کند؛ مانند Expect: 100-continue. جزئیات در RFC 7231.
  • Host: Hostname و Port اختیاری URI هدف را مشخص می‌کند؛ مانند Host: www.example.com. جزئیات در RFC 7230.
  • Max-Forwards: حداکثر تعداد سرورهای واسط یا Proxyهایی را که درخواست را Forward می‌کنند مشخص می‌کند. فقط با TRACE و OPTIONS کار می‌کند و مقدار صحیح می‌گیرد؛ مانند Max-Forwards: 1. جزئیات در RFC 7231.
  • Pragma: برای سازگاری عقب‌رو با Cache در HTTP/1.0 استفاده می‌شود. اگر Cache-Control وجود داشته باشد، این Header نادیده گرفته می‌شود؛ مانند Pragma: no-cache. جزئیات در RFC 7234.
  • Range: درخواست بخشی از سند را در یک بازه، معمولاً بر حسب Byte، مشخص می‌کند؛ مانند Range: bytes 0-2048. جزئیات در RFC 7233.
  • TE: Transfer Coding قطعه‌ای را، برای مثال الگوریتم فشرده‌سازی، مشخص می‌کند؛ مانند TE: gzip. جزئیات در RFC 7230.

Conditional Headers

پنج Header شرطی اجازه می‌دهند تکمیل درخواست به وضعیت منبع هدف وابسته شود:

  • If-Match: بررسی می‌کند منبع درخواستی با یک Representation فعلی منبع تطبیق دارد یا نه؛ مانند If-Match: * یا If-Match: "123" که منبع دارای ETag برابر «123» را هدف می‌گیرد. ETag یک نسخهٔ مشخص از منبع را نمایش می‌دهد. RFC 7232.
  • If-None-Match: برعکس If-Match بررسی می‌کند منبع با هیچ Representation فعلی تطبیق نداشته باشد؛ مانند If-None-Match: * یا If-None-Match: "123". RFC 7232.
  • If-Modified-Since: بررسی می‌کند تاریخ تغییر Representation منبع هدف از تاریخ داده‌شده جدیدتر است یا نه؛ مانند If-Modified-Since: Wed, 22 Aug 2022 21:56:00 GMT. RFC 7232.
  • If-Unmodified-Since: بررسی می‌کند تاریخ تغییر Representation منبع هدف از تاریخ داده‌شده جدیدتر نباشد؛ مانند If-Unmodified-Since: Wed, 22 Aug 2022 21:56:00 GMT. RFC 7232.
  • If-Range: ترکیبی از If-Match و If-Modified-Since است؛ مانند If-Range: "123" یا If-Range: Wed, 22 Aug 2022 21:56:00 GMT. RFC 7233.

Content Negotiation Headers

این Headerها برای توافق کلاینت و سرور دربارهٔ قالب مبادله بسیار مهم‌اند و چهار موردند:

  • Accept: MIME type قابل فهم برای کلاینت را مشخص می‌کند؛ مانند Accept: application/json یا Accept: application/json, application/xhtml+xml. RFC 7231.
  • Accept-Charset: منسوخ است و بسیاری از مرورگرها و سرورها آن را نادیده می‌گیرند. RFC 7231.
  • Accept-Encoding: الگوریتم فشرده‌سازی را مشخص می‌کند؛ مانند Accept-Encoding: deflate, gzip. RFC 7231.
  • Accept-Language: زبان قابل قبول برای کلاینت را به سرور اعلام می‌کند؛ مانند Accept-Language: * یا Accept-Language: en-CA. RFC 7231.

Authentication Credentials Headers

دو Header برای تعامل با منابع محافظت‌شده با احراز هویت مهم‌اند:

  • Authorization: برای احراز هویت روی سرور هدف بسیار رایج است و انواع مختلفی مانند Bearer Token یا Basic Authentication را پشتیبانی می‌کند؛ مانند Authorization: bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…. RFC 7235.
  • Proxy-Authorization: مشابه Authorization است، اما برای احراز هویت Proxy به‌کار می‌رود؛ مانند Proxy-Authorization: basic YW50aG9ueWdpcmV0dGk6MTIzNA==. RFC 7235.

Request Context Headers

RFC 7231 سه Header برای ارائهٔ دادهٔ زمینه‌ای بیشتر به سرور معرفی می‌کند:

  • From: مشخص می‌کند چه کسی با یک نشانی ایمیل درخواست را فرستاده است؛ مانند From: John.Doe@example.com.
  • Referrer: URI مبدأ درخواست را به سرور اعلام می‌کند؛ مانند Referrer: anthonygiretti.com.
  • User-Agent: اطلاعاتی دربارهٔ عامل ایجادکنندهٔ درخواست، مانند قابلیت‌های مرورگر، ارائه می‌کند؛ مانند User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36.

این‌ها از رایج‌ترین Headerهای Request هستند. لازم نیست آن‌ها را حفظ کنید؛ RFCها و همین کتاب برای مراجعه وجود دارند. با گذشت زمان یاد خواهید گرفت در کجا و چه زمانی از آن‌ها استفاده کنید.

جدول ۱-۲. RFCهای اشاره‌شده در این بخش
RFCنشانی متنی
7230www.rfc-editor.org/rfc/rfc7230
7231www.rfc-editor.org/rfc/rfc7231
7232www.rfc-editor.org/rfc/rfc7232
7233www.rfc-editor.org/rfc/rfc7233
7234www.rfc-editor.org/rfc/rfc7234
7235www.rfc-editor.org/rfc/rfc7235

Headerهای Response

همان‌طور که ارسال Metadata به سرور اهمیت دارد، Response Headerها نیز برای آگاه کردن کلاینت، چه مرورگر و چه برنامه، از Metadataهای اضافی مربوط به زمینهٔ پاسخ HTTP ضروری‌اند. بخشی از آن‌ها در RFC 7231 و بقیه در RFCهای 7232، 7233، 7234 و 7235 تعریف شده‌اند. چهار کلاس Response Header عبارت‌اند از: Control Data، Validator، Authentication Challenges و Response Context.

Control Data Headers

این Headerها از مهم‌ترین Headerهای پاسخ‌اند و معمولاً همراه یک HTTP Status Code مناسب اطلاعات بیشتری به کلاینت می‌دهند:

  • Age: سن پاسخ را بر حسب ثانیه اعلام می‌کند. معمولاً نزدیک صفر است، اما اگر پاسخ روی Proxy Cache شده باشد می‌تواند بیشتر باشد؛ مانند Age: 0. RFC 7234.
  • Cache-Control: مشابه Header هم‌نام در Request است و همان نوع مقدار را می‌پذیرد. RFC 7234.
  • Expires: تاریخ و زمانی را مشخص می‌کند که پس از آن پاسخ قدیمی محسوب می‌شود؛ مانند Expires: Tue, 23 Aug 2022 21:35:00 GMT. RFC 7234.
  • Date: تاریخ و زمان تولید پاسخ روی سرور را اعلام می‌کند؛ مانند Sun, 21 Aug 2022 11:22:00 GMT. RFC 7231.
  • Location: URI منبع را، به‌خصوص پس از ایجاد با POST، به کلاینت اعلام می‌کند؛ مانند contoso.com/item/52. RFC 7231.
  • Retry-After: مشخص می‌کند کلاینت پس از پاسخ 503 Service Unavailable چه زمانی دوباره تلاش کند؛ مقدار می‌تواند تاریخ/زمان یا تعداد ثانیه باشد، مانند Retry-After: Wed, 24 Aug 2022 08:15:00 GMT یا Retry-After: 60. RFC 7231.
  • Vary: مشخص می‌کند کدام Header پارامتری Request بر پاسخ سرور اثر دارد. مقدار * یعنی هر چیزی در Request ممکن است اثرگذار باشد؛ مانند Vary: * یا Vary: Accept-Encoding. RFC 7231.
  • Warning: برای ارائهٔ اطلاعات کمکی به کلاینت به‌کار می‌رفت، اما منسوخ شده و توصیه نمی‌شود. RFC 7234.

Validator Header Fields

  • ETag: نسخه یا Representation منبع درخواست‌شده را به کلاینت اعلام می‌کند و می‌تواند هر رشته‌ای باشد؛ مانند ETag: "abc123". RFC 7232.
  • Last-Modified: تاریخ و زمان آخرین تغییر منبع را اعلام می‌کند؛ مانند Sat, 20 Aug 2022, 13:45:00 GMT. RFC 7232.

Authentication Challenges Headers

  • WWW-Authenticate: روش‌های احراز هویت پذیرفته‌شده توسط سرور را به کلاینت می‌گوید؛ مانند WWW-Authenticate: basic. RFC 7235.
  • Proxy-Authenticate: روش‌های احراز هویت پذیرفته‌شده توسط Proxy را اعلام می‌کند؛ مانند Proxy-Authenticate: basic. RFC 7235.

Response Context Headers

  • Accept-Ranges: واحد Range پشتیبانی‌شده توسط سرور برای دانلود بخشی از فایل را اعلام می‌کند؛ مانند Accept-Ranges: bytes. RFC 7233.
  • Allow: Verbهای پشتیبانی‌شده توسط سرور را اعلام می‌کند؛ مانند Allow: GET, POST, PUT, DELETE. RFC 7231.
  • Server: فناوری سروری که درخواست‌های HTTP را پردازش می‌کند مشخص می‌کند؛ مانند Server: Kestrel. RFC 7231.

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 مشخص است و با / آغاز می‌شود.
  • تصویر منبع — صفحهٔ 37Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 37.
    تصویر منبع — صفحهٔ 37Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 37.
  • 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.

تصویر منبع — صفحهٔ 44Visual واقعی استخراج‌شده از PDF، مربوط به صفحهٔ 44.

مطالعهٔ کامل این 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 مستقل است.

منبع: Coding Clean, Reliable, and Safe REST APIs with ASP.NET Core 8 — Anthony Giretti.

فروش یا انتشار این ترجمه منوط به داشتن مجوز لازم از صاحب حقوق اثر است.

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620