طرق الطلب (methods): ماذا يعني GET وPOST وPUT وPATCH وDELETE

تاريخ النشر: وقت القراءة:
للقراءة
عدد الكلمات:
كلمة

طرق الطلب (methods) في HTTP كلمات قصيرة تتصدر كل طلب، أشهرها GET وPOST وPUT وPATCH وDELETE، ولكل واحدة منها معنى محدد يغير ما يفعله الخادم بالمورد. ويظهر أثر هذا الاختلاف في أبسط مثال: العنوان /articles/http-message/comments في مدونة عنوان واحد لا يتغير، ومع ذلك يعود طلب يذهب إليه بقائمة تعليقات المقال، ويضيف طلب آخر يذهب إلى العنوان نفسه تعليقا جديدا تحته. والذي فرق بينهما كلمة واحدة: GET في الأول، وPOST في الثاني.

وليست هذه الكلمة تسمية شكلية يختارها المطور كيف شاء، ولا يقتصر أثرها على الخادم. أعد تحميل صفحة مقال، يعد المتصفح إرسال الطلب في صمت. ثم أعد تحميل الصفحة التي ظهرت بعد إرسال تعليق مباشرة، يتوقف المتصفح ويسألك أولا، وفي متصفح Chrome تظهر رسالة «Confirm Form Resubmission». والمتصفح لا يعرف شيئا عن برنامج هذه المدونة ولا عما يفعله بالتعليقات، لكنه يعرف الطريقة التي أرسلت بها كل صفحة، وعلى معناها وحده بنى قراره.

طرق الطلب (methods): ماذا يعني GET وPOST وPUT وPATCH وDELETE

ما الطريقة؟ كلمة تعلن ما تريد فعله بالمورد

كل طلب HTTP يبدأ بسطر واحد فيه ثلاث قطع، أولاها الطريقة:

CODE
GET /articles/http-message HTTP/1.1

القطعة الأولى GET هي الطريقة، والثانية عنوان المورد المطلوب، والثالثة نسخة البروتوكول. فالعنوان يقول أي شيء تقصد، والطريقة تقول ماذا تريد أن يفعل به.

وأقرب ما يقرب هذه القسمة موظف أرشيف يحفظ الملفات في خزانة. أنت تعطيه اسم الملف، ثم تقول له ماذا يفعل: صور لي نسخة منه، أو أضف هذه الورقة إليه، أو استبدل محتواه بهذا، أو أتلفه. الملف واحد في الطلبات الأربعة، والذي يتغير هو الفعل المطلوب. والطريقة في HTTP هي هذا الفعل.

ومعاني هذه الأفعال ليست اصطلاحا بين مطور وآخر، بل تحددها المواصفة التي تضع دلالات HTTP (RFC 9110)، وهي الوثيقة التي ترجع إليها البرامج كلها لتتفق على معنى واحد للطريقة. وتفرد مواصفة مستقلة (RFC 5789) معنى طريقة PATCH.

غير أن المواصفة تحدد المعنى ولا تملك أن تفرضه. فالخادم في النهاية برنامج يكتبه مطور، وليس في HTTP رقيب يفحص ما يفعله هذا البرنامج حين يصله الطلب. فلو كتب مطور برنامجا يحذف تعليقا كلما وصله طلب GET، لحذفه الخادم فعلا، ولن يمنعه البروتوكول.

فما قيمة المعنى إذن ما دام الخادم قادرا على مخالفته؟ قيمته أن أطرافا كثيرة غير الخادم تقرأ الطريقة وتتصرف بناء عليها:

  • المتصفح: يقرر منها هل يعيد إرسال الطلب في صمت عند إعادة تحميل الصفحة، أم يقف ويسأل المستخدم.
  • برامج الزحف (crawlers): وهي برامج تتبع روابط الصفحات لتبني فهارس محركات البحث، فتطلب كل رابط تجده على أساس أن طلبه لن يغير شيئا.
  • الوسطاء (intermediaries): وهي البرامج التي يمر بها الطلب في طريقه بين العميل والخادم، وتقرر من الطريقة هل يجوز أن تعيد إرسال الطلب إذا تعثر الاتصال.

ولا يرى أي من هذه الأطراف برنامج الخادم، ولا يعرف ماذا سيفعل بالطلب. هو يرى الطريقة، ويتصرف على أساس أن الخادم يلتزم معناها المعروف. ولهذا كانت مخالفة هذا المعنى مكلفة على من يخالفه.

وأمثلة هذا الدرس كلها من مدونة على المضيف blog.example.com: قائمة تعليقات المقال مورد عنوانه /articles/http-message/comments، وكل تعليق داخلها مورد مستقل له عنوانه الخاص، مثل التعليق رقم 17 على العنوان /articles/http-message/comments/17.

