PDA

View Full Version : نکات و ابهاماتی درمورد کاربرد متدهای GET و POST - متدهای Idempotent و غیر Idempotent



eshpilen
شنبه 03 اردیبهشت 1390, 00: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

eshpilen
شنبه 03 اردیبهشت 1390, 01:11 صبح
من الان بخشهای مرتبط RFC رو نگاهی کردم.
الان یخورده بیشتر مطمئن شدم که متد GET کلا نباید برای درخواست هایی که موجب ایجاد تغییر در سمت سرور میشن استفاده بشه. اصلا شاید نه فقط تغییر در سمت سرور، بلکه بطور کلی هر عملیاتی که ممکنه اثر مهمی روی کاربر یا دیگران داشته باشه!
چون ظاهرا نه تنها متد Idempotent و غیر Idempotent داریم، بلکه متدهایی هم داریم که Safe تعریف شدن و متد GET جزو اونهاست.
البته بازم 100% مطمئن نیستم.

ضمنا جواب این مسئله هرچی باشه بازهم مقداری تردید درمواردی مثل http://yoursite.com/empty_basket.php باقی میمونه بنظرم. چون نمیشه خالی کردن سبد خرید رو عملی خطرناک تلقی کرد و اثر پایدار مهمی روی سرور یا جای دیگه نمیذاره.

متاسفانه منابعش مثال نزدن. اگر نظیر چنین مواردی رو مثال میزدن احتمالا مشکلات و بی اطلاعی و مشکل در نتیجه گیری قطعی خیلی کمتر میبود.

===================

منبع: http://tools.ietf.org/html/rfc2616#section-9.1

UnnamE
شنبه 03 اردیبهشت 1390, 07:41 صبح
يه چيز و من متوجه نشدم، پس اطلاعاتي مث مشخصات سيستم كاربر يا منطقه زماني و اين حرفا نبايد از GET گرفته بشن؟

eshpilen
شنبه 03 اردیبهشت 1390, 08:30 صبح
فکر نکنم اینطور چیزا مشکلی داشته باشن. چون اینا بیشتر تنظیمات نمایشی هستن و دیتایی که باعث تغییر یا ذخیرهء پایدار چیزی در سرور میشه نیستن.
البته اگر این تنظیمات رو کاربر داره برای اکانت خودش انجام میده که بصورت دائمی ذخیره بشه و شما این تنظیمات رو بوسیلهء متد GET ارسال کنید احتمالا مشکل داشته باشه (البته قبلا گفتم که هنوز خودم سر این مورد که عملیاتی فقط در درخواست اول تغییر ایجاد میکنه اصلا نباید متد GET باشه مقداری تردید دارم و از منابع نتونستم به قطعیت برسم). بهرحال کمتر جایی اینطور هست. مثلا شما تنظیمات مشابه رو در این فروم یا جای انجام میدید معمولا بوسیلهء متد GET این اطلاعات ارسال نمیشه، بلکه بصورت فرمی هست که با متد POST ارسال میشه.

بطور کلی تنظیمات نمایشی فکر نمیکنم مشکلی داشته باشن. اینا چیزهای موقتی هستن که جایی بصورت دائمی ثبت نمیشن. یجورایی on the fly هستن و جزو ویژگیهای خود لینک ها یا حداکثر تنظیمات نمایشی موقتی به شمار میان.

eshpilen
شنبه 03 اردیبهشت 1390, 08:36 صبح
البته بنظرم این موضوع در واقعیت بیشتر حالت تئوریک داره و/چون بیشتر افراد حتی ازش اطلاع ندارن، و اونایی هم که اطلاع دارن (خیلی وقتا) رعایتش نمیکنن.
ولی بهرحال دونستنش بنظرم مفیده و بعضی جاها ممکنه کمک عملی بکنه و بعضی تصمیمگیری های ما رو تغییر بده.

mtchabok
شنبه 03 اردیبهشت 1390, 10:57 صبح
از نظر من این مواردی که گفته شده بحث استاندارد سازی در پروتکول http داره و هر برنامه نویس به هر صورت که دلخواهش هس می تونه استفاده کنه ولی به هر حال در خواست های get در مرورگرها کش میشن و نتیجه اونرو هم نگه میدارن ، مرورگرها با Get مثل آدرسهای معمولی برخورد میکنن پس میشه به این نتیجه دست پیدا کرد که در صفحاتی که از طریق get اطلاعاتی برای تغییر در سرور ارسال میشن غیر استاندارد هستن و این درخواستها در مرورگر ثبت میشن و احتمال ارسال مجدد از طریق مرورگر وجود داره . مثلا در مواقعی که ارتباطات دچار اختلال جزئی بشه مرورگر سعی میکنه که درخواست بفرسته و این احتمال ارسال چند درخواست رو بالا میبره . در ضمن همونطوریکه خودتون بیان کردید عکس العمل مرورگر به ارسال مجدد اطلاعات post با مجوزی از کاربر امکان پذیر هس که همین نشاندهنده این هس که post برای ایجاد تغییر در سرور استاندارد شده و از get حدالامکان نباید استفاده شود . ولی بازم میگم که به صورت کلی این برنامه نویس هس که تشخیص میده از کدومشون برای چه کاری استفاده کنه . ولی بهتره که از استانداردهای تبعیت کرد .

alonemm
شنبه 03 اردیبهشت 1390, 15:45 عصر
باسلام:
در این مورد باید بگم که با شرط های کنترلی میشه بعضی موارد رو حل کرد.
مثل در این باره که گفتید یک صفحه که سبد خرید رو خالی میکنه.
اگر سشنی داشته باشید که تعداد کالا ها رو در اون ذخیره کرده باشیم و در بار اول در صفحه مربوطه که سبد خرید خالی میشه این سشن هم مقدارش 0 میشه.
بعد در در خواست هایی بعد اول چک میشه که سشن تعداد کالا از 0 بیشتر باشه و در این صورت هیچ عملیاتی انجام نمیشه.
ولی در کل هرچه بشه از POST استفاده کرد نسبت به GET امنیت بالاتری داریم.


موفق باشید.