رفتن به محتوای اصلی

Pedram Sarani

یادداشت‌های SRE و DevOps

بازگشت به نکته‌های سریع

نکته‌های سریع

HTTP بالاخره برای queryهای بزرگ یک جواب تمیز دارد: QUERY

بعد از سال‌ها بحث در IETF، متد QUERY در قالب RFC 10008 استاندارد شد؛ راهی برای اینکه queryهای بزرگ را مثل POST داخل body بفرستیم، اما همچنان به HTTP بگوییم این درخواست read-only است و تکرار شدنش خطرناک نیست.

#http #query-method #rfc-10008 #api-design #caching

بلاخره یکی از قدیمی‌ترین بحث‌های HTTP به نتیجه رسید: متد QUERY در قالب RFC 10008 به عنوان Proposed Standard منتشر شد و رسماً وارد ریجستری متدهای HTTP شد. این ایده اول با نام SEARCH جلو رفته بود، اما بعداً اسمش شد QUERY؛ چون SEARCH از قبل در WebDAV سابقه داشت و ممکن بود گیج‌کننده شود.

ایده اصلی ساده است: query را مثل POST داخل body درخواست بفرست، اما برخلاف POST به زیرساخت HTTP بگو که این درخواست قرار نیست چیزی را تغییر دهد و تکرار شدنش هم خطرناک نیست. در زبان HTTP، یعنی این درخواست safe و idempotent است؛ همین دو ویژگی راه را برای cache و retry خودکار باز می‌کند.

تا امروز برای queryهای بزرگ دو راه داشتیم و هر دو نصفه بودند:
راه اول GET بود که query را داخل URI حمل می‌‌کرد.مشکل فقط شلوغ شدن URL نیست. طول URI ممکن است در یکی از اجزای مسیر درخواست، از client و proxy گرفته تا load balancer و سرور، به محدودیت بخورد و RFC 9110 فقط توصیه می‌کند حداقل ۸۰۰۰ octet پشتیبانی شود؛ URIها خیلی بیشتر از body در log و bookmark ثبت می‌شوند؛ و body در GET هم اصلاً semantics تعریف‌شده ندارد و بعضی پیاده‌سازی‌ها آن را رد می‌کنند.
راه دوم همان POST /search آشنا بود. body دارد و محدودیت طول ندارد، اما از دید HTTP، معنی POST این است که «شاید state تغییر کند».نتیجه این می‌شود که مسیر طبیعی cache و retry خودکار را از دست می‌دهیم، چون زیرساخت HTTP نمی‌تواند از روی متد بفهمد این عملیات واقعاً read-only است. عملاً یک عملیات read-only راجوری اجرا می‌کردیم که همه تضمین‌های read بودن را دور می‌ریخت.

متد QUERY دقیقاً همین شکاف را پر می‌کند و در ریجستری متدهای IANA با دو ویژگی safe و idempotent ثبت شده است. چند نکته ریز اما مهم هم دارد: وجود Content-Type اجباری است و اگر نباشد یا با محتوا نسازد، سرور باید درخواست را با 4xx رد کند (content sniffing هم ممنوع است). پاسخ QUERY قابل cache است، اما cache key باید body درخواست و metadata آن را هم شامل شود؛ یعنی cache کردن QUERY ذاتاً سنگین‌تر از GET است، چون cache باید کل body را بخواند تا بفهمد دو query یکی هستند یا نه.

یک مکانیزم جالب دیگر هم دارد: سرور می‌تواند در پاسخ موفق، header با نام Location برگرداند و برای خود query یک URI معرفی کند. در این حالت، client می‌تواند همان query را بعداً با GET روی آن URI تکرار کند، بدون اینکه دوباره body را بفرستد. این کار می‌تواند cache و conditional request را به مدل آشناتر GET نزدیک کند، البته فقط وقتی سرور واقعاً چنین URIای را پایدار و قابل استفاده طراحی کرده باشد. برای اینکه client بفهمد سرور چه نوع queryای را قبول می‌کند، سرور می‌تواند در پاسخ، Accept-Query را برگرداند و فرمت‌های قابل قبول را اعلام کند.

نمونه request و response در بخش «مثال» انتهای صفحه آمده است. در آن مثال، query به شکل JSON در body ارسال می‌شود و سرور علاوه بر نتیجه، با Location آدرسی برای تکرار همان query با GET برمی‌گرداند.

Comparison of GET, POST and the new HTTP QUERY method from RFC 10008

خلاصه این تغییر: شکافی که سال‌ها بین GET و POST برای عملیات read با ورودی بزرگ وجود داشت، حالا یک جواب استاندارد دارد. البته استاندارد شدن با پشتیبانی فراگیر فرق دارد؛ client، framework، proxy و WAF کل مسیر باید این متد را بشناسند و در browser هم QUERY مثل هر متد غیر safelisted به preflight نیاز دارد. پس فعلاً بیشتر برای طراحی APIهای جدید و شناخت مسیر آینده HTTP باید به آن نگاه کنیم، نه جایگزینی فوری POST /search در production.

مثال

QUERY /orders HTTP/1.1
Host: api.example.org
Content-Type: application/json
Accept: application/json

{
  "filters": { "status": "paid", "total_gt": 1000 },
  "sort": ["-created_at"],
  "limit": 50
}

HTTP/1.1 200 OK
Content-Type: application/json
Location: /orders/stored-queries/7f3a

[ ...matching orders... ]

محدودیت‌ها

  • متد QUERY جزو متدهای CORS-safelisted نیست؛ درخواست‌های cross-origin از سمت browser با این متد به preflight نیاز دارند، هرچند نتیجه preflight می‌تواند طبق Access-Control-Max-Age برای مدتی cache شود.
  • cache کردن پاسخ QUERY ذاتاً پیچیده‌تر از GET است؛ cache key باید کل body درخواست و metadata آن را هم شامل شود، یعنی cache باید body را کامل بخواند.
  • وجود Content-Type اجباری است؛ اگر نباشد یا با محتوای body نسازد، سرور باید درخواست را با 4xx رد کند و content sniffing هم مجاز نیست.
  • استاندارد شدن با پشتیبانی فراگیر فرق دارد؛ client، framework، proxy و WAF کل مسیر باید متد جدید را بشناسند و قبل از استفاده باید مسیر کامل تست شود.

منابع و مطالعهٔ بیشتر

  1. IETFrfc-editor.orgبازبینی: ۱۳ تیر ۱۴۰۵

    تعریف رسمی متد QUERY به عنوان Proposed Standard؛ شامل safe و idempotent بودن، الزام Content-Type، قاعده cache key بر پایه body، فیلد Accept-Query، مکانیزم معرفی URI برای query یا نتیجه، و ملاحظه CORS preflight.

  2. IANA HTTP Method Registryمستندات رسمی

    IANAiana.orgبازبینی: ۱۳ تیر ۱۴۰۵

    ثبت رسمی QUERY در registry متدهای HTTP با دو ویژگی safe و idempotent و ارجاع به RFC 10008.