GET: قراءة المورد دون طلب تغييره

معنى GET في جملة واحدة: أعطني نسخة من هذا المورد كما هو الآن، ولا تغير شيئا.

وهي أكثر الطرق استعمالا على الويب. فكل رابط تضغطه، وكل صورة أو ملف تنسيق تحمله الصفحة، يطلبه المتصفح بطريقة GET. وهذا طلب المتصفح ليقرأ التعليق رقم 17:

CODE
GET /articles/http-message/comments/17 HTTP/1.1
Host: blog.example.com

السطر الأول يقول: أريد قراءة المورد الذي عنوانه /articles/http-message/comments/17. والسطر الثاني يذكر اسم المضيف الذي يوجد عليه هذا المورد. وليس في الطلب غير ذلك، لأنه سؤال لا إرسال بيانات.

ويرد الخادم بالتعليق في جسم الاستجابة:

CODE
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 44

{"author":"sara","text":"Clear explanation"}

السطر الأول يحمل رمز النجاح 200. والسطران بعده يصفان الجسم: صيغته application/json، أي أن بياناته مكتوبة بصيغة JSON التي توضع فيها البيانات أزواجا من اسم وقيمة، وطوله 44 بايتا. ثم يأتي الجسم نفسه، وفيه التعليق بحقليه: author باسم كاتبه، وtext بنصه.

ولا يحمل طلب GET عادة جسما. فإذا أراد العميل أن يرسل مع طلب القراءة بيانات تحدد ما يريده بالضبط، ككلمة بحث، وضعها في استعلام الرابط، كما في /search?q=http. أما أن يضع البيانات في جسم طلب GET فلا تحدد له المواصفة معنى، ولا يغير ذلك هدف الطلب ولا دلالته، وقد ترفض بعض الخوادم الطلب أصلا وتغلق الاتصال.

وإلى جانب GET طريقة قريبة منها هي HEAD، وهي التي يرسلها الخيار curl -I. تطلب HEAD المورد نفسه تماما، غير أن الخادم يرد عليها بسطر الحالة والترويسات دون جسم. وفائدتها أن تعرف شيئا عن المورد، كتاريخ آخر تعديل له أو حجمه، دون أن تنزل محتواه كله.

POST: بيانات يعالجها المورد بقواعده

معنى POST في جملة واحدة: خذ هذه البيانات، وافعل بها ما تقتضيه قواعدك.

فحين يكتب قارئ تعليقا تحت المقال ويضغط زر النشر، يرسل المتصفح هذا الطلب:

CODE
POST /articles/http-message/comments HTTP/1.1
Host: blog.example.com
Content-Type: application/json
Content-Length: 44

{"author":"sara","text":"Clear explanation"}

هنا يحمل الطلب جسما فيه البيانات التي كتبها القارئ، وترويستا Content-Type وContent-Length تصفان هذا الجسم كما وصفتا جسم الاستجابة في GET.

والملاحظة المهمة أن عنوان الطلب هو عنوان قائمة التعليقات، لا عنوان تعليق بعينه. والسبب أن التعليق الجديد لم يوجد بعد، فلا عنوان له أصلا. فالعميل يسلم البيانات إلى القائمة، والخادم هو الذي ينشئ منها تعليقا، ويعطيه عنوانا مثل /articles/http-message/comments/17، ثم يخبر العميل عادة بهذا العنوان في استجابته. وهذا يشبه أن تضع ورقة في صندوق الوارد عند موظف الأرشيف: أنت لا تختار رقم الملف الذي ستحفظ فيه، بل هو من يرقمها ويحفظها ثم يخبرك بموضعها.

وإنشاء مورد جديد استعمال واحد من استعمالات POST. فالمواصفة تعرفها تعريفا واسعا: المورد المستهدف يعالج البيانات بحسب قواعده هو. ولهذا ترسل بـPOST حقول نموذج تسجيل الدخول، وتضاف بها بيانات إلى نهاية مورد قائم، وتطلب بها عملية مثل إتمام دفع. ونماذج HTML لا تدعم غير طريقتين، GET وPOST، فأي نموذج يرسل بيانات تغير شيئا على الخادم يرسلها بطريقة POST.

ولهذه السعة ثمن: من يرى طلب POST من خارج الخادم لا يستطيع أن يعرف ماذا يحدث إذا تكرر. فقد لا يتغير شيء، وقد ينشأ تعليق ثان، وقد يخصم مبلغ مرة أخرى. فأثر التكرار تحدده قواعد المورد وحده، والطلب لا يعلن عنها.

