eshpilen
شنبه 03 اردیبهشت 1390, 01:13 صبح
این مورد نسبتا جالبی هست که بنظرم بیشتر یا حداقل خیلی از افراد نمیدونن.
میدونید که پروتکل HTTP متدهای مختلفی داره که متداول ترین اونا که توسعه دهندگان وب باهاشون سروکار دارن، متدهای GET و POST هستن.
متد GET همون متدی هست که موقع فراخوانی مستقیم آدرسها اجرا میشه. مثلا موقعی که روی یک لینک کلیک میکنید. در این روش تمام متغییرها، درصورت وجود، در URL ارسال میشن.
متد POST متدی هست که برای ارسال فرمها استفاده میشه. در این روش تمام متغییرهای فرم در بدنهء درخواست HTTP ارسال میشن و در URL وجود ندارن و دیده نمیشن. البته URL ای که یک درخواست POST بهش ارسال میشه میتونه شامل پارامترهای URL هم باشه که به اینصورت میشه گفت اطلاعات دیگه ای رو هم (البته معمولا بصورت ثابت و hard-code شده) در URL مشخص و ارسال کردیم، اما متد استفاده شده بهرحال POST هست و نه GET، چون درخواست HTTP ما محتوی دیتای ارسالی توسط کاربر در بندهء خودش هست و متدی که توسط مرورگر در درخواست مشخص میشه از نوع POST خواهد بود.
تگ فرمی با متد GET به این شکل نوشته میشه:
<form ... method="get">
و تگ فرمی که اطلاعات رو با متد POST ارسال میکنه به این شکل:
<form ... method="post">
بر اساس پروتکل HTTP، متد GET نباید برای درخواست هایی که هربار اجرای اونها موجب تغییری در سرور میشه مورد استفاده قرار بگیره. اونطور که بنده فهمیدم، کاربرد اصلی متد GET برای دریافت اطلاعات هست، نه ارسال اطلاعاتی که بخصوص هر بار باعث ایجاد تغییراتی در سمت سرور میشن. شاید از اسم این متد هم بشه به هدف واقعی اون که گرفتن اطلاعات موجود بر روی سرور هست، و نه ذخیره کردن یا تغییر دادن چیزی، پی برد.
بطور مثال این لینک رو درنظر بگیرید:
http://yoursite.com/add_credit.php?to=3546&amount=500
همونطور که باید قابل حدس باشه، این لینک باعث میشه که مقدار 500 تا اعتبار به کاربری با آیدی 3546 اضافه بشه. مسلما اجرای این درخواست موجب تغییر وضعیتی در سمت سرور میشه. یعنی مقدار اعتبار کاربر در دیتابیس به ازای هربار فراخوانی این لینک اضافه میشه. ضمنا این فقط فراخوانی بار اول نیست که تغییر ایجاد میکنه، بلکه هربار فراخوانی وضعیت سمت سرور رو تغییر میده و اثر جدیدی میذاره. تغییری که بر اثر دو درخواست پشت سرهم ایجاد میشه برابر با اثر یک درخواست نیست.
گرچه چنین کاربردهایی که مثال زدیم خیلی متداول هستن، اما در اصل این کاربردها برخلاف استانداردهای ذکر شده در پروتکل HTTP هستن.
خب این طبیعی هست چون خیلی افراد از این قضیه اطلاع ندارن و البته شاید یکی از دلایل دیگر این نقض استاندارد هم این باشه که کار کردن با متد GET بخصوص درموقع توسعه و تست، راحتتر و سریعتر از متد POST هست. فرمانها و متغییرها و مقدار اونها رو میشه در URL براحتی دید و دستکاری کرد و نوشتن کد درخواست های GET در برنامه معمولا راحتتر و سریعتر هست (نیازی به یک فرم نداریم و یک خط آدرس بسادگی این کار رو انجام میده).
روش صحیح برای چنین اعمالی استفاده از متد POST هست. یعنی باید یک فرم داشته باشید که سابمیت بشه. حالا مثلا با کلیک بر روی یک دکمه یا هر روش دیگری.
اگر دقت کنید رفتار مرورگرها هم منطبق با استاندارد پروتکل HTTP هست.
بطور مثال وقتی شما میخواید صفحه ای رو که با متد POST فراخوانی شده رفرش کنید، مرورگر به شما پیام هشداری نشون میده مبنی بر اینکه با فراخوانی مجدد اون صفحه، اطلاعات POST شده مجددا ارسال خواهند شد و از شما بابت این کار تایید میخواد، چون ارسال مجدد این اطلاعات که به معنای ارسال مجدد یک درخواست POST هست میتونه (البته لزوما اینطور نیست) موجب تغییر وضعیت مجدد در سمت سرور بشه و شاید هدف شما از سابمیت کردن مجدد اون اطلاعات واقعا ایجاد یک تغییر جدید نبوده باشه.
خب این بود خلاصهء ماجرا!
البته در این موضوع هنوز برای بنده ابهامات و سوالاتی وجود داره.
درمورد چیزهایی مثل http://yoursite.com/add_credit.php?to=3546&amount=500 تقریبا مطمئن هستم که متد GET استاندارد نیست و باید از متد POST استفاده بشه.
اما بطور مثال درمورد صحیح بودن یا نبودن استفاده از متد GET برای درخواست هایی که فقط در بار اول موجب ایجاد تغییری در سمت سرور میشن و در درخواست های بعدی بی اثر هستن مطمئن نیستم. بطور مثال آدرسی مثل http://yoursite.com/delete_post.php?post_id=68820 فرضا باعث حذف پست شماره 68820 میشه و اگر فرض کنیم برای همیشه فقط یک پست تحت این شماره در سایت ما وجود خواهد داشت و حتی درصورت حذف، هیچوقت پست دیگری با شمارهء مشابه در سایت ثبت نخواهد شد، پس این آدرس فقط یک بار موجب ایجاد تغییر میشه و هر تعداد فراخوانی های بعدی اون تفاوتی با یک فراخوانی بار اول ایجاد نخواهند کرد.
برطبق توضیحی که در بخش Idempotent methods and web applications مقالهء ویکیپدیا درمورد پروتکل HTTP آمده، بنظر میرسه استفاده از متد GET در چنین مواردی مطابق استاندارد باشه. اما در بخش Request methods همین مقاله آمده:
Requests using GET "SHOULD NOT have the significance of taking an action other than retrieval".
ترجمه: درخواستهایی که از متد GET استفاده میکنند نباید معنایی/اهمیتی غیر از انجام عمل بازیابی داشته باشند.
بنابراین بنده در تردید جدی هستم که استفاده از متد GET برای انجام عملی مثل حذف یک چیز، حتی اگر فقط یک نسخه از اون برای یک بار در سایت وجود خواهد داشت، مطابق استاندارد پروتکل HTTP باشه.
اما مجددا در بخش dempotent methods and web applications بیان شده که:
Methods PUT and DELETE are defined to be idempotent (http://en.wikipedia.org/wiki/Idempotent), meaning that multiple identical requests should have the same effect as a single request.
ترجمه: متدهای PUT و DELETE (م: به ترتیب باعث آپلود و حذف یک موجودیت بر روی سرور میشن) Idempotent تعریف شده اند، بدین معنا که درخواست های چندباره باید اثر یک درخواست تنها را داشته باشند.
در اینجا این سوال پیش میاد که آیا یک درخواست مثلا از نوع DELETE، درسته که بعد از باعث شدن مثلا حذف یک فایل خاص از روی سرور اگر دوباره اجرا بشه اثری نداره، اما فرضا اگر بعد از مدتی فایل دیگری مجددا با همون آدرس و نام روی سرور بوجود آمد (اصلا این امکان درمورد این متدها وجود داره یا نه؟)، آیا اگر درخواست DELETE قبلی تکرار بشه موجب یک عملیات حذف جدید روی یک فایل جدید (و احتمالا متفاوت و بی ارتباط با فایل اول) نمیشه؟ و اگر چنین چیزی میتونه رخ بده و برای متدهای Idempotent چنین چیزی مجاز هست، پس احتمالا ما میتونیم این استدلال رو به موارد مشابه درمورد متد GET هم اعمال کنیم.
برای اینکه بیشتر ذهن شما رو حیران کنم مثال مبهم تری رو مطرح میکنم:
http://yoursite.com/empty_basket.php
این درخواست GET مشخصا سبد خرید کاربر رو خالی میکنه.
آیا استفاده از متد GET برای چنین عمیاتی مجاز هست؟
بار اول که سبد خرید خالی میشه فراخوانی مجدد این آدرس اثری نداره، اما اگر سبد خرید دوباره حاوی اقلامی بشه، فراخوانی مجدد این آدرس موجب انجام عملیات جدیدی خواهد شد. حتی بازهم ابهام بیشتری از جهت دیگر در این موارد هست، چون این تغییرات معمولا در سشن و مکان ذخیره سازی موقت صورت میگیرن و تغییرات پایداری رو روی سرور باعث نمیشن. این تغییرات فقط در محدودهء فعالیت جاری کاربر نقش و کاربرد موقتی دارن.
فعلا میرم یه سری به RFC پروتکل HTTP هم بزنم و ببینم چیز بیشتری میفهمم یا نه. البته این پروتکل رو قبلا یک بار کامل خونده بودم و چند بار هم اینطور بخشهای اونو مطالعه کردم، ولی دقیق یادم نمیاد.
=====================
منبع: http://en.wikipedia.org/wiki/Http#Idempotent_methods_and_web_applications
میدونید که پروتکل HTTP متدهای مختلفی داره که متداول ترین اونا که توسعه دهندگان وب باهاشون سروکار دارن، متدهای GET و POST هستن.
متد GET همون متدی هست که موقع فراخوانی مستقیم آدرسها اجرا میشه. مثلا موقعی که روی یک لینک کلیک میکنید. در این روش تمام متغییرها، درصورت وجود، در URL ارسال میشن.
متد POST متدی هست که برای ارسال فرمها استفاده میشه. در این روش تمام متغییرهای فرم در بدنهء درخواست HTTP ارسال میشن و در URL وجود ندارن و دیده نمیشن. البته URL ای که یک درخواست POST بهش ارسال میشه میتونه شامل پارامترهای URL هم باشه که به اینصورت میشه گفت اطلاعات دیگه ای رو هم (البته معمولا بصورت ثابت و hard-code شده) در URL مشخص و ارسال کردیم، اما متد استفاده شده بهرحال POST هست و نه GET، چون درخواست HTTP ما محتوی دیتای ارسالی توسط کاربر در بندهء خودش هست و متدی که توسط مرورگر در درخواست مشخص میشه از نوع POST خواهد بود.
تگ فرمی با متد GET به این شکل نوشته میشه:
<form ... method="get">
و تگ فرمی که اطلاعات رو با متد POST ارسال میکنه به این شکل:
<form ... method="post">
بر اساس پروتکل HTTP، متد GET نباید برای درخواست هایی که هربار اجرای اونها موجب تغییری در سرور میشه مورد استفاده قرار بگیره. اونطور که بنده فهمیدم، کاربرد اصلی متد GET برای دریافت اطلاعات هست، نه ارسال اطلاعاتی که بخصوص هر بار باعث ایجاد تغییراتی در سمت سرور میشن. شاید از اسم این متد هم بشه به هدف واقعی اون که گرفتن اطلاعات موجود بر روی سرور هست، و نه ذخیره کردن یا تغییر دادن چیزی، پی برد.
بطور مثال این لینک رو درنظر بگیرید:
http://yoursite.com/add_credit.php?to=3546&amount=500
همونطور که باید قابل حدس باشه، این لینک باعث میشه که مقدار 500 تا اعتبار به کاربری با آیدی 3546 اضافه بشه. مسلما اجرای این درخواست موجب تغییر وضعیتی در سمت سرور میشه. یعنی مقدار اعتبار کاربر در دیتابیس به ازای هربار فراخوانی این لینک اضافه میشه. ضمنا این فقط فراخوانی بار اول نیست که تغییر ایجاد میکنه، بلکه هربار فراخوانی وضعیت سمت سرور رو تغییر میده و اثر جدیدی میذاره. تغییری که بر اثر دو درخواست پشت سرهم ایجاد میشه برابر با اثر یک درخواست نیست.
گرچه چنین کاربردهایی که مثال زدیم خیلی متداول هستن، اما در اصل این کاربردها برخلاف استانداردهای ذکر شده در پروتکل HTTP هستن.
خب این طبیعی هست چون خیلی افراد از این قضیه اطلاع ندارن و البته شاید یکی از دلایل دیگر این نقض استاندارد هم این باشه که کار کردن با متد GET بخصوص درموقع توسعه و تست، راحتتر و سریعتر از متد POST هست. فرمانها و متغییرها و مقدار اونها رو میشه در URL براحتی دید و دستکاری کرد و نوشتن کد درخواست های GET در برنامه معمولا راحتتر و سریعتر هست (نیازی به یک فرم نداریم و یک خط آدرس بسادگی این کار رو انجام میده).
روش صحیح برای چنین اعمالی استفاده از متد POST هست. یعنی باید یک فرم داشته باشید که سابمیت بشه. حالا مثلا با کلیک بر روی یک دکمه یا هر روش دیگری.
اگر دقت کنید رفتار مرورگرها هم منطبق با استاندارد پروتکل HTTP هست.
بطور مثال وقتی شما میخواید صفحه ای رو که با متد POST فراخوانی شده رفرش کنید، مرورگر به شما پیام هشداری نشون میده مبنی بر اینکه با فراخوانی مجدد اون صفحه، اطلاعات POST شده مجددا ارسال خواهند شد و از شما بابت این کار تایید میخواد، چون ارسال مجدد این اطلاعات که به معنای ارسال مجدد یک درخواست POST هست میتونه (البته لزوما اینطور نیست) موجب تغییر وضعیت مجدد در سمت سرور بشه و شاید هدف شما از سابمیت کردن مجدد اون اطلاعات واقعا ایجاد یک تغییر جدید نبوده باشه.
خب این بود خلاصهء ماجرا!
البته در این موضوع هنوز برای بنده ابهامات و سوالاتی وجود داره.
درمورد چیزهایی مثل http://yoursite.com/add_credit.php?to=3546&amount=500 تقریبا مطمئن هستم که متد GET استاندارد نیست و باید از متد POST استفاده بشه.
اما بطور مثال درمورد صحیح بودن یا نبودن استفاده از متد GET برای درخواست هایی که فقط در بار اول موجب ایجاد تغییری در سمت سرور میشن و در درخواست های بعدی بی اثر هستن مطمئن نیستم. بطور مثال آدرسی مثل http://yoursite.com/delete_post.php?post_id=68820 فرضا باعث حذف پست شماره 68820 میشه و اگر فرض کنیم برای همیشه فقط یک پست تحت این شماره در سایت ما وجود خواهد داشت و حتی درصورت حذف، هیچوقت پست دیگری با شمارهء مشابه در سایت ثبت نخواهد شد، پس این آدرس فقط یک بار موجب ایجاد تغییر میشه و هر تعداد فراخوانی های بعدی اون تفاوتی با یک فراخوانی بار اول ایجاد نخواهند کرد.
برطبق توضیحی که در بخش Idempotent methods and web applications مقالهء ویکیپدیا درمورد پروتکل HTTP آمده، بنظر میرسه استفاده از متد GET در چنین مواردی مطابق استاندارد باشه. اما در بخش Request methods همین مقاله آمده:
Requests using GET "SHOULD NOT have the significance of taking an action other than retrieval".
ترجمه: درخواستهایی که از متد GET استفاده میکنند نباید معنایی/اهمیتی غیر از انجام عمل بازیابی داشته باشند.
بنابراین بنده در تردید جدی هستم که استفاده از متد GET برای انجام عملی مثل حذف یک چیز، حتی اگر فقط یک نسخه از اون برای یک بار در سایت وجود خواهد داشت، مطابق استاندارد پروتکل HTTP باشه.
اما مجددا در بخش dempotent methods and web applications بیان شده که:
Methods PUT and DELETE are defined to be idempotent (http://en.wikipedia.org/wiki/Idempotent), meaning that multiple identical requests should have the same effect as a single request.
ترجمه: متدهای PUT و DELETE (م: به ترتیب باعث آپلود و حذف یک موجودیت بر روی سرور میشن) Idempotent تعریف شده اند، بدین معنا که درخواست های چندباره باید اثر یک درخواست تنها را داشته باشند.
در اینجا این سوال پیش میاد که آیا یک درخواست مثلا از نوع DELETE، درسته که بعد از باعث شدن مثلا حذف یک فایل خاص از روی سرور اگر دوباره اجرا بشه اثری نداره، اما فرضا اگر بعد از مدتی فایل دیگری مجددا با همون آدرس و نام روی سرور بوجود آمد (اصلا این امکان درمورد این متدها وجود داره یا نه؟)، آیا اگر درخواست DELETE قبلی تکرار بشه موجب یک عملیات حذف جدید روی یک فایل جدید (و احتمالا متفاوت و بی ارتباط با فایل اول) نمیشه؟ و اگر چنین چیزی میتونه رخ بده و برای متدهای Idempotent چنین چیزی مجاز هست، پس احتمالا ما میتونیم این استدلال رو به موارد مشابه درمورد متد GET هم اعمال کنیم.
برای اینکه بیشتر ذهن شما رو حیران کنم مثال مبهم تری رو مطرح میکنم:
http://yoursite.com/empty_basket.php
این درخواست GET مشخصا سبد خرید کاربر رو خالی میکنه.
آیا استفاده از متد GET برای چنین عمیاتی مجاز هست؟
بار اول که سبد خرید خالی میشه فراخوانی مجدد این آدرس اثری نداره، اما اگر سبد خرید دوباره حاوی اقلامی بشه، فراخوانی مجدد این آدرس موجب انجام عملیات جدیدی خواهد شد. حتی بازهم ابهام بیشتری از جهت دیگر در این موارد هست، چون این تغییرات معمولا در سشن و مکان ذخیره سازی موقت صورت میگیرن و تغییرات پایداری رو روی سرور باعث نمیشن. این تغییرات فقط در محدودهء فعالیت جاری کاربر نقش و کاربرد موقتی دارن.
فعلا میرم یه سری به RFC پروتکل HTTP هم بزنم و ببینم چیز بیشتری میفهمم یا نه. البته این پروتکل رو قبلا یک بار کامل خونده بودم و چند بار هم اینطور بخشهای اونو مطالعه کردم، ولی دقیق یادم نمیاد.
=====================
منبع: http://en.wikipedia.org/wiki/Http#Idempotent_methods_and_web_applications