PUT: حالة كاملة تحل محل حالة المورد

معنى PUT في جملة واحدة: اجعل حالة هذا المورد مطابقة لما في جسم الطلب.

فلو أراد صاحب التعليق أن يصحح نصه، أرسل التعليق كاملا بصورته الجديدة إلى عنوان التعليق نفسه:

CODE
PUT /articles/http-message/comments/17 HTTP/1.1
Host: blog.example.com
Content-Type: application/json
Content-Length: 52

{"author":"sara","text":"Clear explanation, thanks"}

وفي هذا الطلب فرقان عن طلب POST السابق. الأول أن العنوان هنا عنوان التعليق رقم 17 نفسه، لا عنوان القائمة، لأن المورد موجود ويعرف العميل عنوانه. والثاني أن الجسم يحمل التعليق كاملا بحقليه، لا الحقل الذي تغير وحده.

وهنا الفرق الجوهري بين PUT وPOST: جسم POST بيانات يفعل بها المورد ما تقتضيه قواعده، أما جسم PUT فهو الحالة الجديدة للمورد نفسه. ولأن هذه الحالة مكتوبة صراحة في الطلب، يستطيع كل من يراه أن يعرف النتيجة التي يريدها العميل، وهي أن يصير التعليق مطابقا للجسم المرسل، مهما كانت تفاصيل تنفيذ ذلك داخل الخادم.

ويترتب على هذا المعنى نتيجة يخطئ فيها كثيرون: الحقل الغائب عن جسم PUT ليس معناه «اتركه كما هو»، بل معناه أنه ليس من الحالة الجديدة. فلو أرسل العميل {"text":"Clear explanation, thanks"} وحده إلى عنوان التعليق، لكان معنى طلبه: اجعل هذا التعليق نصا بلا كاتب. وعندها إما أن يرفض الخادم الطلب إن كانت قواعده تشترط لكل تعليق كاتبا، وإما أن يملأ الحقل بنفسه إن كانت برمجته تسمح بذلك. وفي الحالتين، ما طلبه العميل كان استبدالا كاملا.

وبموظف الأرشيف: طلب PUT كأن تسلمه ورقة جديدة يضعها مكان الورقة القديمة في الملف. فما لم تكتبه في الورقة الجديدة لن تجده بعد اليوم.

PATCH: تعليمات تغير جزءا من المورد

معنى PATCH في جملة واحدة: طبق هذه التعديلات على الحالة الموجودة.

والحاجة إليها تظهر حين يكبر المورد. فإرسال المورد كاملا لتغيير حقل واحد فيه يثقل الطلب بلا داع، ويعرض للضياع أي حقل ينسى العميل إرساله، كما رأينا في PUT. ولهذا وضعت طريقة PATCH: جسمها لا يحمل الحالة الجديدة كاملة، بل تعليمات تصف التغيير المطلوب. ولتصحيح نص التعليق وحده يكفي هذا الطلب:

CODE
PATCH /articles/http-message/comments/17 HTTP/1.1
Host: blog.example.com
Content-Type: application/merge-patch+json
Content-Length: 36

{"text":"Clear explanation, thanks"}

الجسم هنا لا يقول «هذه هي حالة التعليق الجديدة»، بل يقول «غير حقل النص إلى هذه القيمة». والذي يحسم أي القراءتين هي المقصودة ترويسة Content-Type: قيمتها application/merge-patch+json تعني أن التعليمات مكتوبة بصيغة JSON Merge Patch (RFC 7396)، وقاعدة هذه الصيغة أن الحقول المذكورة تأخذ القيم الجديدة، وأن ما لم يذكر يبقى على حاله. فيتغير نص التعليق، ويبقى author بقيمته.

وبموظف الأرشيف: PATCH ليست ورقة جديدة تحل محل القديمة، بل ملاحظة صغيرة تقول له: غير السطر الثاني إلى كذا، واترك بقية الورقة كما هي.

الجسم {"text":"Clear explanation, thanks"} نفسه يطلب مع PUT تعليقا بلا author، لأن جسم PUT هو الحالة كاملة، ويطلب مع PATCH تغيير النص وحده، لأن التعليمات لا تتناول الحقل الغائب. فالطريقة هي التي تحدد كيف يقرأ الخادم الجسم.

ولا تقتصر التعليمات على وضع قيمة جديدة في حقل. فبعض الصيغ، مثل JSON Patch (RFC 6902)، تحمل أوامر أخرى، مثل «أضف هذه القيمة إلى آخر القائمة» أو «احذف هذا الحقل». ولكل صيغة قيمتها الخاصة في ترويسة Content-Type، وهي التي تخبر الخادم كيف يقرأ التعليمات. ولهذا الفرق بين نوعي التعليمات أثر يظهر عند تكرار الطلب.

DELETE: إزالة المورد من عنوانه

معنى DELETE في جملة واحدة: أزل هذا المورد من عنوانه.

CODE
DELETE /articles/http-message/comments/17 HTTP/1.1
Host: blog.example.com

ولا يحتاج الطلب إلى جسم، لأن ما يحذف معروف من عنوانه.

والمواصفة دقيقة في وصف هذه الطريقة: هي طلب لفك الارتباط بين العنوان والمورد، لا أمر بمحو البيانات من القرص. فما يفعله الخادم بعد ذلك شأنه: قد يمحو التعليق من قاعدة البيانات، وقد يخفيه ويحتفظ به في أرشيف. والذي يعني العميل أن العنوان /articles/http-message/comments/17 لم يعد يوصل إلى التعليق بعد نجاح الطلب. وبموظف الأرشيف: أنت تطلب رفع الملف من الدرج ونزع بطاقته، لا أن تفرض عليه إحراق الأوراق.

وتجتمع معاني الطرق الخمس في الجدول التالي:

الطريقة ما يطلبه العميل جسم الطلب
GET نسخة من المورد بحالته الحالية لا جسم
POST أن يعالج المورد البيانات بقواعده البيانات المرسلة
PUT أن تصير حالة المورد مطابقة للجسم الحالة الكاملة الجديدة
PATCH أن تطبق على المورد تعليمات تغيير تعليمات التغيير
DELETE أن يزال المورد من عنوانه لا جسم

الطرق الآمنة (safe): طلبات لا تطلب تغييرا

وبين هذه المعاني الخمسة فرق يهم كل من يرسل طلبا: طريقة GET تطلب القراءة وحدها، أما POST وPUT وPATCH وDELETE فتطلب كلها تغييرا على الخادم.

وتسمي المواصفة الطريقة التي لا يطلب بها العميل تغييرا في حالة الخادم، ولا يتوقعه، طريقة آمنة (safe). فGET آمنة، ومثلها HEAD، ومثلهما OPTIONS التي يسأل بها العميل عن الخيارات التي يدعمها المورد. والأربع الأخرى غير آمنة.

وكلمة «آمنة» هنا تحتاج تدقيقا، فهي لا تعني أن الخادم لا يغير شيئا. فحين يصل طلب GET لصفحة مقال، قد يسجل الخادم الزيارة في ملف السجلات، وقد يزيد عداد مشاهدات المقال في قاعدة البيانات. وهذه تغييرات حقيقية، لكن العميل لم يطلبها ولا يحاسب عليها، فيبقى طلبه آمنا. وتضرب المواصفة مثالا أوضح: الضغط على إعلان في صفحة ويب طلب آمن، مع أنه كثيرا ما يترتب عليه خصم من حساب المعلن. فالأمان وصف لما طلبه العميل، لا لكل ما جرى على الخادم بسبب طلبه.

وعلى هذا التمييز تقوم برامج كثيرة ترسل طلبات لم يطلبها إنسان: برامج الزحف تطلب كل رابط تجده لتبني فهارس محركات البحث، وأدوات فحص الروابط تزور الروابط لتتأكد أنها تعمل، وبعض أدوات التسريع تطلب الصفحة التي يشير إليها الرابط قبل أن يضغطه المستخدم ليجدها جاهزة، وهو ما يسمى الطلب المسبق (pre-fetching). وهذه البرامج ترسل طلباتها دون إذن أحد لأنها تقتصر على الطرق الآمنة.

ومن هنا يظهر خطر مخالفة هذا المعنى. فلو جعل موقع حذف التعليق رابطا عاديا مثل /articles/http-message/comments/17?do=delete، لصار الحذف عند كل برنامج يمر بالصفحة طلب GET عاديا، فيطلبه ويحذف التعليق دون أن يقصد ذلك أحد. ولهذا تنص المواصفة على هذه الحالة بعينها: على صاحب المورد الذي يؤدي فعلا غير آمن أن يعطل هذا الفعل إذا طلب المورد بطريقة آمنة.

وهذه ليست حالة نظرية. ففي مايو 2005 أطلقت Google أداة اسمها Google Web Accelerator، تعمل على جهاز المستخدم وتطلب مسبقا الصفحات التي تشير إليها روابط الصفحة المفتوحة ليجدها جاهزة حين يضغطها. وبعد أيام بدأ مستخدمو تطبيق Backpack من شركة 37signals يجدون صفحاتهم قد اختفت. والسبب أن روابط الحذف في واجهة التطبيق كانت روابط عادية، فطلبتها الأداة كما تطلب أي رابط. أما نافذة التأكيد التي تعرضها JavaScript قبل الحذف فلم تظهر أصلا، لأنها لا تعمل إلا حين يضغط المستخدم الرابط بنفسه.

فكرة جوهرية

البروتوكول لا يمنع الخادم من الحذف عند طلب GET، لكن المتصفحات وبرامج الزحف وأدوات الطلب المسبق تفترض أن GET لا يطلب تغييرا، فترسله دون أن يطلبه المستخدم. لذلك يقع ضرر مخالفة معنى الطريقة على الموقع الذي خالفه.

ثبات الأثر عند التكرار (idempotency)

وتجيب الطريقة عن سؤال ثان: ماذا يحدث لو وصل الطلب نفسه إلى الخادم مرتين؟

وقبل المصطلح، خذ زرين تعرفهما. زر استدعاء المصعد تضغطه مرة أو عشر مرات، فالنتيجة واحدة: المصعد قادم إلى دورك. وزر «أضف إلى السلة» كل ضغطة عليه تزيد قطعة في سلتك. فالأول أثره لا يتغير بالتكرار، والثاني يتراكم أثره مع كل ضغطة.

وتسمي المواصفة الطريقة من النوع الأول ثابتة الأثر (idempotent)، أي أن أثر عدة طلبات متطابقة بها هو أثر طلب واحد منها. والمقصود بالأثر حالة الخادم بعد الطلبات، لا الاستجابة التي تعود بها.

خذ طلب PUT الذي صحح نص التعليق رقم 17. بعد الطلب الأول صار النص Clear explanation, thanks. فإذا وصل الطلب نفسه مرة ثانية، كتب الخادم النص نفسه مكانه، فلم يتغير شيء عما كان بعد الطلب الأول. وسواء وصل الطلب مرة أو عشرا، فالحالة التي ينتهي إليها التعليق واحدة، ولهذا فطريقة PUT ثابتة الأثر.

وطريقة DELETE ثابتة الأثر كذلك، وإن بدت الاستجابتان مختلفتين. فالطلب الأول يحذف التعليق ويرد الخادم برمز نجاح، والطلب الثاني لا يجد تعليقا ليحذفه، فقد يرد عليه الخادم بالرمز 404. ومع ذلك فحالة الخادم بعد الطلبين هي حالته بعد الطلب الأول: التعليق رقم 17 لم يعد موجودا. والرمز المختلف يصف ما وجده الخادم حين وصله الطلب الثاني، ولا يعني أن هذا الطلب أحدث أثرا جديدا.

ثبات الأثر لا يعني أن الاستجابة تتكرر كما هي. يعني أن حالة الخادم بعد عدة طلبات متطابقة هي حالته بعد طلب واحد، ولو عاد الطلب الثاني برمز حالة مختلف.

أما طريقة POST فغير ثابتة الأثر، وهي حالة زر «أضف إلى السلة». فطلب نشر التعليق إذا وصل مرتين عالجته القائمة مرتين، فظهر تحت المقال تعليقان متطابقان برقمين مختلفين، 17 و18. وقد يكتب مطور الخادم برنامجه بحيث يكتشف التكرار فلا ينشئ نسخة ثانية، لكن هذا قرار في برنامجه لا تعلنه الطريقة، فلا يستطيع أحد من خارج الخادم أن يبني عليه.

وطريقة PATCH غير ثابتة الأثر في تعريفها، لأن أثر تكرارها يتوقف على التعليمات التي يحملها جسمها. فتعليمة «اجعل النص كذا» تنتهي بعد أي عدد من التكرارات إلى النص نفسه، أما تعليمة «أضف هذه القيمة إلى آخر القائمة» فتضيف القيمة مرة مع كل تكرار. والطريقة وحدها لا تكشف أي النوعين في الجسم، ولذلك يعاملها كل من هو خارج الخادم على أنها غير ثابتة الأثر.

والطرق الآمنة كلها ثابتة الأثر بطبيعتها، فقراءة المورد عشر مرات لا تطلب تغييرا، كما لا تطلبه قراءته مرة واحدة. وبهذا تجتمع الخاصيتان في جدول واحد:

الطريقة آمنة ثابتة الأثر
GET نعم نعم
HEAD نعم نعم
OPTIONS نعم نعم
PUT لا نعم
DELETE لا نعم
POST لا لا
PATCH لا لا

والجدول يصف ما تعلنه المواصفة عن كل طريقة، لا ما يفعله كل خادم فعلا. فلو كتب مطور خادما يضيف مبلغا إلى رصيد مع كل طلب PUT، لكان مخالفا لمعنى الطريقة، ولظل العملاء والوسطاء يعاملون طلبات PUT إليه على أنها ثابتة الأثر، فقد يعيدون الطلب إذا انقطع الاتصال فيضاف المبلغ مرتين. فمخالفة المعنى لا يعاقب عليها البروتوكول، وإنما يدفع ثمنها مستخدمو الموقع.

وللطرق خاصية ثالثة تذكرها المواصفة، هي قابلية الاستجابة للتخزين المؤقت (caching)، أي أن يحفظ المتصفح أو وسيط في الطريق نسخة من الاستجابة ويرد بها على طلب لاحق بدل الرجوع إلى الخادم. فاستجابات GET وHEAD قابلة للحفظ، واستجابات PUT وDELETE لا تحفظ، وأغلب أنظمة التخزين المؤقت لا تحفظ إلا استجابات GET وHEAD. أما متى تحفظ الاستجابة وكم تبقى صالحة، فذلك موضوع له موضعه في الدورة.

إعادة الطلب بعد انقطاع الاتصال

ولهاتين الخاصيتين ثمرة عملية تظهر في موقف يقع كل يوم على الشبكة: يرسل العميل طلبه كاملا، ثم ينقطع الاتصال قبل أن تصل الاستجابة. والعميل هنا في حيرة حقيقية: هل وصل الطلب إلى الخادم ونفذه ثم ضاعت الاستجابة في الطريق؟ أم لم يصل الطلب أصلا؟ ليس عنده ما يفرق بين الحالتين.

فإذا كانت الطريقة ثابتة الأثر، فالمخرج سهل: يفتح العميل اتصالا جديدا ويرسل الطلب مرة أخرى. فإن كان الطلب الأول قد نفذ، لم يضف التكرار شيئا؛ وإن لم يكن قد نفذ، نفذه الطلب الثاني. وفي الحالتين تنتهي الحال إلى ما أراده العميل. وتضرب المواصفة هذا المثال بطلب PUT تحديدا، وتجيز للعميل أن يعيد الطلب ثابت الأثر تلقائيا.

أما POST فإعادته تلقائيا مجازفة: فإن كان الطلب الأول قد نفذ، نشر التكرار تعليقا ثانيا أو خصم المبلغ مرة أخرى. لذلك توصي المواصفة بألا يعيد العميل طلبا غير ثابت الأثر من تلقاء نفسه، إلا إذا كانت لديه وسيلة يعرف بها أن التكرار لا يضر، أو أن الطلب الأول لم ينفذ. وتمنع البروكسي (proxy)، وهو وسيط يمرر طلبات العملاء إلى الخوادم، من إعادة هذه الطلبات تلقائيا منعا قاطعا.

وهنا يتضح سبب التحذير الذي بدأ به هذا الدرس. فالصفحة التي جاءت استجابة لطلب POST، إذا أعاد المستخدم تحميلها، أعاد المتصفح إرسال طلب POST نفسه. والمتصفح لا يعرف ماذا يفعل هذا التكرار في هذا الموقع: قد ينشر تعليقا ثانيا، وقد يكرر عملية شراء. فيقف ويسأل المستخدم قبل أن يرسله. أما صفحة المقال التي جاءت بطلب GET فيعيد طلبها دون سؤال، لأن تكرار القراءة لا يغير شيئا.

أما الخدمات التي تتلقى طلبات الدفع بطريقة POST فتحتاج وسيلة تجعل تكرار الطلب بلا ضرر، وهي مسألة في تصميم الواجهات البرمجية لها موضعها في الدورة.

طرق أخرى، وكيف يعلن المورد ما يقبله

الطرق الخمس السابقة هي الأكثر حضورا في المواقع والواجهات البرمجية، وفي المواصفة طرق أخرى تلقاها من حين إلى آخر:

  • HEAD: مر ذكرها مع GET، وتطلب الترويسات دون الجسم.
  • OPTIONS: يسأل بها العميل عن الخيارات التي يدعمها المورد، وترسلها المتصفحات تلقائيا قبل بعض الطلبات الموجهة إلى أصل (origin) غير أصل الصفحة.
  • CONNECT: تطلب من وسيط أن يفتح نفقا إلى خادم آخر، أي قناة ينقل عبرها البيانات بين الطرفين كما هي دون أن يقرأها.
  • TRACE: تطلب من الخادم أن يعيد إلى العميل نسخة من الطلب كما وصله، فتكشف ما غيره الوسطاء في الطريق.

وTRACE آمنة وثابتة الأثر مثل GET وHEAD وOPTIONS، أما CONNECT فليست آمنة ولا ثابتة الأثر.

ولا يقبل كل مورد كل الطرق. فقد تكون الطريقة معروفة للخادم، لكن هذا المورد بعينه لا يدعمها؛ وعندها يرد الخادم برمز الحالة 405، وتلزمه المواصفة بأن يضيف إلى الاستجابة ترويسة Allow تسرد الطرق التي يقبلها المورد. وتستطيع أن ترى ذلك بنفسك: المسار /delete في خدمة الصدى httpbin لا يقبل إلا الحذف، فإذا طلبته قراءة بـGET جاءك هذا الرد:

CODE
curl -i https://httpbin.org/delete
CODE
HTTP/2 405
date: Tue, 15 Sep 2026 10:17:14 GMT
content-type: text/html
content-length: 178
server: gunicorn/19.9.0
allow: OPTIONS, DELETE
...

تقول الترويسة allow إن هذا المورد يقبل OPTIONS وDELETE وحدهما. ولو أعدت الأمر مع الخيار -X DELETE، لعادت الاستجابة بالرمز 200 وبجسم يصف الطلب الذي وصل إلى الخدمة.

ما تعلنه الطريقة لمن لا يعرف الخادم

لكل طرف يمر به الطلب نافذة واحدة يطل منها على نية العميل، هي الطريقة. فالمتصفح وبرنامج الزحف والوسيط لا يرى أحدهم برنامج الخادم ولا يعرف ماذا سيفعل بالطلب، ومع ذلك يقرر كل منهم: هل يرسل الطلب من تلقاء نفسه؟ هل يعيده إن تعثر الاتصال؟ هل يحفظ استجابته ويرد بها لاحقا؟ وكل هذه القرارات تبنى على كلمة واحدة.

ولهذا كان الخلط بين PUT وPOST أكثر من خطأ في التسمية. فكلتاهما ترسل بيانات في الجسم، لكن PUT تعلن للجميع الحالة التي سينتهي إليها المورد، فيعرف من يرى الطلب أن تكراره لا يغير النتيجة. أما POST فلا تعلن إلا أن المورد سيعالج البيانات بقواعده، فيبقى أثرها وأثر تكرارها معروفا للخادم وحده، ويتعامل معها كل من سواه بحذر. فحين تختار طريقة لطلبك، أنت لا تخاطب الخادم وحده، بل كل من سيمر بهذا الطلب في الطريق.

اختبر فهمك

ثمانية أسئلة قصيرة تغطي أفكار الدرس. اختر إجابة ثم اضغط «تحقق» لترى إن كانت صحيحة ولماذا. وفي آخر الاختبار تظهر نتيجتك، ومع كل سؤال أخطأت فيه زر يعيدك إلى القسم الذي يشرحه.

أرسل متصفح الطلب POST /articles/http-message/comments وفي جسمه نص تعليق جديد. ما الذي يطلبه هذا الطلب من المورد؟
أن تصير قائمة التعليقات مطابقة للجسم المرسل
أن تعالج القائمة البيانات بقواعدها، كأن تنشئ منها تعليقا
أن ينشئ الخادم تعليقا في كل حال، لأن POST لا تعني إلا الإنشاء
أن يغير الخادم نص آخر تعليق في القائمة إلى النص المرسل
جسم POST بيانات يعالجها المورد المستهدف بحسب قواعده، وإنشاء تعليق يختار الخادم عنوانه أحد هذه الاستعمالات لا كلها. أما أن يصير المورد مطابقا للجسم فهو معنى PUT، وتغيير جزء من مورد قائم معنى PATCH.
POST: بيانات يعالجها المورد بقواعده
حالة التعليق رقم 17 هي {"author":"sara","text":"Clear explanation"}. أرسل العميل إلى عنوانه طلب PUT جسمه {"text":"Thanks"} وحده. ما الحالة التي يطلبها هذا الطلب؟
نص جديد هو Thanks مع بقاء author على قيمته
تعليق جديد برقم آخر نصه Thanks
إضافة Thanks إلى آخر النص الحالي
تعليق حالته {"text":"Thanks"} وحدها بلا author
جسم PUT هو الحالة الكاملة الجديدة للمورد، فالحقل الغائب عنه غائب عن الحالة المطلوبة. أما تغيير النص مع إبقاء author فهو ما يطلبه هذا الجسم مع PATCH.
PUT: حالة كاملة تحل محل حالة المورد
يزيد خادم مدونة عداد مشاهدات المقال في قاعدة البيانات مع كل طلب GET لصفحته. هل يبقى هذا الطلب آمنا بمعنى المواصفة؟
نعم، لأن العميل لم يطلب هذا التغيير ولا يحاسب عليه
لا، لأن أي كتابة في قاعدة البيانات تنقض الأمان
لا، إلا إذا أعاد الخادم العداد إلى قيمته بعد الرد
نعم، لأن HTTP يمنع طلب GET من تغيير أي بيانات
الأمان صفة لما يطلبه العميل، لا لكل ما يحدث على الخادم، فتسجيل الطلب وزيادة العداد لا يجعلان الطلب غير آمن. وHTTP لا يمنع الخادم من تغيير شيء عند GET، وإنما يحدد معنى الطريقة.
الطرق الآمنة (safe): طلبات لا تطلب تغييرا
جعل موقع حذف التعليق رابطا عاديا هو /comments/17?do=delete، ثم وجد تعليقات محذوفة لم يضغط أحد روابط حذفها. ما التفسير الأرجح؟
رابط GET لا يستطيع حذف شيء، فالعطل في قاعدة البيانات
HTTP يحول الروابط التي فيها delete إلى طلبات DELETE
برنامج يتبع الروابط طلبها بـGET لأنه يفترض أنها لا تغير شيئا
المتصفح أعاد طلبات قديمة لأن GET طريقة ثابتة الأثر
برامج الزحف وأدوات الطلب المسبق تطلب الروابط بـGET دون أن يطلبها أحد، لأن GET طريقة آمنة في المواصفة. والبروتوكول لا يمنع الخادم من الحذف عند GET، فوقع الحذف كما حدث مع Google Web Accelerator سنة 2005.
الطرق الآمنة (safe): طلبات لا تطلب تغييرا
أرسل عميل DELETE /articles/http-message/comments/17 مرتين، فعاد الأول برمز نجاح والثاني بالرمز 404. هل يتفق هذا مع ثبات الأثر في DELETE؟
لا، لأن الطلبين أعادا استجابتين مختلفتين
نعم، لأن DELETE طريقة آمنة لا تطلب تغييرا
نعم، لأن حالة الخادم بعد الطلبين هي حالته بعد الأول
لا، لأن الطلب الثاني فشل فلم يكتمل أثره
ثبات الأثر يخص حالة الخادم لا الاستجابة، والتعليق غير موجود بعد الطلب الأول وبعد الثاني. أما DELETE فليست آمنة، لأنها تطلب تغييرا في حالة الخادم.
ثبات الأثر عند التكرار (idempotency)
أي هذه الطلبات لا يمكن الافتراض أن تكراره ينتهي إلى أثر طلب واحد؟
PATCH يضيف قيمة إلى آخر قائمة في المورد
PUT يرسل حالة التعليق كاملة إلى عنوانه
DELETE يطلب إزالة التعليق رقم 17
GET يقرأ قائمة تعليقات المقال
أثر تكرار PATCH يتوقف على تعليماتها، وتعليمات الإضافة إلى آخر القائمة تضيف القيمة مرة مع كل تكرار. أما PUT وDELETE وGET فطرق ثابتة الأثر، تنتهي بعد التكرار إلى حالة الطلب الواحد.
ثبات الأثر عند التكرار (idempotency)
أي طريقة من هذه الطرق ثابتة الأثر وغير آمنة؟
GET
POST
PATCH
PUT
PUT تطلب تغيير حالة المورد فليست آمنة، وتكرارها ينتهي إلى الحالة نفسها فهي ثابتة الأثر. أما GET فآمنة وثابتة الأثر معا، وPOST وPATCH ليستا آمنتين ولا ثابتتي الأثر.
ثبات الأثر عند التكرار (idempotency)
أرسل تطبيق طلب POST لإتمام دفع، ثم انقطع الاتصال قبل وصول الاستجابة. لماذا لا يعيد العميل الطلب تلقائيا؟
لأن الخادم يرفض كل طلب POST يصله مرتين
لأن الطلب الأول ربما نفذ، وتكراره قد يدفع المبلغ مرة أخرى
لأن HTTP يمنع إعادة أي طلب بعد انقطاع الاتصال
لأن انقطاع الاتصال يعني أن الطلب لم يصل إلى الخادم
العميل لا يعرف هل نفذ الطلب قبل انقطاع الاتصال، وPOST ليست ثابتة الأثر، فتوصي المواصفة بألا يعيدها تلقائيا. أما الطلب ثابت الأثر مثل PUT فتجوز إعادته تلقائيا، لأن تكراره لا يضيف أثرا.
إعادة الطلب بعد انقطاع الاتصال
التصنيفات

قد تُعجبك هذه المشاركات

4419914284293787151

العلامات المرجعية

قائمة العلامات المرجعية فارغة ... قم بإضافة مقالاتك الآن

    البحث