توجه ! این یک نسخه آرشیو شده می باشد و در این حالت شما عکسی را مشاهده نمی کنید برای مشاهده کامل متن و عکسها بر روی لینک مقابل کلیک کنید : State Managementدر ASP. NET 2.0 ( مباحث كامل )
يكی از مهمترين تفاوت های موجود بين برنامه های وب و Desktop ، مديريت state است كه در آن می بايست به اين پرسش پاسخ داده شود كه نحوه نگهداری اطلاعات در ارتباط با كاربر جاری به چه صورت است ؟
در يك برنامه سنتی ويندوز ، state بطور اتوماتيك مديريت می گردد . حافظه به حد فراوان يافت می شود و همواره در دسترس است . در برنامه های وب داستان بگونه ای ديگر است . هزاران كاربر ممكن است بطور همزمان برنامه ای مشابه را بر روی كامپيوتری يكسان ( سرويس دهنده وب ) اجراء و هر يك از آنان از طريق پروتكل HTTP ( برگرفته شده از Hypertext Transfer Protocol) كه دارای ماهيتی stateless است با سرويس دهنده وب ارتباط برقرار نمايند . مجموعه شرايط فوق باعث شده است كه نتوان برنامه های وب را با سناريوئی دقيقا" مشابه با برنامه های سنتی ويندوز طراحی و پياده سازی كرد .
هيچگونه فريمورك برنامه نويسی وب ، صرفنظر از ميزان پيشرفته بودن آن ، نمی تواند ماهيت stateless بودن پروتكل HTTP را تغيير دهد. پس از هر درخواست و پاسخ به آن ، ارتباط منطقی سرويس گيرنده با سرويس دهنده قطع خواهد شد . معماری فوق ، اين اطمينان را ايجاد می نمايد كه برنامه های وب بتوانند به هزاران كاربر بطور همزمان و بدون نگرانی در خصوص حافظه پاسخ دهند . استفاده از روش های مختلف برای ذخيره اطلاعات بين درخواست های متعدد يك كاربر و بازيابی آنها در زمانی كه به آنها نياز است از جمله مشكلات معماری فوق برای پياده كنندگان برنامه های وب محسوب می گردد .
آشنائی و درك مناسب نسبت به محدوديت های state ، يكی از مفاهيم كليدی در زمان ايجاد برنامه های وب كارآ و قدرتمند است .
در مجموعه مقالاتی كه در اين خصوص آماده و بتدريج بر روی سايت منتشر خواهد شد به بررسی موارد زير خواهيم پرداخت :
* آشنائی با مفاهيم ، جايگاه و لزوم مديريت state در برنامه های وب
* آشنائی با پتانسيل های ارائه شده در ASP. NET 2.0 برای ذخيره سازی و مديريت اطلاعات
* آشنائی با گزينه های متفاوت موجود به منظور مديريت state نظير View state ، Session state ، كوكی های سفارشی
* نحوه انتقال اطلاعات از يك صفحه به صفحه ديگر
مديريت state و مسائل در ارتباط با آن
در يك برنامه سنتی ويندوز ، كاربران با يك برنامه در حال اجراء بطور پيوسته ارتباط برقرار می نمايند . بخشی از حافظه موجود بر روی كامپيوتر Desktop برای ذخيره تنظيمات جاری اطلاعات محيط كار كاربر اختصاص داده می شود .
در يك برنامه وب ، داستان كاملا" متفاوت است . شايد از ديد كاربران يك سايت حرفه ای اينگونه برداشت شود كه يك برنامه بطور مستمر در حال اجراء است و به آنان سرويس های لازم را می دهد . علی رغم اين كه ظاهر موضوع درست بنظر می آيد ولی در پس پرده داستان بگونه ای ديگر دنبال می شود . برنامه های وب از يك الگوی دستيابی غيرمتصل كارآ استفاده می نمايند . در اين الگو ، سرويس گيرنده پس از ارتباط با سرويس دهنده از آن درخواست يك صفحه را می نمايد . پس از پاسخ به سرويس گيرنده ،ارتباط منطقی ايجاد شده قطع و سرويس دهنده بی خيال هر گونه اطلاعاتی در رابطه با سرويس گيرنده می گردد . پس از دريافت صفحه درخواستی توسط سرويس گيرنده ، برنامه اجراء خود را متوقف و ASP.NET engine اشياء مربوط به صفحه را دور می اندازد .
با توجه به اين كه سرويس گيرندگان لازم است در اكثر موارد صرفا" برای چندين ثانيه متصل باشند ، يك سرويس دهنده وب می تواند به هزاران درخواست با كارآئی مطلوب پاسخ دهد .
در صورتی كه لازم است اطلاعات بين چندين عمليات كاربر نگهداری شوند ، می بايست از راهكارهای مختلفی به منظور مديريت state استفاده كرد .
View state
همانگونه كه اطلاع داريد كنترل های سرويس دهنده ASP.NET از view state برای بخاطر سپردن state استفاده می نمايند . اطلاعات view state در يك فيلد مخفی نگهداری شده و بطور اتوماتيك پس از هر postback برای سرويس دهنده ارسال می گردد . view state محدود به كنترل های سرويس دهنده نمی گردد و در صورت ضرورت می توان مجموعه ای از اطلاعات مورد نياز را مستقيما" در مجموعه view state ذخيره تا امكان بازيابی آنها پس از هر postback فراهم شود . نوع های مختلفی را می توان در view state ذخيره نمود . نوع های داده ساده و اشياء سفارشی نمونه هائی در اين زمينه می باشند .
خصلت ViewState صفحه، مجموعه view state را ارائه می نمايد . اين خصلت يك نمونه از كلاس مجموعه StateBag است .برای اضافه كردن و حذف آيتم هائی در اين كلاس ، از گرامری مشابه با يك ديكشنری استفاده می گردد كه در آن هر آيتم دارای يك نام منحصر بفرد است .
كد زير نحوه استفاده از view state را نشان می دهد .
ViewState("Counter") = 1
دستور فوق ، مقدار 1 را در مجموعه view state قرار داده و به آن يك نام منحصربفرد را نسبت می دهد ( Counter ) . در صورتی كه آيتم ديگری با همين نام در view state موجود نباشد ، يك آيتم جديد بطور اتوماتيك به آن اضافه می گردد . در صورتی كه يك آيتم با نام Counter در view state موجود باشد ، با مقدار فوق جايگزين می گردد .
برای بازيابی آيتم های ذخيره شده در view state از نام نسبت داده شده به هر يك از آنها استفاده می گردد . همچنين ، لازم است كه مقدار بازيابی شده را با استفاده از گرامر casting به نوع داده مناسب تبديل نمود چراكه مجموعه ViewState تمامی آينم ها را به عنوان اشياء عام ذخيره می نمايد تا بتواند با نوع های داده مختلف سرو كار داشته باشد .
كد زير نحوه بازيابی مقدار نسبت داده شده به Counter از view state و تبديل آن به يك عدد صحيح را نشان می دهد .
Dim counter As Integer
counter = CType(ViewState("Counter"), Integer)
در صورت عدم وجود اطلاعات مورد نظر در view state با يك NullReferenceException مواجه خواهيم شد . بنابراين ، لازم است كه همواره قبل از بازيابی و تبديل داده ذخيره شده در view state از وجود آن در ساختار فوق اطمينان حاصل نمود .
برای آشنائی بيشتر با نحوه بكارگيری view state در برنامه های وب به بررسی يك نمونه مثال كاربردی خواهيم پرداخت .
مثال : ثبت تعداد دفعاتی كه بر روی يك دكمه كليك می گردد
كد زير يك برنامه ساده شمارنده را نشان می دهد كه در آن تعداد دفعاتی كه بر روی يك دكمه كليك می شود تشخيص داده شده و تعداد آن در خروجی نمايش داده می شود . بدون استفاده از يك راهكار مناسب برای مديريت state ، شمارنده بطور دائم عدد 1 را در خروجی نشان خواهد داد .
برای ايجاد خروجی مورد نظر ، می بايست از يك راهكار مناسب (view state ) جهت مديريت state استفاده گردد .
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Sub cmdIncrement_Click(ByVal sender As Object,ByVal e As EventArgs) Handles cmdIncrement.Click
Dim Counter As Integer
If ViewState("Counter") Is Nothing Then
Counter = 1
Else
Counter = CType(ViewState("Counter"), Integer) + 1
End If
ViewState("Counter") = Counter
lblCount.Text = "مقدار شمارنده برابر است با : " & Counter.ToString()
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>تست view state </title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server">
<div>
<asp:Button ID="cmdIncrement" runat="server"
Text="افزايش شمارنده" Font-Names="Tahoma" /><br /><br />
<asp:Label ID="lblCount" runat="server"
Font-Names="Tahoma"></asp:Label>
</div>
</form>
</body>
</html>
در كد فوق قبل از تلاش برای بازيابی آيتم مورد نظر از view state ، وجود آن در ساختار فوق بررسی می گردد . شكل 1 خروجی برنامه فوق را نشان می دهد .
برای حل مسئله مديريت state در مثال فوق و نگهداری مقدار counter در بين چندين postback از روش هائی ديگر نيز می توان استفاده كرد . به عنوان مثال ، می توان برای كنترل سرويس دهنده label ويژگی view state را فعال و از label برای ذخيره مقدار counter استفاده نمود . هر مرتبه كه بر روی دكمه "افزايش شمارنده " كليك گردد ، مقدار جاری از طريق خصلت text كنترل label بازيابی و پس از تبديل به يك عدد صحيح در خروجی نمايش داده می شود .
از روش فوق نمی توان همواره به عنوان يك راهكار مناسب استفاده كرد . مثلا" ممكن است قصد ايجاد برنامه ای را داشته باشيم كه تعداد دفعاتی را كه بر روی يك دكمه كليك می گردد ثبت نمايد ولی قصد نمايش نتايج را در خروجی نداشته باشيم . در چنين مواردی می توان همچنان اطلاعات را در يك كنترل سرويس دهنده ذخيره نمود ولی مجبور خواهيم بود كه آن را مخفی نگاه داريم .
پس از انجام تمامی اين كارها ، به چيزی می رسيم كه view state آن را در اختيار ما قرار می دهد . view state ، اطلاعات را بطور اتوماتيك در يك فيلد مخفی خاص در صفحه نگهداری می نمايد . با توجه به اين كه ASP. NET با جزئيات اين كار سروكار دارد ، كد نوشته شده توسط پياده كنندگان از خوانائی بيشتری برخوردار خواهد بود .
(بخش دوم)
در بخش اول به ضرورت مديريت state در برنامه های وب اشاره و در ادامه به بررسی اولين گزينه موجود (view state ) پرداختيم . در اين بخش با نحوه ايمن سازی اطلاعات ذخيره شده در view state آشنا خواهيم شد .
اطلاعات view state در يك رشته درهم آميخته مشابه زير ذخيره می گردد .
کد:
<input type="hidden"
name="__VIEWSTATE"
value="/wEPDwUKMTUyMzMyNzc3NGRklXVE/6qqfC5AWkr1Yw0Xu5IpHg0="
/>
به موازات اضافه كردن اطلاعات بيشتر به view state ، طول اين رشته طولانی تر خواهد شد . با توجه به اين كه مقدار ذخيره شده در رشته فوق به صورت متن شفاف نمی باشد ، بسياری از برنامه نويسان ASP.NET بر اين باور هستند كه داده ذخيره شده در view state به صورت رمز شده است .ولی واقعيت اينچنين نيست . در واقع ، اطلاعات view state به سادگی در حافظه به يكديگر متصل شده و به يك رشته Base64 تبديل می شوند .يك هكر باهوش می تواند با استفاده از مهندسی معكوس رشته فوق ، view state را بررسی و از اطلاعات ذخيره شده در آن به سرعت آگاه گردد .
كد زير نحوه رمز كردن يك رشته معمولی را به يك رشته Base64 نشان می دهد .
کد:
Private Function EncodeBase64(ByVal input As String) As String
Dim strBytes() As Byte = System.Text.Encoding.UTF8.GetBytes(input)
Return System.Convert.ToBase64String(strBytes)
End Function
كد زير نحوه رمز گشائی يك رشته Base64 را به يك رشته معمولی نشان می دهد .
کد:
Private Function DecodeBase64(ByVal input As String) As String
Dim strBytes() As Byte = System.Convert.FromBase64String(input)
Return System.Text.Encoding.UTF8.GetString(strBytes)
End Function
در صورتی كه لازم است اطلاعات ذخيره شده در view state دارای ايمنی بيشتری باشند از دو گزينه مختلف می توان استفاده كرد :
گزينه اول : به ASP. NET اعلام شود كه از يك hash code استفاده نمايد. برخی اوقات از hash code به عنوان يك checksum قدرتمند پنهانی نيز ياد می شود . در چنين مواردی ، ASP. NET تمامی داده ذخيره شده در view state را بررسی و يك الگوريتم hashing را بر روی آن اعمال می نمايد . الگوريتم فوق يك سگمنت كوتاه از داده را ايجاد می نمايد كه در واقع همان hash code است . در ادامه ، كد فوق به انتهای داده ذخيره شده در view state اضافه می گردد .
زمانی كه صفحه post back می گردد ، ASP. NET داده موجود در view state را بررسی و مجددا" hash code را با استفاده از فرآيندی مشابه توليد می نمايد . در ادامه مقدار محاسبه شده با مقدار موجود در رشته مقايسه می گردد تا اين اطمينان حاصل شود كه داده ذخيره شده در view state تغيير نكرده باشد .
در صورتی كه يك كاربر بدانديش بخشی از داده موجود در view state را تغيير دهد ، ASP. NET يك hash code را توليد خواهد كرد كه با كد ذخيره شده در انتهای view state مطابقت نخواهد كرد . در صورت تحقق چنين شرايطی ، postback بطور كامل ناديده گرفته خواهد شد .
شايد در ذهن شما اين موضوع مطرح شده باشد كه يك كاربر باهوش می تواند با بكارگيری ترفندهائی بر مشكل اشاره شده غلبه كرده و علاوه بر توليد اطلاعات نادرست ، يك hash code مناسب و منطبق بر اطلاعات ذخيره شده در view state را نيز توليد نمايد . در پاسخ می بايست به اين نكته اشاره كرد كه كاربران بدانديش قادر به توليد hash code صحيح نخواهند بود چراكه آنان دارای كليد رمزنگاری مشابه ASP. NET نمی باشند . اين بدان معنی است كه hash code توليد شده با وضعيت موجود نمی تواند مطابقت نمايد .
hash code بطور پيش فرض فعال است . بنابراين در صورت تمايل به استفاده از پتانسيل فوق ، لازم نيست كه مراحل اضافه ای را دنبال نمود . در برخی موارد پياده كنندگان ويژگی فوق را غيرفعال می نمايند تا از مشكلات احتمالی موجود در يك web farm پيشگيری نمايند . در چنين وضعيتی ، سرويس دهندگان مختلف دارای كليدهای مختلفی می باشند و مشكل زمانی اتفاق می افتد كه پس از post back صفحه ، يك سرويس دهنده جديد آن را دريافت نمايد .
در يك محيط web farm كليد می بايست در بين تمامی سرويس دهندگان يكسان باشد . در صورتی كه كليد يكسان نباشد و صفحه برای يك سرويس دهنده متفاوت با سرويس دهنده ای كه صفحه را ايجاد كرده است ، post back گردد ، يك خطاء ايجاد خواهد شد .بنابراين در يك محيط web farm ، می بايست پياده كنندگان يك كليد را در فايل Machine.config مشخص نمايند ( در مقابل اين كه به ASP.NET اجازه داده شود كه اين كليد را بطور اتوماتيك ايجاد نمايد) .
برای غيرفعال كردن hash codes ، می بايست از خصلت enableViewStateMac عنصر <pages> در فايل web.config و يا machine.config به صورت زير استفاده كرد .
کد:
<configuration >
<system.web>
<pages enableViewStateMac="false" />
...
</system.web>
</configuration>
گزينه دوم : با ايجاد يك كد hash ، فريمورك ASP. NET اين موضوع را بررسی خواهد كرد كه آيا داده ذخيره شده در view state دستكاری شده است ؟ علی رغم ايجاد اين لايه امنيتی ، داده موجود در view state همچنان قابل مشاهده توسط كاربران بدانديش خواهد بود .
در صورتی كه بر روی داده ذخيره شده در view state حساسيت زيادی وجود داشته باشد و بخواهيم امنيت آن را افزايش دهيم ، می بايست رمزنگاری view state را فعال كرد . برای فعال كردن ويژگی فوق از خصلت ViewSta****cryptionMode به همراه دايركتيو page استفاده می گردد .
کد:
<%@Page ViewSta****cryptionMode="Always" %>
در صورت تمايل می توان از خصلت فوق در فايل پيكربندی نيز استفاده كرد .
کد:
<configuration >
<system.web>
<pages viewSta****cryptionMode="Always" />
...
</system.web>
</configuration>
به خصلت ViewSta****cryptionMode يكی از مقادير زير را می توان نسبت داد :
*
Always : همواره رمزنگاری انجام می شود .
*
Never : رمزنگاری انجام نخواهد شد .
*
Auto : رمزنگاری صرفا" در مواردی كه يك كنترل با صراحت آن را درخواست نمايد ، انجام خواهد شد .
گزينه پيش فرض Auto است . اين بدان معنی است كه يك كنترل با فراخوانی متد Page.RegisterRequiresViewSta****cryption رمزنگاری را درخواست می نمايد . در صورتی كه يك كنترل به دليل عدم داشتن اطلاعات حساس از متد فوق استفاده نكند ، view state رمز نخواهد شد و عمليات بيشتری جهت رمزنگاری به سيستم تحميل نخواهد شد . به عبارت ديگر ، يك كنترل زمانی می تواند دل خود را به خدمات ارائه شده توسط متد فوق خوش نمايد كه مقدار خصلت viewSta****cryptionMode ، معادل Auto در نظر گرفته شده باشد . در صورتی كه مقدار خصلت فوق Never در نظر گرفته شده باشد ، به درخواست كنترل ها جهت رمزنگاری پاسخ داده نخواهد شد.
با توجه به اين كه رمزنگاری عمليات بيشتری را به سرويس دهنده وب تحميل می نمايد ( هم در زمان رمزنگاری و هم در زمان رمزگشائی پس از هر post back ) در صورت عدم نياز به پتانسيل فوق و به منظور عدم تاثيرگذاری آن بر روی كارآئی برنامه های وب ، ضرورتی به فعال كردن آن وجود ندارد .
(بخش سوم)
اين بخش به بررسی نحوه نگهداری member variable و اشياء سفارشی در view state خواهيم پرداخت .
اجازه دهيد قبل از تشريح موارد فوق ، در ابتدا اشاره ای به انواع متعيرها داشته باشيم .
انواع متغيرها
پس از ايجاد ساختار اوليه يك كلاس ، می بايست عناصر داده پايه را به آن اضافه نمود .
در كد زير ، سه member variable تعريف شده است كه اطلاعاتی را در ارتباط با product ( شامل نام ، قيمت و URL آن كه به يك فايل image اشاره می نمايد ) در خود نگهداری می نمايند .
Public Class Product
Private name As String
Private price As Decimal
Private imageUrl As String
End Class
يك متغير محلی صرفا" تا زمانی وجود خواهد داشت كه فعاليت متد جاری ادامه داشته باشد . به عبارت ديگر ، يك member variable ( و يا فيلد ) به عنوان بخشی از كلاس تعريف می شود و برای تمامی متدهای موجود در كلاس قابل دسترس و استفاده است . اين نوع متغيرها ، پس از ايجاد شی ، ايجاد خواهند شد و تا زمانی كه شی مورد نظر به فعاليت خود ادامه می دهد ، آنها نيز فعال و به حيات خود ادامه خواهند داد .
زمانی كه يك member variable تعريف می گردد ، می بايست با صراحت قابليت دستيابی به آن مشخص گردد . قابليت دستيابی پذيری مشخص می نمايد كه چه بخش هائی از كد قادر به خواندن و تغيير اين متغير می باشند . مثلا" اگر شی A شامل يك متغير خصوصی (Private ) باشد ، شی B قادر به خواندن و تغيير آن نخواهد بود و صرفا" شی A قادر به انجام اين كار خواهد بود . به عبارت ديگر ، اگر شی A دارای يك متغير عمومی ( public ) باشد ، هر شی ديگر موجود در برنامه اين آزادی عمل را خواهد داشت كه اقدام به خواندن و تغيير اطلاعات ذخيره شده در آن نمايد . متغيرهای محلی از هيچگونه كليد واژه قابليت دستيابی پذيری حمايت نمی نمايند چراكه اين نوع متغيرها هرگز نمی توانند برای ساير كد های موجود در خارج از متد جاری در دسترس باشند .
در يك برنامه ساده ASP. NET ، اكثر member variables خصوصی خواهند بود چراكه اكثر كد نوشته شده توسط پياده كنندگان در يك كلاس صفحه وب قرار می گيرد .
به موازات تلاش جهت ايجاد عناصر نرم افزاری با قابليت استفاده مجدد ، اهميت قابليت دستيابی پذيری افزايش خواهد يافت .
جدول 1 انواع سطوح دستيابی را نشان می دهد .
كليد واژه
قابليت دستيابی پذيری
Public
امكان دستيابی توسط هر كلاس
Private
صرفا" امكان دستيابی به آن توسط متدهای درون كلاس جاری وجود دارد .
Friend
امكان دستيابی به آن توسط متدهای موجود در هر كلاس موجود در اسمبلی جاری ( فايل كد ترجمه شده ) وجود دارد.
Protected
امكان دستيابی به آن توسط متدهای موجود در كلاس جاری و يا هر كلاسی كه از اين كلاس مشتق شده باشد ، وجود دارد
جدول 1 انواع سطوح دستيابی
توجه داشته باشيد كه كليد واژه " قابليت دستيابی پذيری " ، صرفا" در ارتباط با member variable بكار گرفته نمی شود و از آن در ارتباط با متدها ، خصلت ها و رويدادها نيز استفاده می گردد.
ذخيره Member variables در view state
هر گونه اطلاعاتی كه در يك member variable صفحه ASP. NET ذخيره می گردد ، بطور اتوماتيك و پس از اتمام پردازش و ارسال صفحه برای سرويس گيرنده از بين می رود ( به عنوان نمونه متغير counter در مثال ارائه شده در بخش اول ) .
برای حل مشكلاتی اين چنين می توان تمامی member variables را در زمان بروز رويداد Page.PreRender در view state ذخيره و زمانی كه رويداد Page.Load ايجاد می گردد آنها را بازيابی كرد . رويداد Load هر مرتبه كه صفحه ايجاد می شود ، محقق می گردد . در زمان postback ، در ابتدا رويداد Load محقق شده و در ادامه ساير رويدادهای مرتبط با كنترل ها ايجاد خواهند شد .
مثال
در كد زير از روش فوق در ارتباط با متغيری با نام Contents ، استفاده شده است . در اين صفحه يك text box به همراه دو button ارائه شده است . كاربر در خصوص ذخيره و يا بازيابی داده از view state تصميم می گيرد . روتين های مربوط به رويداد كليك هر يك از دكمه های موجود بر روی فرم ، مسئوليت ذخيره و بازيابی داده در متغير Contents را برعهده دارند . در روتين های فوق ضرورتی به ذخيره و بازيابی مقدار متغير contents در view state وجود ندارد چراكه اين مسئوليت به روتين های رويدادهای Load و PreRender واگذار شده است تا در زمان آغاز و اتمام پردازش صفحه ، وظايف اشاره شده ( ذخيره و بازيابی ) را انجام دهند .
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa" %>
<script runat="server">
Private Contents As String
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If Me.IsPostBack Then
Contents = CType(ViewState("Text"), String)
End If
End Sub
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
ViewState("Text") = Contents
End Sub
Protected Sub cmdSave_Click(ByVal sender As Object, ByVal e As EventArgs)Handles cmdSave.Click
Contents = txtValue.Text
txtValue.Text = ""
End Sub
Protected Sub cmdLoad_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdLoad.Click
txtValue.Text = Contents
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>ذخيره member variable</title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtValue" runat="server" Height="153px" TextMode="MultiLine" Width="341px"
Font-Names="Tahoma" >نحوه ذخيره و بازيابی متغيرهای member در view state</asp:TextBox><br />
<br />
<asp:Button ID="cmdSave" runat="server" Text="ذخيره " Font-Names="Tahoma" />
<asp:Button ID="cmdLoad" runat="server" Text="بازيابی" Font-Names="Tahoma" />
</div>
</form>
</body>
</html>
شكل 1 ، خروجی برنامه فوف را نشان می دهد .
در زمان استفاده از روش فوق می بايست از ذخيره اطلاعات غيرضروری در view state اجتناب گردد ، چراكه در چنين مواردی ، حجم خروجی صفحه نهائی افزايش می يابد . اين موضوع می تواند كاهش سرعت انتقال صفحه را به دنبال داشته باشد .
در صورت ضرورت استفاده از رويكرد فوق برای ذخيره member variable در view state ، می بايست از آن در موارد كاملا" خاص استفاده شود .
ذخيره اشياء سفارشی
پياده كنندگان می توانند اشياء خود را در view state ذخيره نمايند . برای ذخيره يك آيتم در view state ، می بايست امكان تبديل آن به مجموعه ای از بايت ها وجود داشته باشد تا بتوان آن را در يك فيلد ورودی مخفی ذخيره كرد . به فرآيند فوق serialization می گويند . در صورتی كه اشياء تعريف شده توسط پياده كنندگان قابليت تبديل به مجموعه ای از بايت ها را نداشته باشند ( به صورت پيش فرض اين قابليت وجود ندارد ) ، در زمان استقرار آنها در view state يك پيام خطاء مواجه خواهيم شد.
برای ايجاد قابليت serializable در اشياء تعريف شده ، می بايست خصلت [Serializable] را به تعريف كلاس اضافه كرد .
كد زير نحوه انجام اين كار را نشان می دهد .
<Serializable()> _
Public Class Customer
Public FirstName As String
Public LastName As String
Public Sub New(ByVal firstName As String, ByVal lastName As String)
Me.FirstName = firstName
Me.LastName = lastName
End Sub
End Class
با توجه به اين كه در كلاس Customer خصلت serializable تعريف شده است ، امكان ذخيره آن در view state وجود خواهد داشت .
Dim customer1 As New Customer("MyFirstName","MyLastName")
ViewState("CurrentCustomer") = customer1
در زمان استفاده از اشياء سفارشی ، پس از بازيابی داده از view state می بايست آنها را تبديل ( cast ) كرد.
Dim customer1 As Customer
customer1 = CType(ViewState("CurrentCustomer"), Customer)
آيا تمامی اشياء دات نت را می توان در view state ذخيره كرد ؟
پس از بررسی مستندات يك كلاس و با مشاهده خصلت [Serializable] ، امكان ذخيره شی مورد نظر در view state وجود خواهد داشت . در صورتی كه شی مورد نظر فاقد خصلت فوق باشد ، امكان ذخيره آن در view state وجود نخواهد داشت . در چنين مواردی می توان از روش های ديگر state management كه در بخش های بعدی به آنها اشاره خواهيم كرد، استفاده كرد.
يكی از مهمترين محدوديت های view state ، شعاع استفاده از اطلاعات ذخيره شده در آن توسط ساير صفحات وب است . اطلاعات ذخيره شده در view state صرفا" توسط صفحه ای كه آنها را ايجاد كرده است قابل استفاده خواهند بود و ساير صفحات قادر به استفاده از اطلاعات نخواهند بود . به عنوان مثال ، در صورتی كه كاربر به صفحه ای ديگر حركت و يا هدايت شود ، اطلاعات ذخيره شده در view state قابل دستيابی نبوده و عملا" از بين خواهند رفت . برای غلبه بر محدوديت فوق ، از روش های متعدد ديگری می توان استفاده كرد .
(بخش چهارم)
يكی از مهمترين محدوديت های view state ، شعاع استفاده از اطلاعات ذخيره شده در آن توسط ساير صفحات وب است . اطلاعات ذخيره شده در view state صرفا" توسط صفحه ای كه آنها را ايجاد كرده است قابل استفاده می باشند و ساير صفحات نمی توانند از اطلاعات فوق استفاده نمايند . به عنوان مثال ، در صورتی كه كاربر به صفحه ای ديگر حركت و يا هدايت شود ، اطلاعات ذخيره شده در view state قابل دستيابی نبوده و عملا" از بين خواهند رفت . برای غلبه بر محدوديت فوق ( انتقال اطلاعات از يك صفحه به صفحه ديگر )، از روش های متعدد ديگری می توان استفاده كرد .
در اين بخش ، با روش انتقال اطلاعات از يك صفحه به صفحه ای ديگر با استفاده از cross-page posting آشنا خواهيم شد .
cross-page posting
يكی از امكانات جديد ارائه شده در ASP. NET 2.0 ، امكان postback يك صفحه به صفحه ای ديگر است ( برخلاف گزينه پيش فرض كه هر صفحه به خود postback می گردد ) .
بدين منظور خصلت جديد PostBackUrl در كنترل هائی نظير ImageButton ، LinkButton و Button پيش بينی شده است . برای استفاده از ويژگی فوق كافی است مقدار PostBackUrl برابر با آدرس صفحه مقصد در نظر گرفته شود . بدين ترتيب ، پس از كليك بر روی دكمه موجود بر روی فرم ، صفحه به همراه تمامی مقادير كنترل های ورودی موجود بر روی آن برای آدرس مشخص شده ارسال می گردد .
مثال
در اين مثال بر روی فرم وب از دو كنترل text box و يك كنترل button استفاده شده است ( صفحه Crosspage1.aspx ) . پس از كليك بر روی دكمه موجود بر روی فرم ، اطلاعات موجود بر روی فرم برای صفحه ای با نام Crosspage2.aspx ارسال می گردد .
صفحه Crosspage1.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>صفحه اول </title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server" >
<div>
نام <asp:TextBox ID="txtFirstName" runat="server"></asp:TextBox><br /><br />
نام خانوادگی <asp:TextBox ID="txtLastName" runat="server"></asp:TextBox> <br />
<br />
<asp:Button runat="server" ID="cmdPost"
PostBackUrl="CrossPage2.aspx"
Text="ارسال به صفحه ديگر" Font-Names="Tahoma" /><br />
</div>
</form>
</body>
</html>
صفحه Crosspage1.aspx شامل هيچگونه كدی نمی باشد .
پس از اجرای كد فوق و كليك بر روی دكمه "ارسال به صفحه ديگر" ، صفحه برای CrossPage2.aspx ارسال می گردد . صفحه Crosspage2.aspx با استفاده از خصلت Page.PreviousPage قادر به برقراری ارتباط با صفحه Crosspage1.aspx خواهد بود .
كد زير نحوه دريافت و نمايش عنوان صفحه قبلی را در صفحه Crosspage2.aspx نشان می دهد .
صفحه Crosspage2.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If PreviousPage IsNot Nothing Then
lblInfo.Text = "شما از صفحه ای با عنوان " & _
"<b>" & PreviousPage.Header.Title & "</b>" & _
" به اين صفحه آمده ايد" & "<br />"
End If
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>صفحه دوم</title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server" >
<div>
<asp:Label ID="lblInfo" runat="server" Font-Size="Small"></asp:Label>
</div>
</form>
</body>
</html>
شكل 1 ، نحوه انتقال اطلاعات از يك صفحه به صفحه ديگر را نشان می دهد يكی از مهمترين محدوديت های view state ، شعاع استفاده از اطلاعات ذخيره شده در آن توسط ساير صفحات وب است . اطلاعات ذخيره شده در view state صرفا" توسط صفحه ای كه آنها را ايجاد كرده است قابل استفاده می باشند و ساير صفحات نمی توانند از اطلاعات فوق استفاده نمايند . به عنوان مثال ، در صورتی كه كاربر به صفحه ای ديگر حركت و يا هدايت شود ، اطلاعات ذخيره شده در view state قابل دستيابی نبوده و عملا" از بين خواهند رفت . برای غلبه بر محدوديت فوق ( انتقال اطلاعات از يك صفحه به صفحه ديگر )، از روش های متعدد ديگری می توان استفاده كرد .
در اين بخش ، با روش انتقال اطلاعات از يك صفحه به صفحه ای ديگر با استفاده از cross-page posting آشنا خواهيم شد .
cross-page posting
يكی از امكانات جديد ارائه شده در ASP. NET 2.0 ، امكان postback يك صفحه به صفحه ای ديگر است ( برخلاف گزينه پيش فرض كه هر صفحه به خود postback می گردد ) .
بدين منظور خصلت جديد PostBackUrl در كنترل هائی نظير ImageButton ، LinkButton و Button پيش بينی شده است . برای استفاده از ويژگی فوق كافی است مقدار PostBackUrl برابر با آدرس صفحه مقصد در نظر گرفته شود . بدين ترتيب ، پس از كليك بر روی دكمه موجود بر روی فرم ، صفحه به همراه تمامی مقادير كنترل های ورودی موجود بر روی آن برای آدرس مشخص شده ارسال می گردد .
مثال
در اين مثال بر روی فرم وب از دو كنترل text box و يك كنترل button استفاده شده است ( صفحه Crosspage1.aspx ) . پس از كليك بر روی دكمه موجود بر روی فرم ، اطلاعات موجود بر روی فرم برای صفحه ای با نام Crosspage2.aspx ارسال می گردد .
صفحه Crosspage1.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>صفحه اول </title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server" >
<div>
نام <asp:TextBox ID="txtFirstName" runat="server"></asp:TextBox><br /><br />
نام خانوادگی <asp:TextBox ID="txtLastName" runat="server"></asp:TextBox> <br />
<br />
<asp:Button runat="server" ID="cmdPost"
PostBackUrl="CrossPage2.aspx"
Text="ارسال به صفحه ديگر" Font-Names="Tahoma" /><br />
</div>
</form>
</body>
</html>
صفحه Crosspage1.aspx شامل هيچگونه كدی نمی باشد .
پس از اجرای كد فوق و كليك بر روی دكمه "ارسال به صفحه ديگر" ، صفحه برای CrossPage2.aspx ارسال می گردد . صفحه Crosspage2.aspx با استفاده از خصلت Page.PreviousPage قادر به برقراری ارتباط با صفحه Crosspage1.aspx خواهد بود .
كد زير نحوه دريافت و نمايش عنوان صفحه قبلی را در صفحه Crosspage2.aspx نشان می دهد .
صفحه Crosspage2.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If PreviousPage IsNot Nothing Then
lblInfo.Text = "شما از صفحه ای با عنوان " & _
"<b>" & PreviousPage.Header.Title & "</b>" & _
" به اين صفحه آمده ايد" & "<br />"
End If
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>صفحه دوم</title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server" >
<div>
<asp:Label ID="lblInfo" runat="server" Font-Size="Small"></asp:Label>
</div>
</form>
</body>
</html>
شكل 1 ، نحوه انتقال اطلاعات از يك صفحه به صفحه ديگر را نشان می دهد
در صفحه Crosspage2.aspx قبل از تلاش برای دستيابی به شی PreviousPage ، مقدار آن با null بررسی می گردد . در صورتی كه مقدار آن معادل null باشد ، cross-page postback محقق نشده است . اين بدان معنی است كه صفحه Crosspage2.aspx مستقيما" درخواست شده است و يا به خود postback شده است . در چنين مواردی شی PreviousPage قابل دسترس نخواهد بود .
(بخش پنجم)
حوه دريافت اطلاعات از صفحه مبداء
در مثال ارائه شده در بخش چهارم با نحوه عملكرد cross-page postback آشنا شديم و با بررسی يك مثال كاربردی ، عنوان صفحه مبداء در صفحه مقصد استخراج و در خروجی نمايش داده شد. شايد برای بسياری از خوانندگان اين سوال مطرح شده باشد كه چگونه می توان اطلاعاتی ديگر نظير مقادير درج شده در text boxes موجود بر روی صفحه مبداء ( CrossPage1.aspx ) را در صفحه مقصد ( CrossPage2.aspx ) بازيابی و از آنها استفاده كرد؟
در زمان پيكربندی يك صفحه برای عمليات cross-page posting ، اغلب پياده كنندگان تمايل به دريافت اطلاعات از صفحه مبداء را دارند . اين اطلاعات می تواند شامل كنترل های موجود بر روی صفحه مبداء و يا خصلت های عمومی صفحه مبداء باشد .
دريافت مقادير كنترل ها
كلاس Page دارای يك خصلت با نام PreviousPage است . در صورتی كه صفحه مبداء و مقصد در يك برنامه مشابه ASP.NET باشند ، خصلت PreviousPage در صفحه مقصد شامل يك مرجع به صفحه مبداء خواهد بود .
در صورتی كه صفحات مبداء و مقصد در برنامه های متفاوتی باشند ، خصلت PreviousPage مقداردهی اوليه نخواهد شد و نمی توان از طريق صفحه مقصد مستقيما" به مقادير كنترل های موجود بر روی صفحه مبداء دستيابی داشت . در چنين مواردی می توان با استفاده از ديكشنری Form اقدام به خواندن داده ارسال شده نمود . همچنين ، در چنين مواردی امكان خواندن مقادير ذخيره شده در view state صفحه مبداء وجود نخواهد داشت . در صورتی كه بخواهيم مقاديری را در صفحه مبداء ذخيره نمائيم و آنها را برای يك صفحه مقصد و در يك برنامه ديگر قابل دسترس نمائيم ، می توان مقادير مورد نظر را به عنوان رشته درون فيلدهای مخفی بر روی صفحه مبداء ذخيره و در صفحه مقصد از طريق Request.Form به آنها دستيابی داشت .
با استفاده از مرجع در خصلت PreviousPage می توان عمليات جستجو بر روی صفحه مبداء را به منظور بازيابی و استخراج مقادير مورد نظر توسط متد FindControl انجام داد .
كد زير نحوه بازيابی مقادير درج شده در كنترل های Textbox موجود بر روی صفحه مبداء (CrossPage1.aspx) را نشان می دهد .
بازيابی مقادير كنترل های موجود در صفحه مبداء
(Script section صفحه مقصد )
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If PreviousPage IsNot Nothing Then
Dim txtSourceFirstName As TextBox
Dim txtSourceLastName As TextBox
txtSourceFirstName = CType(PreviousPage.FindControl("txtFirstName"), TextBox)
txtSourceLastName = CType(PreviousPage.FindControl("txtLastName"), TextBox)
lblInfo.Text = " نام و نام خانوادگی دريافت شده از صفحه مبداء " & _
txtSourceFirstName.Text & " " & txtSourceLastName.Text
End If
End Sub
</script>
متد FindControl ، كنترل های مورد نظر را در فهرست جاری نام جستجو می نمايد . در صورتی كه قصد جستجوی كنترل موجود در كنترل ديگر را داشته باشيم ( نظير يك تمپليت ) ، می بايست در ابتدا يك مرجع به آْن را ايجاد و عمليات جستجو را در آن فهرست انجام داد .
دريافت مقادير خصلت های عمومی
در صفحه مقصد يك cross-page posting ، می توان مقادير فيلدهای عمومی موجود در صفحه مبداء را دريافت كرد . بدين منظور صفحه مبداء خصلت هائی عمومی را با توجه به اهداف عملياتی برنامه تعريف می نمايد تا در ادامه صفحه مقصد بتواند به آنها دستيابی داشته باشد ( به عنوان يك توصيه امنيتی پيشنهاد شده است كه حجم اين گونه اطلاعات عمومی كمتر در نظر گرفته شود تا آسيب پذيری كد در مقابل حملات كاهش يابد ) .
برای دريافت فيلدهای عمومی صفحه مبداء ، در ابتدا می بايست يك مرجع به صفحه مبداء را تعريف كرد . بدين منظور می توان از روش های مختلفی استفاده كرد . استفاده از دايركتيو PreviousPageType @ در صفحه مقصد كه به كمك آن صفحه مبداء مشخص می گردد ، يكی از روش های موجود در اين زمينه است .
كد زير نحوه انجام اين كار را مشخص می نمايد .
<%@ PreviousPageType VirtualPath="~/CrossPage1.aspx" %>
پس از استقرار دايركتيو فوق در صفحه مقصد ، خصلت PreviousPage به كلاس صفحه مبداء اشاره خواهد داشت ( به عنوان يك مرجع ) . در ادامه می توان مستقيما" به اعضاء عمومی موجود در صفحه مبداء دستيابی داشت .
به عنوان مثال، در صورتی كه قصد داشته باشيم از مقادير موجود در دو text box صفحه مبداء استفاده نمائيم (صفحه CrossPage1.aspx)، می توان خصلت هائی را به منظور استفاده از متغيرهای كنترل به آن اضافه كرد .
در نمونه كد زير دو خصلت به كلاس CrossPage1 اضافه شده است تا بتوان از مقادير كنترل های Textbox در صفحه مقصد استفاده كرد .
Public ReadOnly Property FirstNameTextBox() As TextBox
Get
Return txtFirstName
End Get
End Property
Public ReadOnly Property LastNameTextBox() As TextBox
Get
Return txtLastName
End Get
End Property
در صورتی كه صفحه مقصد شامل دايركتيو PreviousPageType باشد كه توسط آن به صفحه مبداء اشاره می گردد ، می توان به خصلت های عمومی صفحه مبداء دستيابی داشت .
مثال
برای بازنويسی مثال ارائه شده در بخش چهارم مراحل زير را انجام می دهيم .
مرحله اول: بازنويسی صفحه CrossPage1.aspx . كد فوق مشابه كد نوشته شده در بخش چهارم است با اين تفاوت كه برای دستيابی به نام و نام حانوادگی درج شده در هر Textbox يك خصلت عمومی با نام FullName تعريف شده است تا نام و نام خانوادگی ورودی را در خود نگهداری نمايد .
صفحه Crosspage1.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Public ReadOnly Property FullName() As String
Get
Return txtFirstName.Text & " " & txtLastName.Text
End Get
End Property
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>صفحه اول </title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server" >
<div>
نام <asp:TextBox ID="txtFirstName" runat="server"></asp:TextBox><br /><br />
نام خانوادگی <asp:TextBox ID="txtLastName" runat="server"></asp:TextBox> <br />
<br />
<asp:Button runat="server" ID="cmdPost"
PostBackUrl="CrossPage2.aspx"
Text="ارسال به صفحه ديگر" Font-Names="Tahoma" /><br />
</div>
</form>
</body>
</html>
بدين ترتيب ، ارتباط بين دو صفحه واضح و ساده بوده و نگهداری آنها نيز آسان خواهد شد . همچنين ، می توان كنترل ها را در صفحه مبداء ( CrossPage1 ) بدون نياز به تغيير ساير بخش های برنامه تغيير داد . مثلا" ، در صورتی كه تصميم داشته باشيم از كنترل های مختلفی برای درج نام در صفحه CrossPage1.aspx استفاده نمائيم ، می بايست بر روی كد مربوط به خصلت FullName متمركز گرديد . تغيير فوق صرفا" در صفحه CrossPage1.aspx اعمال می گردد و ضرورتی به تغيير صفحه CrossPage2.aspx وجود نخواهد داشت .
مرحله دوم : بازنويسی صفحه CrossPage2.aspx . در بازنويسی صفحه فوق در اولين اقدام از دايركتيو PreviousPageType @ برای معرفی كلاس صفحه مبداء استفاده شده است تا به كمك آن بتوان به اعضاء عمومی تعريف شده در صفحه مبداء دستيابی داشت . برای دستيابی به خصلت عمومی FullName ، از PreviousPage.FullName استفاده شده است .
صفحه Crosspage2.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<%@ PreviousPageType VirtualPath="~/crossPage1.aspx" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If PreviousPage IsNot Nothing Then
lblInfo.Text = "شما از صفحه ای با عنوان " & _
"<b>" & PreviousPage.Header.Title & "</b>" & _
" به اين صفحه آمده ايد" & "<br />"
lblInfo.Text &= "نام و نام خانوادگی دريافت شده از صفحه مبداء " & _
& "<b>" & PreviousPage.FullName & "<br />"
End If
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>صفحه دوم</title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server" >
<div>
<asp:Label ID="lblInfo" runat="server" Font-Size="Small"></asp:Label>
</div>
</form>
</body>
</html>
شكل 1 ، خروجی برنامه فوق را نشان می دهد .
ASP. NET از ويژگی جالبی برای كاركرد بهتر فرآيند cross-page postbacks استفاده می نمايد . اولين مرتبه ای كه صفحه مقصد به Page.PreviousPage دستيابی پيدا می نمايد ، ASP. NET شی صفحه قبلی ( مبداء) را ايجاد نمايد . برای انجام اين كار ، ASP. NET پردازش صفحه را آغاز ولی آن را قبل از رسيدن به مرحله PreRender با وقفه متوقف نموده و اجازه نمی دهد كه صفحه خروجی HTML را توليد نمايد .
روش فوق دارای اثرات جانبی جالبی است ، مثلا" تمامی رويدادهای صفحه مربوط به صفحه قبل ( نظير Page.Load و Page.Init ) به همراه رويداد Button.Click كه باعث cross-page postback شده است ، فعال می گردند . علت فعال شدن رويدادهای فوق ، مقدار دهی صفحه مبداء توسط ASP.NET است .
بررسی PostBack در صفحه مقصد
در حين فرآيند cross-page postback ، محتويات كنترل های موجود بر روی صفحه مبداء برای صفحه مقصد ارسال و مرورگر يك عمليات HTTP POST را انجام می دهد ( نه عمليات GET ) . در صفحه مقصد و بلافاصله پس از عمليات postback ، مقدار خصلت IsPostBack برابر با false خواهد شد . با اين كه ماهيت عمليات در واقع يك POST را نشان می دهد ، ولی cross-posting بيانگر يك Postback به صفحه مقصد نمی باشد .
در برخی موارد لازم است كه در يك برنامه تشخيص دهيم كه آيا يك cross-page post واقع شده است. در چنين مواردی می توان مقدار خصلت IsCrossPagePostBack را بر روی صفحه مقصد كه توسط خصلت PreviousPage صفحه مقصد برگردانده شده است ، بررسی كرد .
كد زير نحوه انجام اين كار را نشان می دهد .
If PreviousPage IsNot Nothing Then
If PreviousPage.IsCrossPagePostBack = True Then
Label1.Text = "Cross-page post."
End If
Else
Label1.Text = "Not a cross-page post."
End If
(بخش ششم)
Query String
يكی ديگر از روش های ارسال اطلاعات بين صفحات ، استفاده از يك query string در URL است . از رويكرد فوق در موتورهای جستجو استفاده می گردد . مثلا" در صورت استفاده از موتور جستجوی گوگل ، پس از درج كليد واژه مورد نظر به يك URL جديد كه با پارامترهای جستجو تركيب می گردد ، هدايت می شويم.
کد:
web - Google Search (http://forum.patoghu.com/redirector.php?url=http%3A%2F%2Fwww.google.com%2Fs earch%3Fq%3Dweb)
application+ASP.NET
query string ، به عنوان بخشی از URL محسوب می گردد و پس از علامت سوال آورده می شود . در مثال فوق ، يك متغير با نام q و مقدار web application +ASP. NET تعريف می گردد .
يكی از مهمترين مزايای query string ، عدم تحميل عمليات اضافه به سرويس دهنده است . علی رغم مزيت فوق ، رويكرد فوق دارای محدوديت های متعددی است كه به برخی از آنها اشاره می گردد :
*
اطلاعات محدود به رشته های ساده مشتمل بر كاراكترهای مجاز URL است .
*
اطلاعات توسط كاربران قابل مشاهده بوده و هر شخص علاقه مند می تواند آنها را استراق سمع نمايد .
*
كاربران ماهر می توانند محتويات query string را تغيير داده و وضعيتی را ايجاد نمايند كه برنامه قادر به واكنش مناسب با آن نباشد ( ايجاد شرايط غيرقابل پيش بينی )
*
تعداد زيادی از مرورگرها دارای محدوديت طول يك URL می باشند ( معمولا" بين يك تا دو كيلو بايت ) . بنابراين ، نمی توان حجم بالائی از اطلاعات را در query string ذخيره كرد و اين اطمينان را داشت كه اكثر مرورگرها بتوانند از آن استفاده نمايند ( به دليل عدم سازگاری ) .
علی رغم محدوديت های اشاره شده ، query string همچنان به عنوان مكانيزمی جهت ارسال داده از يك صفحه به صفحه ديگر استفاده می گردد . روش فوق در برنامه هائی با محوريت بانك های اطلاعاتی بيشتر مورد توجه است . در چنين مواردی در آغاز ليستی از آيتم هائی نمايش داده می شود كه متاثر از داده موجود در بانك اطلاعاتی است ( نظير ليست محصولات ، ليست مقالات ، ليست خبرها و ... ) . پس از انتخاب يكی از آيتم ها ، كاربر به صفحه ای ديگر هدايت می گردد تا اطلاعات بيشتری در ارتباط با آيتم انتخابی در اختيار وی گذاشته شود .
برای ذخيره اطلاعات در query string ، می بايست پياده كنندگان خود اطلاعات را در مكان مورد نظر قرار دهند . متاسفانه ، روشی مبتنی بر collection برای انجام اين كار وجود ندارد . اين بدان معنی است كه در چنين مواردی می بايست عموما" از يك كنترل Hyperlink خاص و يا عبارت Response.Redirect استفاده كرد .
كد زير كاربر را به صفحه newspage.aspx هدايت می نمايد . همراه با كاربر متغيری با نام recordID و مقدار 10 نيز برای صفحه فوق ارسال می گردد .
Response.Redirect("newpage.aspx?recordID=10")
در صورت نياز می توان چندين پارامتر را كه توسط علامت & ( ampersand ) از يكديگر جدا می شوند ، به صفحه مقصد ارسال كرد .
كد زير كاربر را به صفحه newspage.aspx هدايت می نمايد . همراه با كاربر متغيرهائی با نام recordID و مقدار 10 و mode با مقدار full نيز برای صفحه فوق ارسال می گردد .
Response.Redirect("newpage.aspx?recordID=10&mode=f ull")
صفحه دريافت كننده به سادگی می تواند اطلاعات ارسالی را دريافت نمايد . بدين منظور از مجموعه ديكشنری QueryString كه توسط شی Request ارائه شده است ، استفاده می گردد .
Dim ID As String = Request.QueryString("recordID")
توجه داشته باشيد كه اطلاعات همواره به عنوان يك رشته بازيابی می گردند و در صورت نياز می بايست آنها را به نوع داده مورد نظر تبديل كرد . مقادير ذخيره شده در مجموعه QueryString ، با استفاده از اسامی متغيرها ، ايندكس می گردند .
query string بر خلاف view state ، اطلاعات را كاملا" شفاف ، آشكارا و غير رمز شده ارسال می نمايد . بنابراين در مواردی كه لازم است اطلاعاتی بطور مخفی از يك صفحه به صفحه ديگر ارسال و يا بر روی آن حساسيت خاصی از نظر امنيتی وجود دارد ، استفاده از روش query string توصيه نمی گردد .
مثال
در اين مثال هدف بررسی نحوه عملكرد و يا رفتار query string با استفاده از دو صفحه است . در صفحه مبداء ليستی از آيتم ها در اختيار كاربر گذاشته می شود . پس از انتخاب يكی از آيتم ها ، كاربر به يك صفحه جديد ( مقصد ) هدايت می گردد . در صفحه مقصد آيتم انتخاب شده به همراه مقدار متغير mode در خروجی نمايش داده می شود .
صفحه مبداء شامل ليستی از آيتم ها ، يك check box ، يك كنترل label و دكمه "مشاهده اطلاعات " است .
كد زير محتويات صفحه مبداء ( QueryStringSender.aspx ) را نشان می دهد .
صفحه QueryStringSender.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If Not Me.IsPostBack Then
lstItems.Items.Add("مقالات ")
lstItems.Items.Add("نكته ها")
lstItems.Items.Add("خودآزمون ها")
lstItems.Items.Add("خود آموزها")
lstItems.Items.Add("هيچكدام")
End If
End Sub
Protected Sub cmdGo_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdGo.Click
If lstItems.SelectedIndex = -1 Then
lblError.Text = " يكی از آيتم های موجود در ليست را انتخاب نمائيد"
Else
Dim Url As String = "QueryStringRecipient.aspx?"
Url &= "Item=" & lstItems.SelectedItem.Text & "&"
Url &= "Mode=" & chkDetails.Checked.ToString()
Response.Redirect(Url)
End If
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl">
<head runat="server">
<title>تست query string | صفحه ارسال كننده</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<span style="font-size: 9pt; font-family: Tahoma">
<strong>
<span style="color: #006699">
علاقه مند به مشاهده كدام بخش از سايت سخا روش می باشيد ؟
</span><br />
</strong>
</span><br />
<asp:ListBox ID="lstItems" runat="server" Height="90px" Width="165px"
Font-Names="Tahoma"></asp:ListBox><br />
<br />
<asp:CheckBox ID="chkDetails" runat="server" Text="جزئيات نمايش داده شود ؟"
TextAlign="Right" Font-Names="Tahoma" /><br /> <br />
<asp:Button ID="cmdGo" runat="server" Text="مشاهده اطلاعات"
Width="122px" Font-Names="Tahoma" /><br /><br />
<asp:Label ID="lblError" runat="server" EnableViewState="False"
Font-Names="Tahoma" ForeColor="Red"></asp:Label>
</div>
</form>
</body>
</html>
كد زير محتويات صفحه مقصد ( QueryStringRecipient.aspx ) را نشان می دهد .
صفحه QueryStringRecipient.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
lblInfo.Text = "آيتم انتخاب شده توسط كاربر : " & "<b>" & Request.QueryString("Item") & "</b>"
lblInfo.Text &= "<br>نمايش تمامی اطلاعات : " & "<b>"
lblInfo.Text &= Request.QueryString("Mode") & "</b>"
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl">
<head runat="server">
<title>تست query string | صفحه دريافت كننده</title>
</head>
<body style="font-family: Tahoma;">
<form id="form1" runat="server">
<div >
<asp:Label ID="lblInfo" runat="server" EnableViewState="False" Font-Names="Tahoma" >
</asp:Label>
</div>
</form>
</body>
</html>
شكل 1 ، نحوه عملكرد Query string را در ارتباط با صفحه مبداء (QueryStringSender.aspx) و مقصد (QueryStringRecipient.aspx ) نشان می دهد .
URL Encoding
يكی از مسائل در ارتباط با روش query string ، استفاده از كاراكترهای غيرمجاز در يك URL است . ليست كاراكترهای مجاز در يك URL بمراتب كمتر از ليست كاراكترهای مجاز در يك سند HTML است . تمامی كاراكترها می بايست الفبا عددی و يا يكی از مجموعه كاراكترهای خاص نظير $-_.+!*'() باشد . علاوه بر كاراكترهای فوق ، برخی مرورگرها ممكن است دارای محدوديت های مختص به خود نيز باشند .
در صورتی كه لازم است مجاز بودن كاراكترهای ذخيره شده در query string بررسی گردد ، می توان از URL encoding استفاده كرد . با استفاده از ويژگی فوق ، كاراكترهای خاص با دنباله ای از escaped character جايگزين می گردند كه با علامت درصد (%) شروع و به دنبال آن يك عدد دو رقمی مبنای شانزده آورده می شود ( مثلا" space به 20 % تبديل می گردد ) .
از متدهای كلاس HttpServerUtility می توان برای encode اتوماتيك داده استفاده كرد .
كد زير نحوه encoding يك رشته حاوی داده جهت استفاده در query string را نشان می دهد . بدين ترتيب ، تمامی كاراكترهای غيرمجاز با دنباله ای از escaped character جايگزين می گردند .
Dim productName As String = "Test Product"
Response.Redirect("newpage.aspx?productName=" & Server.UrlEncode(productName))
از كد زير برای decoding در صفحه مقصد استفاده می گردد .
Dim ID As String = Server.UrlDecode(Request.QueryString("recordID"))
متاسفانه ، ASP.NET دارای مكانيزم خاصی جهت بررسی و رمزنگاری اتوماتيك query string نمی باشد . با استفاده از كلاس های متعدد رمزنگاری ارائه شده در دات نت ، می توان رشته های query string را رمز و يك سطح مطلوب امنيتی در ارتباط با آنها را ايجاد كرد ( اميدواريم در آينده و در مقالاتی جداگانه اين موضوع را بطور كامل تشريح نمائيم ) .
در بخش هفتم به بررسی كوكی های سفارشی جهت ذخيره و بازيابی اطلاعات خواهيم پرداخت .
(بخش هفتم)
مفاهيم و چالش ها
كوكی های سفارشی يكی ديگر از روش های موجود جهت ذخيره اطلاعات به منظور استفاده در ساير صفحات می باشند . كوكی ، فايل های كوچكی می باشند كه بر روی هارد ديسك سرويس گيرنده ( و يا حافظه مرورگر وب در صورتی كه قصد ذخيره موقت آنها وجود داشته باشد ) ايجاد می گردند .
يكی از مزايای كوكی ها عملكرد غيرمحسوس آنها و عدم آگاهی كاربر نسبت به ذخيره اطلاعات است . علاوه بر اين كه می توان از كوكی ها در هر يك از صفحات برنامه استفاده كرد ، امكان استفاده از اطلاعات ذخيره شده در آنها طی بازديدهای آتی كاربر نيز وجود دارد ( مكانی برای ذخيره اطلاعات با طول عمر بيشتر) .
كوكی ها دارای محدوديت ها و يا چالش های مختص به خود نيز می باشند :
*
ذخيره حجم اندكی از اطلاعات : صرفا" امكان ذخيره حجم اندكی از اطلاعات به صورت متن در آنها وجود دارد .
*
عدم ايمن بودن : در صورتی كه كاربر كوكی و فايل مربوط به آن را پيدا می كند ، می تواند به سادگی به آن دستيابی پيدا نمايد ( خواندن ، حذف ) .
*
امكان حذف آنها توسط كاربران : همواره اين احتمال وجود خواهد داشت كه كاربران اقدام به حذف كوكی های موجود بر روی كامپيوتر خود نمايند .
*
وجود محدوديت در برخی مرورگرها با توجه به نوع دستگاه سرويس گيرنده : كوكی ها می توانند در تعداد مخاطبان با توجه به نوع دستگاه آنها محدوديت ايجاد نمايند . به عنوان نمونه ، بكارگيری كوكی به همراه مرورگرهای از قبل تعبيه شده در دستگاه های موبايل مناسب نمی باشد .
*
وابسته به تنظيمات كاربر : برخی از كاربران امكان ايجاد كوكی را از طريق مرورگر خود غيرفعال می نمايند . اين كار می تواند مسائل متعددی را برای برنامه های وب به دنبال داشته باشد .
عوامل فوق باعث شده است كه كوكی به عنوان يك گزينه ضعيف برای ذخيره اطلاعات مورد توجه قرار گيرد . در مواردی كه اطلاعات پيچيده ، خصوصی و يا حجم آنها زياد باشد ، استفاده از كوكی بسيار محدود می گردد .
نحوه ذخيره و بازيابی اطلاعات
فرآيند ذخيره و بازيابی اطلاعات در يك كوكی به سادگی انجام می شود . اشياء Request و Response يك مجموعه Cookies را ارائه می نمايند . كوكی ها از طريق شی Request بازيابی و مقداردهی آنها از طريق شی Response انجام می شود . برای مقداردهی يك كوكی ، صرفا" می بايست يك شی جديد HttpCookie را ايجاد كرد . در ادامه می توان اطلاعات مورد نظر را در آن و از نوع رشته ذخيره و به پاسخ فعلی وب اضافه كرد .
' ايجاد شی كوكی
Dim cookie As New HttpCookie("Preferences")
' مقداردهی كوكی
cookie("LanguagePref") = "English"
' اضافه كردن كوكی به پاسخ جاری وب
Response.Cookies.Add(cookie)
كوكی فوق تا زمانی كه كاربر مرورگر را close نكند ، وجود خواهد داشت و با هر درخواست ارسال می گردد . برای ايجاد يك كوكی با طول عمر بيشتر ، می توان برای آن يك تاريخ سررسيد را تعريف كرد ( عمر مفيد ) .
' مدت زمان حيات كوكی يك سال تعيين شده است
Cookie.Expires = DateTime.Now.AddYears(1)
برای بازيابی كوكی ، می توان از طريق نام استفاده شده در مجموعه Request.Cookies اقدام نمود.
' بررسی وجود كوكی قبل از دستيابی به آن
Dim language As String
If Cookie IsNot Nothing Then
language = Cookie("LanguagePref")
End If
تنها روش موجود برای حذف يك كوكی ، جايگزين كردن آن با يك كوكی است كه تاريخ اعتبار آن به اتمام رسيده باشد . كد زير نحوه انجام اين كار را نشان می دهد .
Dim cookie As New HttpCookie("LanguagePref")
cookie.Expires = DateTime.Now.AddDays(-1)
Response.Cookies.Add(cookie)
مثال
در اين مثال هدف آشنائی با نحوه ايجاد ، مقداردهی و بازيابی يك كوكی است .
در اولين مرتبه ای كه صفحه درخواست می گردد به دليل عدم وجود كوكی مورد نظر ، پيام "كاربر ناشناس " نمايش داده شده و با ارائه يك text box اين امكان در اختيار كاربر گذاشته می شود تا نام مورد نظر خود را جهت ذخيره در كوكی درج نمايد . پس از اين كار و همزمان با درخواست صفحه و با توجه به وجود كوكی مورد نظر ، يك پيام خوش آمدگوئی نمايش داده می شود . كوكی ايجاد شده در اين مثال به مدت يك سال از تاريخ ايجاد ، اعتبار خواهد داشت .
عمليات تشخيص وجود كوكی و نمايش يك پيام خوش آمدگوئی از طريق روتين Page_Load انجام می شود . روتين cmdStoreCookiee_Click مسئوليت ايجاد كوكی و تعيين يك تاريخ اعتبار برای آن را برعهده دارد .
صفحه CookieExample.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) _
Handles MyBase.Load
Dim Cookie As HttpCookie = Request.Cookies("Preferences")
If Cookie Is Nothing Then
lblWelcome.Text = "<b>كاربر ناشناس</b>"
Else
lblWelcome.Text = "<b>كوكی موجود است </b><br><br>"
lblWelcome.Text &= "خوش آمديد, " & Cookie("Name")
End If
End Sub
Protected Sub cmdStoreCookiee_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles cmdStoreCookie.Click
Dim Cookie As HttpCookie = Request.Cookies("Preferences")
If Cookie Is Nothing Then
Cookie = New HttpCookie("Preferences")
End If
Cookie("Name") = txtName.Text
Cookie.Expires = DateTime.Now.AddYears(1)
Response.Cookies.Add(Cookie)
lblWelcome.Text = "<b>كوكی ايجاد گرديد</b><br><br>"
lblWelcome.Text &= "كاربر جديد : " & Cookie("Name")
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head runat="server">
<title>تست كوكی</title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server">
<asp:Label ID="lblWelcome" runat="server" EnableViewState="False" Font-Names="Tahoma" >
</asp:Label><br /> <br />
نام:<asp:TextBox ID="txtName" runat="server" Width="178px" Font-Names="Tahoma">
</asp:TextBox><br /><br />
<asp:Button ID="cmdStoreCookie" runat="server" Text="ايجاد كوكی" Font-Names="Tahoma" />
</form>
</body>
</html>
شكل 1 ، نحوه عملكرد برنامه فوق را در اولين مرتبه دستيابی ( و با فرض عدم وجود كوكی ) نشان می دهد .
و اما آخرين نكته اين كه ، برخی از ويژگی های ارائه شده در ASP. NET با استفاده از كوكی خدمات خود را ارائه می نمايند . session state كه به پياده كنندگان اجازه می دهد بطور موقت اطلاعات مرتبط با كاربر را در حافظه سرويس دهنده ذخيره نمايند و امنيت فرم ها كه اين امكان را در اختيار پياده كنندگان قرار می دهد تا بخش هائی از يك وب سايت را محدود به كاربران تائيد شده نمايند ، دو نمونه متداول در اين رابطه می باشند .
(بخش هشتم)
مفاهيم و چالش ها
در مدت زمان حيات يك برنامه به مواردی برخورد می كنيم كه لازم است جهت ذخيره سازی اطلاعات از امكانات پيشرفته تری استفاده گردد . به عنوان مثال ، يك برنامه ممكن است به ذخيره اطلاعات پيچيده ای نظير اشياء سفارشی داده و استفاده از آنها در ساير صفحات نياز داشته باشد . ارسال اينگونه اطلاعات از طريق كوكی و يا يك query string مشكل و يا غيرممكن است . علاوه بر اين ، در برخی موارد ملاحظات امنيتی در رابطه با داده وجود دارد و نمی توان اطلاعات مربوط به يك سرويس گيرنده را در view state و يا كوكی ذخيره كرد .
در چنين مواردی می توان از امكانات از قبل تعبيه شده session state در ASP.NET استفاده كرد .
مديريت session state يكی از ويژگی های برجسته ASP.NET است كه به كمك آن می توان هر نوع داده ئی را در حافظه سرويس دهنده ذخيره كرد . بدين ترتيب ، يك سطح حفاظتی مطلوب در خصوص داده ايجاد خواهد شد چراكه اطلاعات برای سرويس گيرنده ارسال نخواهند شد و برای هر جلسه كاری منحصربفرد می باشند .
هر سرويس گيرنده ای كه به برنامه دستيابی داشته باشد دارای يك session متفاوت و مجموعه ای از اطلاعات متمايز و مختص به خود است . session state برای ذخيره اطلاعاتی نظير آيتم های خريداری شده توسط كاربر از يك سايت و استقرار آنها در سبد خريد در زمان حركت از يك صفحه به صفحه ديگر بسيار مفيد و موثر واقع می شود .
با استفاده از session state می توان اطلاعات مورد نظر را از طريق يك صفحه ذخيره و در ساير صفحات از آنها استفاده كرد .
با اين كه session state بسياری از مشكلات در ارتباط با ساير روش های مديريت state را برطرف نموده است ولی خود نيز دارای چالش های مختص به خود است . به عنوان مثال ، با بكارگيری روش فوق در برنامه های وب ، سرويس دهنده وب ملزم به ذخيره اطلاعات بيشتری در حافظه سرويس دهنده خواهد شد. اين موضوع می تواند همزمان با افزايش كاربران يك برنامه بر روی كارآئی آن تاثير بگذارد . چراكه درصد استفاده از يك منبع محدود ( حافظه ) افزايش خواهد يافت . بنابراين ، لازم است استفاده از session state با دقت و بررسی تمامی جوانب كار صورت پذيريد .
معماری session
مديريت session به عنوان بخشی از استاندارد HTTP محسوب نمی گردد . بنابراين لازم است كه ASP.NET عمليات بيشتری را به منظور پيگيری اطلاعات session انجام دهد .
ASP.NET هر session را از طريق يك شناسه 120 بيتی منحصربفرد پيگيری و از يك الگوريتم اختصاصی برای توليد آن استفاده می نمايد . بنابراين حداقل اين تضمين از لحاظ تئوری ايجاد می گردد كه عدد توليد شده منحصر بفرد بوده و به اندازه كافی تصادفی است تا امكان و يا احتمال تشخيص و حدس آن توسط مهاجمان به حداقل مقدار خود برسد ( مهاجمان با بكارگيری روش هائی موسوم به مهندسی معكوس در تلاش جهت آگاهی از اين موضوع هستند كه يك سرويس گيرنده خاص از چه شناسه ای برای session استفاده می نمايد ) .
شناسه ، تنها اطلاعات مبادله شده بين سرويس دهنده وب و سرويس گيرنده است . زمانی كه سرويس گيرنده شناسه session خود را ارائه می نمايد ، ASP.NET در اولين اقدام جستجو جهت يافتن session متناظر با آن را انجام می دهد . در صورتی كه ماحصل فرآيند فوق مثبت باشد ، داده از state server بازيابی و به اشياء مورد نظر تبديل می گردد . در ادامه ، اشياء فوق در يك مجموعه خاص استقرار می گردند تا امكان دستيابی به آنها از طريق كد وجود داشته باشد . فرآيند فوق بطور اتوماتيك انجام می شود .
شايد برای شما اين سوال مطرح شده باشد كه ASP.NET ، اطلاعات مربوط به session را در چه مكانی ذخيره و چگونه آنها را serialize و deserialize می نمايد ؟ در ASP كلاسيك ، session state به عنوان يك شی COM پياده سازی شده است كه در كتابخانه asp.dll مستقر می گردد . در ASP.NET ، اينترفيس برنامه نويسی تقريبا" يكسان است ولی نحوه پياده سازی آن با ASP كلاسيك كاملا" متفاوت است .
زمانی كه ASP.NET يك درخواست HTTP را بررسی می نمايد ، آن را از طريق مجموعه ای از مدول های مختلف كه قادر به واكنش در خصوص رويدادهای برنامه می باشند ، به حركت در می آورد . SessionStateModule ، يكی از مدول های موجود در اين زنجيره است ( موجود در namespace با نام System.Web.SessionState ) . مدول فوق شناسه session را توليد ، داده session را از ارائه دهندگان خارجی state بازيابی و داده را به درخواست مورد نظر نسبت می دهد . همچنين مدول فوق ، اطلاعات مربوط به session را پس از اتمام پردازش صفحه ، ذخيره می نمايد.
توجه داشته باشيد كه مدول SessionStateModule عملا" داده session را ذخيره نمی نمايد . در واقع ، داده session در عناصر مجزاء نگهداری می گردد كه به آنها state provider می گويند .
شكل 1 معماری session state در ASP.NET را نشان می دهد .
کد:
http://www.srco.ir/Articles/images/StateManagement7.jpg (http://forum.patoghu.com/redirector.php?url=http%3A%2F%2Fwww.srco.ir%2FArti cles%2Fimages%2FStateManagement7.jpg)
نكته آخر در ارتباط با معماری فوق نحوه پيگيری كوكی از يك درخواست به درخواست ديگر است . برای اين كه session state به درستی كار كند ، سرويس گيرنده می بايست شناسه session خود را همراه با هر درخواست ارائه نمايد . بدين منظور از دو روش مختلف استفاده می گردد .
*
استفاده از كوكی : در اين حالت ، شناسه session از طريق يك كوكی خاص (ASP.NET_SessionId) كه ASP.NET بطور اتوماتيك و در زمان استفاده از مجموعه session آن را ايجاد می نمايد ، ارسال می گردد . گزينه فوق به صورت پيش فرض انتخاب می گردد و مشابه رويكردی است كه از آن در نسخه های اوليه ASP استفاده می گرديد .
*
استفاده از URLs اصلاح شده : در اين حالت ، شناسه session از طريق يك URL اصلاح شده خاص ارسال می گردد . گزينه فوق يك ويژگی جديد در ASP.NET است و به پياده كنندگان اجازه می دهد در مواردی كه سرويس گيرنده از كوكی حمايت نمی نمايد ، از session state در برنامه های وب استفاده نمايند .
استفاده از session state
با استفاده از كلاس System.Web.SessionState.HttpSessionState كه در يك صفحه ASP.NET به عنوان شی session از قبل تعبيه شده پيش بينی شده است ، می توان با session state ارتباط برقرار كرد . نحوه اضافه كردن و بازيابی داده در مجموعه session state همانند view state است .
مثلا" می توان يك Dataset را در session قرار داد . كد زير نحوه انجام اين كار را نشان می دهد .
Session("ds") = ds
كد زير نحوه بازيابی و تبديل داده ذخيره شده در session را نشان می دهد .
ds = Ctype(Session("ds"),DataSet)
امكان دستيابی به session state در تمامی برنامه و برای كاربر جاری امكان پذير است . session state به دلايل متعددی ممكن است از بين رود :
*
بستن و فعال كردن مجدد مرورگر توسط كاربر
*
دستيابی به صفحه مشابه از طريق يك پنجره جداگانه مرورگر توسط كاربر
*
اتمام تاريخ اعتبار session به دليل عدم فعاليت كاربر در يك بازه زمانی خاص ( مقدار پيش فرض 20 دقيقه )
*
خاتمه دادن به عمر مفيد يك session از طريق كد و توسط برنامه نويس ( استفاده از متد Session.Abandon)
در دو مورد اول ، session همچنان در حافظه باقی خواهد ماند چراكه سرويس دهنده وب از بستن مرورگر و يا تغيير پنجره توسط كاربر آگاهی ندارد . در چنين مواردی ، session آخرين لحظات عمر خود را در حافظه طی می نمايد و عملا" غيرقابل دسترس باقی می ماند تا زمانی كه عمر آن به اتمام رسد .
علاوه بر موارد فوق ، زمانی كه application domain مجددا" ايجاد گردد ، session state حذف خواهد شد . فرآيند فوق در زمان بهنگام سازی برنامه و يا تغيير در تنظيمات پيكربندی انجام می شود .
همچنين به منظور حصول اطمينان از صحت عملكرد برنامه ، application domain بطور ادواری بازسازی می شود . در صورتی كه رويكرد فوق باعث بروز مسائلی می گردد ، می توان اطلاعات session state را به صورت out of process ذخيره كرد ( در بخش بعد در اين رابطه توضيح خواهيم داد ) . در مدل نگهداری state به صورت out-of-process ، اطلاعات session حتی با غيرفعال شدن application domain همچنان باقی خواهند مان
(بخش نهم)
مثال
در اين مثال هدف آشنائی با نحوه ذخيره و بازيابی داده در session است . بدين منظور يك شی با نام Articles و شامل سه فيلد عمومی به شرح زير تعريف شده است :
*
Title : عنوان يك مقاله را در خود ذخيره می نمايد .
*
Abstract : شرح مختصری از مقاله را در خود ذخيره می نمايد .
*
ViewCount : تعداد دفعات مشاهده يك مقاله را مشخص می نمايد .
شی فوق از يك constructor خاص استفاده می نمايد تا فرآيند ايجاد و مقداردهی آن به سادگی انجام شود .
Public Class Articles
Public Title As String
Public Abstract As String
Public ViewCount As Integer
Public Sub New(ByVal Title As String, ByVal Abstract As String, ByVal ViewCount As Integer)
Me.Title = Title
Me.Abstract = Abstract
Me.ViewCount = ViewCount
End Sub
End Class
اشياء Articles در زمان استقرار صفحه در حافظه ايجاد و در session state ذخيره می گردند . در ادامه و پس از انتخاب يك آيتم توسط كاربر از طريق ليست موجود ، شی مرتبط با آيتم انتخاب شده از session بازيابی و اطلاعات مرتبط با آن در خروجی نمايش داده می شود .
صفحه SessionStateExample.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Public Class Articles
Public Title As String
Public Abstract As String
Public ViewCount As Integer
Public Sub New(ByVal Title As String, ByVal Abstract As String, ByVal ViewCount As Integer)
Me.Title = Title
Me.Abstract = Abstract
Me.ViewCount = ViewCount
End Sub
End Class
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
If Me.IsPostBack = False Then
مرحله اول : ايجاد اشياء '
Dim ArticleInfo1 As New Articles("State Management در ASP. NET 2.0 (بخش هشتم) ", _
" بررسی Session State ", 43)
Dim ArticleInfo2 As New Articles("State Management در ASP. NET 2.0 (بخش هفتم) ", _
" كوكی های سفارشی و نحوه عملكرد آنها", 94)
Dim ArticleInfo3 As New Articles("State Management در ASP. NET 2.0 (بخش ششم) ", _
" نحوه انتقال اطلاعات بين صفحات با استفاده از Query String", 103)
Dim ArticleInfo4 As New Articles(" State Management در ASP. NET 2.0 (بخش پنجم) ", _
" نحوه دريافت اطلاعات از صفحه مبداء در cross-page posting", 99)
مرحله دوم : اضافه كردن اشياء به session state '
Session("Article1") = ArticleInfo1
Session("Article2") = ArticleInfo2
Session("Article3") = ArticleInfo3
Session("Article4") = ArticleInfo4
مرحله سوم : اضافه كردن سطر به ليست '
lstItems.Items.Clear()
lstItems.Items.Add(ArticleInfo1.Title)
lstItems.Items.Add(ArticleInfo2.Title)
lstItems.Items.Add(ArticleInfo3.Title)
lstItems.Items.Add(ArticleInfo4.Title)
End If
نمايش برخی اطلاعات پايه در رابطه با session '
جهت بررسی اطلاعات پيكربندی '
Dim strCookieLess As String
Dim strNewSession As String
If Session.IsCookieless Then
strCookieLess = "بلی"
Else
strCookieLess = "خير"
End If
If Session.IsNewSession Then
strNewSession = "بلی"
Else
strNewSession = "خير"
End If
lblSession.Text = "شناسه : " & Session.SessionID
lblSession.Text &= "<br>تعداد اشياء : "
lblSession.Text &= Session.Count.ToString()
lblSession.Text &= "<br>مد : " & Session.Mode.ToString()
lblSession.Text &= "<br>آيا session ايجاد شده Cookieless است؟ "
lblSession.Text &= strCookieLess
lblSession.Text &= "<br> آيا session جديد است ؟ "
lblSession.Text &= strNewSession
lblSession.Text &= "<br> اعتبار session (بر حسب دقيقه ) : "
lblSession.Text &= Session.Timeout.ToString()
End Sub
Protected Sub cmdMoreInfo_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdMoreInfo.Click
If lstItems.SelectedIndex = -1 Then
lblRecord.Text = "آيتمی انتخاب نشده است"
Else
مرحله اول :ايجاد نام كليد صحيح بر اساس ايندكس '
Dim Key As String
Key = "Article" & (lstItems.SelectedIndex + 1).ToString()
مرحله دوم : بازيابی اشياء از session state '
Dim ArticleInfo As Articles = CType(Session(Key), Articles)
مرحله سوم : نمايش اطلاعات مرتبط با شی بازيابی شده '
lblRecord.Text = "عنوان مقاله :" & ArticleInfo.Title
lblRecord.Text &= "<br>شرح: "
lblRecord.Text &= ArticleInfo.Abstract
lblRecord.Text &= "<br>تعداد دفعات مشاهده : " & ArticleInfo.ViewCount.ToString()
End If
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl">
<head runat="server">
<title>تست session </title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label id="lblSession" runat="server" Width="472px" Height="61px" Font-Size="Smaller"
Font-Names="Tahoma" Font-Bold="False"></asp:Label><br /><br />
<div >
<asp:ListBox id="lstItems" runat="server" Width="345px" Height="106px"
Font-Names="Tahoma"></asp:ListBox><br /> <br />
<asp:Button id="cmdMoreInfo" runat="server" Text="اطلاعات بيشتر "
Font-Names="Tahoma"></asp:Button><br /><br /><br />
<asp:Label id="lblRecord" runat="server" Font-Size="Small"
Font-Names="Tahoma" ></asp:Label>
</div>
</form>
</body>
</html>
شكل 1 نحوه عملكرد و خروجی برنامه فوق را نشان می دهد .
حتی المقدور می بايست از تعداد session اندكی در برنامه استفاده گردد چراكه مديريت آنها مستلزم انجام عمليات اضافه و استفاده از منابع محدود موجود در سمت سرويس دهنده است . پياده كنندگان برنامه های وب برای رفع اين نگرانی می توانند يك دكمه Log out را در صفحه مورد نظر خود پيش بينی نمايند تا پس از كليك بر روی آن ، با استفاده از متد Session.Abandon اقدام به حذف session گردد ( آزاد سازی حافظه سرويس دهنده زودتر از موعد مقرر و مشخص شده توسط خصلت Timeout ) .
(بخش دهم)
پيكربندی session در برنامه های وب
پياده كنندگان برنامه های وب برای پيكربندی session state می توانند از فايل web.config ( موجود در دايركتوری مجازی شامل فايل های aspx . ) استفاده نمايند . با استفاده از فايل فوق می توان گزينه های پيشرفته ای نظير timeout و مد session state را پيكربندی كرد . در صورتی كه از ويژوال استوديو برای ايجاد يك برنامه وب استفاده شده باشد ، همزمان با ايجاد پروژه ، بطور اتوماتيك يك فايل web.config نيز ايجاد می گردد .
كد زير يك نمونه فايل web.config را به همراه مهمترين خصلت های تاثيرگذار در پيكربندی session state را نشان می دهد .
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
...
<sessionState
cookieless="UseCookies" cookieName="ASP.NET_SessionId"
regenerateExpiredSessionId="false"
timeout="20"
mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
sqlCommandTimeout="30"
allowCustomSqlDatabase="false"
customProvider=""
/>
</system.web>
</configuration>
در ادامه به تشريح هر يك از خصلت های فوق خواهيم پرداخت .
Cookieless
مقدار خصلت فوق بر اساس شرايط زير تعيين می گردد .
*
UseCookies : گزينه پيش فرض است و همواره از كوكی استفاده خواهد شد حتی اگر مرورگر و يا دستگاه سرويس گيرنده از آن حمايت نكند و يا آن را غيرفعال كرده باشد . در صورتی كه دستگاه سرويس گيرنده از كوكی حمايت نكند ، اطلاعات session در بين درخواست های متوالی گم می شود . چراكه هر درخواست يك ID جديد را خواهد گرفت .
*
UseUri : از كوكی صرفنظر از قابليت های مرورگر و يا دستگاه سرويس گيرنده استفاده نخواهد شد . در چنين مواردی ، شناسه session در يك URL ذخيره می گردد .
*
UseDeviceProfile : معيار انتخاب ASP. NET جهت استفاده از cookieless session ، بررسی نتايج حاصل از بكارگيری شی BrowserCapabilities است . شی فوق صرفا" پتانسيل هائی را كه دستگاه مورد نظر از آنها حمايت می نمايد مشخص می كند ( خود را درگير مواردی نظير غيرفعال كردن كوكی توسط كاربر نمی كند ) .
*
AutoDetect : در اين روش ، در آغاز ASP. NET سعی می كند تشخيص دهد كه آيا مرورگر از كوكی حمايت می نمايد . بدين منظور يك كوكی بر روی كامپيوتر سرويس گيرنده ايجاد و در ادامه آن را بازيابی می نمايد . ماحصل فرآيند فوق می تواند اين موضوع را به اثبات رساند كه مرورگر از كوكی حمايت می نمايد ولی توسط كاربر غير فعال شده است ( در چنين مواردی از مد cookieless استفاده می گردد )
كد زير بر استفاده از مد cookieless تاكيد می نمايد ( مناسب برای تست ) .
<sessionState cookieless="UseUri" ...="" />
در مد cookieless ، شناسه session بطور اتوماتيك درون يك URL قرار می گيرد . زمانی كه ASP. NET يك درخواست را دريافت می نمايد ، شناسه آن را حذف ، مجموعه session را بازيابی و درخواست دريافتی را برای دايركتوری مورد نظر ارسال می نمايد .
با توجه به اين كه شناسه session درون URL جاری قرار می گيرد ، لينك های مربوطه نير بطور اتوماتيك قادر به استفاده از شناسه session خواهند بود . به عبارت ديگر ، در صورتی كه كاربر بر روی page1.aspx باشد و بر روی لينك مربوط به page2.aspx كليك نمايد ، لينك مربوطه شامل شناسه session جاری به عنوان بخشی از URL مورد نظر خواهد بود . سناريوی فوق در مواردی كه از متد Response.Redirect به همراه يك URL نسبی استفاده شده باشد نيز صدق می كند .
Response.Redirect("Page2.aspx")
مثال
در اين مثال با نحوه استفاده از session آشنا خواهيم شد . بدين منظور از دو صفحه با مد cookieless استفاده شده است ( در فايل web.config مقدار cookieless معادل " UseUri" در نظر گرفته شده است ) . اولين صفحه ( Cookieless1.aspx ) شامل يك كنترل Hyperlink و دو دكمه است . دومين صفحه ( Cookieless1.aspx) ، صفحه ای است كه كاربر پس از كليك بر روی يكی از گزينه های موجود به آن هدايت شده و پس از بازيابی session ، اطلاعات در خروجی نمايش داده می شود .
شكل 1 ، نحوه عملكرد صفحه Cookieless1.aspx را نشان می دهد .
*
لينك به همراه مسير نسبی : خصلت Hyperlink.NavigateUrl از طريق كد مقدار Cookieless1.aspx را می گيرد. در صورت كليك بر روی لينك فوق ، شناسه session بازيابی و می توان از اطلاعات session در صفحه جديد ( Cookieless2.aspx) استفاده كرد .
*
تغيير مسير ( مسير نسبی ) : تعيير مسير از طريق كد با مد cookieless نيز كار می كند ( همانند بكارگيری يك مسير نسبی ) . در مثال فوق از متد Response. Redirect برای هدايت كاربر به صفحه Cookieless2.aspx استفاده شده است . در صورت كليك بر روی دكمه فوق ، شناسه session بازيابی و امكان استفاده از اطلاعات session در صفحه جديد فراهم می گردد . كد زير نحوه انجام اين كار را نشان می دهد .
Protected Sub cmdLink_Click(ByVal sender As Object,ByVal As EventArgs) Handles cmdLink.Click
Response.Redirect("Cookieless2.aspx")
End Sub
*
تغيير مسير ( مسير مطلق ) : تنها محدوديت cookieless ، عدم امكان استفاده از لينك های absolute است . چراكه ASP. NET نمی تواند شناسه session را درون آنها قرار دهد . مثلا" در صورتی كه بر روی دكمه دوم كليك شود ، امكان استفاده از session جاری در صفحه Cookieless2.aspx وجود نخواهد داشت . كد زير نحوه انجام اين كار را نشان می دهد .
Protected Sub cmdLinkAbsolute_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdLinkAbsolute.Click
Dim url As String = "http://" & Request.Url.Authority & _
Request.Url.Segments(0) & Request.Url.Segments(1) & "Cookieless2.aspx"
Response.Redirect(url)
End Sub
كد صفحات Cookieless1.aspx و Cookieless2.aspx در جداول زير نشان داده شده است .
صفحه Cookieless1.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Protected Sub cmdLink_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdLink.Click
Response.Redirect("Cookieless2.aspx")
End Sub
Protected Sub cmdLinkAbsolute_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim url As String = "http://" & Request.Url.Authority & Request.Url.Segments(0) &_
Request.Url.Segments(1) & "Cookieless2.aspx"
Response.Redirect(url)
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Session("test") = "Test String"
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>تست session </title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server">
<div>
<strong> تست session <br /> </strong>
<br />
<asp:HyperLink id="lnkRedirect" runat="server" Width="191px" Height="25px"
NavigateUrl="Cookieless2.aspx">لينك به همراه مسير نسبی</asp:HyperLink><br />
<br />
<asp:Button id="cmdLinkAbsolute" runat="server" Width="183px"
Text="تغيير مسير(مسير مطلق)" Font-Names="Tahoma" Font-Size="Small" ></asp:Button><br /><br />
<asp:Button id="cmdLink" runat="server" Width="187px"
Text="تغيير مسير ( مسير نسبی ) " Font-Names="Tahoma" Font-Size="Small" ></asp:Button>
</div>
</form>
</body>
</html>
صفحه Cookieless2.aspx
<%@ Page Language="VB" Culture="fa-IR" UICulture="fa-IR" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Session("test") Is Nothing Then
lblInfo.Text = "اطلاعات session موجود نمی باشد"
Else
lblInfo.Text = "اطلاعات session با موفقيت بازيابی گرديد " & CType(Session("test"), String)
End If
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" dir="rtl" >
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body style="font-family: Tahoma">
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Font-Bold="True"
Font-Names="Tahoma" Font-Size="Small"
Height="52px" Style="z-index: 101; left: 488px; position: absolute; top: 25px"
Width="353px" ForeColor="#C04000"></asp:Label>
</div>
</form>
</body>
</html>
به صورت پيش فرض ، ASP. NET امكان استفاده مجدد از يك شناسه session را فراهم می نمايد. مثلا" در صورتی كه درخواستی ايجاد و query string شامل يك session باشد كه مدت زمان اعتبار آن به پايان رسيده باشد ، ASP. NET يك session جديد را ايجاد و از شناسه session استفاده می نمايد .
مشاهده ناخودآگاه يك شناسه session در يك مكان عمومی نظير نتايج ارائه شده توسط يك موتور جستجو يكی از چالش های مهم روش فوق محسوب می گردد كه ممكن است زمينه دستيابی چندين كاربر به سرويس دهنده با استفاده از شناسه session مشابه را فراهم نمايد .
برای پيشگيری از اين تهديد امنيتی ، می بايست از خصلت regenerateExpiredSessionId با مقدارtrue استفاده شود ( زمانی كه از session با مد cookieless استفاده شده باشد ) . در چنين مواردی ، در صورتی كه يك كاربر با يك شناسه session كه تاريخ اعتبار آن به اتمام رسيده است به سرويس دهنده متصل شده باشد ، يك شناسه session جديد برای وی ايجاد خواهد شد . تنها نكته قابل تامل در اين روش ، از دست دادن مقادير موجود در view sate و داده موجود در فرم است ، چراكه ASP. NET برای حصول اطمينان از اين موضوع كه مرورگر دارای يك شناسه جديد session است ، عمليات redirect را انجام خواهد داد .
(بخش يازدهم)
Timeout
يكی ديگر از تنظيمات مهم در ارتباط با session state ، مشخص كردن مدت زمان timeout است . مقدار در نظر گرفته شده برای خصلت فوق ( تعريف شده در فايل web.config ) ، مدت زمان انتظار ASP.NET قبل از حذف session را مشخص می كند ( عدم دريافت هيچگونه درخواست در بازه زمانی مشخص شده ) .
در نمونه كد زير به ASP.NET اعلام شده است كه اگر پس از گذشت 20 دقيقه درخواستی از سرويس گيرنده دريافت نگرديد ، session آن را حذف كن .
<sessionState
timeout="20" />
خصلت فوق يكی از مهمترين پارامترهای مديريت session در برنامه های وب است كه عدم مقداردهی مناسب آن می تواند نتايج نامطلوبی را در ارتباط با كارآئی يك برنامه وب به دنبال داشته باشد . در زمان مقداردهی پارامتر فوق می بايست به اين نكته دقت شود كه اولا" زمان در نظر گرفته شده به اندازه ای كوتاه باشد كه سرويس دهنده بتواند پس از سپری شدن مدت زمان اندكی كه كاربر از برنامه استفاده نمی نمايد ، منبع ارزشمند حافظه را آزاد نمايد و ثانيا" كاربر بتواند بدون نگرانی در خصوص از دست دادن session خود با خيالی آسوده از برنامه استفاده نمايد .
در صورت نياز ، می توان مقداردهی پارامتر فوق را از طريق كد نيز انجام داد . به عنوان نمونه در مواردی كه يك session حاوی يك حجم غيرمتعارف از اطلاعات باشد ، می توان مدت زمان حيات session را محدودتر كرد . كد زير نحوه تغيير مقدار پارامتر فوق را به 10 دقيقه نشان می دهد .
Session.Timeout = 10
Mode
با استفاده از خصلت mode می توان نحوه ذخيره سازی اطلاعات session را مشخص كرد . به اين خصلت می توان مقاديری نظير InProc ، off ، StateServer ، SQLServer و Custom را نسبت داد . در واقع به كمك خصلت فوق ، استراتژی ذخيره سازی اطلاعات session مشخص می گردد .
در ادامه با هر يك از موارد فوق بيشتر آشنا خواهيم شد . قبل از آن لازم است به يك نكته مهم اشاره گردد . در صورتی كه برنامه ASP.NET بر روی بيش از يك سرويس دهنده وب هاست شده باشد ( كه از آن با نام web farm ياد می شود ) ، می بايست دامنه پيكربندی را گسترش داد تا اين اطمينان ايجاد شود كه سرويس دهندگان وب همساز می باشند . در غيراينصورت ، ممكن است يك سرويس دهنده اطلاعات موجود در session را با روشی متفاوت نسبت به سرويس دهنده ديگر ، رمز نمايد . بديهی است در چنين مواردی اگر كاربر از يك سرويس دهنده به سرويس دهنده ديگر هدايت شود ، در session وی اختلال ايجاد خواهد شد . برای حل اين مشكل می بايست با مراجعه به بخش <machineKey> فايل machine.config تنظميات مورد نظر را بگونه ای انجام داد كه شيوه رمزنگاری session بر روی يك سرويس دهنده با سرويس دهنده ديگر يكسان و سازگار باشد .
InProc
مقدار پيش فرض خصلت mode می باشد و عملكرد آن همانند ذخيره سازی session state در نسخه های قديمی ASP است . در اين روش اطلاعات در پردازه مشابه ASP.NET worker threads ذخيره می گردند . اين روش بالاترين كارآئی و كمترين ماندگاری را دارد . در صورتی كه سرويس دهنده به هر دليلی راه اندازی مجدد گردد ، اطلاعات session از بين خواهند رفت . روش فوق برای اكثر وب سايت های كوچك مناسب است . در مواردی كه برنامه وب در يك web farm هاست شده باشد ، از اين روش نمی توان استفاده كرد . در چنين مواردی و به منظور به اشتراگ گذاشتن اطلاعات session بين چندين سرويس دهنده ، می بايست از گزينه Out-of-Process و يا سرويس SQL Server state استفاده كرد .
در برخی موارد ممكن است برنامه نويسان به اين نتيجه رسيده باشند كه كاربران اطلاعات session خود را بدون هيچگونه دليلی از دست می دهند . همين امر باعث می شود كه آنان استفاده از گزينه ای غير از InProc را در دستور كار قرار دهند . در ASP.NET ، حوزه برنامه ها به دلايل متعددی ممكن است راه اندازی مجدد گردد ( نظير اعمال تغييرات در پيكربندی ، بهنگام سازی صفحات ) .
توجه داشته باشيد ، در زمان استفاده از StateServer و يا SQLServer ، اشيائی می توانند در session state ذخيره گردند كه قابليت سريال شدن را داشته باشند . در غيراينصورت ، ASP.NET قادر به انتقال و يا ارسال اشياء به state service و يا ذخيره آنها در بانك اطلاعاتی نخواهد بود .
off
با انتخاب مقدار فوق برای خصلت mode ، مديريت state در تمامی صفحات يك برنامه وب غيرفعال خواهد شد . بديهی است با غيرفعال كردن session شاهد بهبود ملموس كارآئی در وب سايت هائی خواهيم بود كه عملكرد و سرويس دهی آنها مشروط به استفاده از session نمی باشد .
StateServer
با در نظر گرفتن مقدار فوق برای خصلت mode ، از يك سرويس ويندوز جداگانه برای مديريت state استفاده می گردد . سرويس فوق بر روی سرويس دهنده مشابه اجراء می گردد ولی در خارج از پردازه اصلی ASP.NET قرار می گيرد . رويكرد فوق دارای مزايا و معايب مختص به خود می باشد . مهمترين مزيت استفاده از يك سرويس دهنده ديگر برای ذخيره اطلاعات session ، عدم وابستگی آن به پردازه ASP.NET است . در چنين مواردی با راه اندازی مجدد پردازه ASP.NET ( به هر دليل ) ، اختلالی در داده ذخيره شده در session ايجاد نخواهد شد چراكه آنها در يك سرويس دهنده جداگانه نگهداری شده اند . از مهمترين معايب و يا بهتر بگوئيم محدوديت های رويكرد فوق ، افزايش تاخير زمانی در زمان ارسال اطلاعات session بين دو پردازه است . بديهی است در صورتی كه فركانس دستيابی و تغيير اطلاعات ذخيره شده در session بالا باشد ، سرعت و كارآئی يك برنامه وب كاهش می يابد.
در زمان استفاده از StateServer ، می بايست مقدار stateConnectionString را مشخص كرد . پارامتر فوق ، آدرس IP كامپيوتری را كه بر روی آن سرويس StateServer اجراء شده است را به همراه شماره پورت مربوطه مشخص می نمايد ( شماره پورت توسط ASP.NET تعيين می گردد و معمولا" لزومی به تغيير آن وجود ندارد ) . بدين ترتيب ، می توان StateServer را بر روی كامپيوتر ديگر هاست كرد . در صورتی كه قصد تغيير تنظيمات پيش فرض را نداشته باشيم ، از سرويس دهنده محلی استفاده خواهد شد ( با آدرس IP : 127.0.0.1 ) .
قبل از اين كه برنامه وب بتواند از سرويس فوق استفاده نمايد ، می بايست آن را اجراء كرد . ساده ترين روش برای انجام اين كار انتخاب گزينه Services از طريق Control Panel است . با مشاهده ASP.NET State Service در ليست سرويس ها ، می توان نحوه اجراء آن را مشخص نمود ( بطور اتوماتيك ) .
در مواردی كه از StateServer استفاده می گردد ، می توان برای خصلت اختياری stateNetworkTimeout يك مقدار را مشخص نمود . پارامتر فوق ، حداكثر مدت زمان انتظار برای پاسخ سرويس دهنده بر حسب ثانيه را مشخص می نمايد . مقدار گزينه پيش فرض ، 10 ثانيه است .
SQLServer
با در نظر گرفتن مقدار فوق برای خصلت mode ، از يك بانك اطلاعاتی SQL Server برای ذخيره اطلاعات session استفاده می گردد . بانك اطلاعاتی مورد نظر توسط خصلت sqlConnectionString مشخص می گردد . روش فوق متداولترين مكانيزم برای ذخيره state در برنامه های وب می باشد ولی در مقايسه با روش های ديگر از سرعت كمتری برخوردار است .
برای استفاده از روش فوق می بايست از يك سرويس دهنده SQL استفاده شود . مقدار خصلت sqlConnectionString ، همانند الگوی استفاده شده جهت دستيابی به داده توسط ADO. NET است و شامل مشخص كردن يك منبع داده ( آدرس سرويس دهنده ) ، يك رمزعبور و شناسه كاربر ( مگر اين كه از integrated security استفاده شده باشد ) است . علاوه بر اين ، می بايست stored procedures و session موقت بانك اطلاعاتی نصب گردند . stored procedures مسئوليت ذخيره و بازيابی اطلاعات session را برعهده دارند .
ASP.NET شامل يك اسكريپت Transact-SQL برای اين هدف خاص با نام InstallSqlState.sql است كه در
مسير [ C:\[WinDir]\Microsoft.NET\Framework\[Version قرار دارد . برای اجرای اسكريپت فوق می توان از يك برنامه كاربردی SQL Server نظير SQL Server Management Studio يا sqlcmd.exe ( برای سرويس SQL 2005 ) و يا OSQL.exe و Query ****yze ( برای نسخه های قبلی ) استفاده كرد . اسكريپت فوق صرفا" يك مرتبه و به منظور ايجاد بانك ، جداول و stored procedures مورد نياز اجراء خواهد شد .
نام بانك اطلاعاتی معمولا" ASPState می باشد .در واقع ، connection string موجود در فايل web.config با صراحت نام بانك اطلاعاتی را مشخص نخواهد كرد بلكه صرفا" مكان سرويس دهنده و نوع تائيديه مشخص می گردد . كد زير نحوه انجام اين كار را نشان می دهد .
<sessionState
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
...
/>
در صورتی كه قصد استفاده از يك بانك اطلاعاتی با نام ديگر و ساختار مشابه را داشته باشيم ، كافی است مقدار خصلت CustomSqlDatabase برابر با true در نظر گرفته شود .
<sessionState
allowCustomSqlDatabase="true"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI;Initial Catalog=CustDatabase"
...
/>
زمانی كه از يك بانك اطلاعاتی SQL Server برای ذخيره اطلاعات session استفاده می گردد ، می توان از گزينه اختياری sqlCommandTimeout است كرد . پارامتر فوق ، حداكثر مدت زمان انتظار برای پاسخ سرويس دهنده بر حسب ثانيه را مشخص می نمايد . مقدار گزينه پيش فرض ، 30 ثانيه است .
Custom
زمانی كه برای خصلت mode مقدار custom در نظر گرفته می شود ، می بايست session state provider را با استفاده از خصلت customProvider مشخص كرد . خصلت فوق به نام يك كلاس كه بخشی از برنامه وب موجود در دايركتوری App_Code است و يا يك اسمبلی كمپايل شده موجود در دايركتوری BIN و يا GAC ، اشاره می نمايد .
ايجاد يك provider سفارشی ، مسائل مختص به خود را دارد و می بايست با دقت پياده سازی گردد تا بتواند اهدافی نظير امنيت و قابليت توسعه را به خوبی تامين نمايد . بحث بر روی provider سفارشی خارج از حوصله اين مقاله است .
برخی توليد كنندگان ممكن است نسخه هائی خاص از state provider را ارائه نمايند كه در صورت نياز و تمايل می توان از آنها استفاده كرد . به عنوان مثال ، اوراكل ممكن است يك provider سفارشی را ارائه نمايد كه امكان ذخيره اطلاعات session را در يك بانك اطلاعاتی اوراكل فراهم نمايد .
(بخش دوازدهم)
با استفاده از application state می توان اشياء سراسری ( global ) را با هدف دستيابی توسط هر يك از سرويس گيرندگان ذخيره كرد . عملكرد application state بر اساس كلاس System.Web.HttpApplicationState می باشد كه بطور پيش فرض از طريق شی از قبل تعبيه شده Application در تمامی صفحات قابل استفاده است .
طرز كار application state مشابه session state است و از اشيائی با نوع های مشابه ، نگهداری اطلاعات در سمت سرويس دهنده و گرامر مبتنی بر ديكشنری استفاده می نمايد . استفاده از يك شمارنده سراسری به منظور نگهداری تعداد دفعاتی كه يك عمليات خاص توسط تمامی سرويس گيرندگان يك برنامه وب انجام می شود ، يك نمونه متداول استفاده از application state می باشد .
مثلا" می توان يك event handler در فايل global.asax را تعريف كرد تا تعداد session ايجاد شده و يا تعداد درخواست های دريافتی توسط يك برنامه را ثبت كند . همچنين ، می توان از روشی مشابه در Page. Load به منظور تعيين تعداد دفعاتی كه يك صفحه خاص توسط سرويس گيرندگان مختلف درخواست شده است ، استفاده كرد .
كد زير نحوه انجام اين كار را مشخص می كند .
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim Count As Integer = CType(Application("HitCounter"), Integer)
Count += 1
Application("HitCounter") = Count
lblCounter.Text = Count.ToString()
End Sub
لازم است مجددا" به اين نكته اشاره گردد كه آيتم های application state به عنوان شی ذخيره می گردند ، بنابراين می بايست در زمان بازيابی آنها را cast كرد . تاريخ اعتبار و يا مصرف آيتم های ذخيره شده در application state هرگز به اتمام نخواهد رسيد و تا زمانی كه برنامه و يا سرويس دهنده راه اندازی مجدد نگردد و يا حوزه برنامه خود را refresh ننمايد ( به علت تنظيمات ادواری اتوماتيك پردازه و يا بهنگام سازی يكی از صفحات و يا عناصر موجود در برنامه ) ، امكان استفاده از آنها وجود خواهد داشت .
امروزه اغلب از application state به دليل عدم كارآئی مناسب استفاده نمی شود . در مثال قبل ، همواره اين احتمال وجود خواهد داشت كه در شمارنده مقدار درستی ذخيره نگردد ( خصوصا" در مواردی كه ترافيك بالا باشد ) . به عنوان مثال ، در صورتی كه دو سرويس گيرنده در يك زمان مشابه صفحه ای را درخواست نمايند ، دارای مجموعه ای از رويدادها به شرح ذيل خواهيم بود :
* كاربر A مقدار جاری شمارنده را ( فرضا" عدد 432 ) بازيابی می نمايد .
* كاربر B مقدار جاری شمارنده را ( فرضا" عدد 432 ) بازيابی می نمايد .
* كاربر A مقدار شمارنده را تغيير و آن را به 433 تغيير می دهد .
* كاربر B مقدار شمارنده را تغيير و آن را به 433 تغيير می دهد .
به عبارت ديگر ، يكی از درخواست ها باعث افزايش شمارنده نمی گردد ، چراكه دو سرويس گيرنده بطور همزمان به شمارنده دستيابی داشته اند . برای پيشگيری از بروز اين مسئله ، می بايست از متدهای Lock و Unlock استفاده كرد . با استفاده از متدهای فوق در هر لحظه صرفا" به يك سرويس گيرنده اجازه دستيابی به مجموعه application state داده می شود .
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Application.Lock()
Dim Count As Integer = CType(Application("HitCounter"), Integer)
Count += 1
Application("HitCounter") = Count
Application.Unlock()
lblCounter.Text = Count.ToString()
End Sub
ساير سرويس گيرندگانی كه صفحه را درخواست می نمايند می بايست تا زمانی كه مجموعه application state آزاد نشده است ، منتظر بمانند . اين وضعيت می تواند كاهش كارآئی برنامه را به دنبال داشته باشد .
به عنوان يك قانون كلی ، مقاديری كه دائما" و با فركانس بالا تغيير می يابند نمی توانند كانديدی مناسب جهت استفاده از application state باشند .
application state در دنيای دات نت بندرت استفاده می شود چراكه دو كاربرد متداول استفاده از آن با روش های موثرتر ديگر جايگزين شده است .
*
در گذشته ، از application state برای ذخيره ثوابت در سطح برنامه نظير يك connection string بانك اطلاعاتی استفاده می گرديد . هم اينك در دات نت می توان اين نوع ثوابت را به سادگی در فايل web.config كه به مراتب از انعطاف بيشتری برخوردار است ذخيره نمود .
*
در گذشته برای ذخيره اطلاعاتی كه در يك برنامه از آنها بدفعات استفاده می گرديد و برای توليد اين اطلاعات زمان زيادی صرف می گرديد ، استفاده می شد ( نظير يك كاتولوگ كامل از محصولات ) . افزايش حجم كاتالوگ و معتبر سازی داده موجود در اينچنين كاتالوگ هائی ، همواره از مشكلات برنامه نويسان بوده است . هم اينك و با استفاده از دات نت می توان از فنآوری cache كه دارای كارآئی بمراتب بالاتری نسبت به روش های پيشين و بكارگرفته شده در application state است ، استفاده كرد .
فايل Global.asax
با استفاده از فايل global.asax می توان كد مورد نياز جهت برخورد با رويدادهائی در سطح برنامه را ايجاد كرد . رويدادهای فوق در نقاط مختلفی از چرخه حيات يك برنامه وب محقق می گردند ( مثلا" زمانی كه session ايجاد می گردد ) . همين موضوع باعث شده است كه فايل global.asax بتواند در تعامل مناسب با ويژگی های state management قرار بگيرد . مثلا" می توان از فايل global.asax برای مقداردهی اوليه مجموعه ای از اشياء كه قصد داريم آنها را در application state ذخيره نمائيم ، استفاده كرد .
فايل global.asax ، همانند يك فايل aspx . است . با اين تفاوت كه اين نوع فايل ها نمی توانند شامل تگ های HTML و يا ASP. NET باشند . در مقابل می توان در آنها event handler مورد نياز را قرار داد . مثلا" فايل global.asax زير در مقابل رويداد Application.EndRequest از خود واكنش نشان می دهد ( رويدادی كه قبل از ارسال صفحه برای كاربر محقق می گردد ) .
<%@ Application Language="VB" %>
<script runat="server">
Sub Application_EndRequest(ByVal sender As Object, ByVal e As EventArgs)
Response.Write("<hr>This page was served at " & DateTime.Now.ToString())
End Sub
</script>
event handler فوق از متد write شی از قبل تعبيه شده Response برای نوشتن يك footer در پائين صفحه ( تاريخ و زمان ايجاد صفحه ) استفاده می نمايد .
هر برنامه ASP.NET می تواند دارای يك فايل global.asax باشد كه پس از استقرار در دايركتوری مجازی مربوطه ، ASP.NET آن را بطور اتوماتيك تشخيص و از آن استفاده خواهد كرد . مثلا" ، اگر فايل global.asax فوق را در يك دايركتوری مجازی قرار دهيم ، شاهد نمايش يك footer در پائين هر يك از صفحات موجود در برنامه خواهيم بود .
اضافه كردن يك footer اتوماتيك در پائين صفحات ، يك عمليات مفيد برای سايت های حرفه ای نمی باشد . نوشتن يك ركورد در يك بانك اطلاعاتی مختص ثبت وقايع ( log ) ، شايد كاربرد مناسب تری از ويژگی فوق را نشان دهد . هم اينك تعداد زيادی از برنامه نويسان برنامه های وب تمايل به استفاده از فايل global.asax را ندارند . فايل فوق از مدل code-behind حمايت می نمايد .
رويدادهای Application
Application.EndRequest ، صرفا" يكی از رويدادهای موجود جهت استفاده در فايل global.asax است . برای ايجاد يك event handler متفاوت ، می توان يك روتين با نام دلخواه را ايجاد كرد .
برخی از متداولترين رويدادهای application عبارتند از :
*
Application_Start : در زمان آغاز به كار برنامه و دريافت اولين درخواست توسط هر يك از كاربران ، محقق می گردد . رويداد فوق در پاسخ به درخواست های بعدی محقق نخواهد شد . از اين رويداد معمولا" برای ايجاد و يا cache برخی اطلاعات اوليه استفاده می شود.
*
Application_End : رويداد فوق پس از اتمام فعاليت برنامه ، محقق می گردد ( عمدتا" زمانی كه سرويس دهنده وب راه اندازی مجدد می گردد ) . در روتين مربوطه می توان كد مورد نياز برای پاكسازی را درج كرد .
*
Application_BeginRequest : رويداد فوق پس از دريافت هر درخواست توسط برنامه ، محقق می گردد ( قبل از اجرای كد صفحه ) .
*
Application_EndRequest : رويداد فوق پس از دريافت هر درخواست توسط برنامه ، محقق می گردد ( پس از اجرای كد صفحه ) .
*
Session_Start : رويداد فوق زمانی محقق می گردد كه درخواست يك كاربر جديد دريافت و يك session فعاليت خود را آغاز نمايد .
*
Session_End : رويداد فوق زمانی محقق می گردد كه مدت زمان حيات يك session به اتمام رسيده باشد ( از طريق كد و يا بطور اتوماتيك ) .
*
Application_Error : رويداد فوق در پاسخ به يك خطاء غيرقابل پيش بينی، محقق می گردد .
State Management در ASP. NET 2.0 (بخش پايانی)
state management ، فرآيندی است كه به كمك آن می توان اطلاعاتی را بين درخواست های متعدد ، نگهداری كرد . اطلاعات فوق معمولا" شامل دو دسته می باشند :
*
اطلاعات در ارتباط با يك كاربر : نظير ليست كالاهای موجود در يك سبد خريد ، نام كاربر و يا يك سطح دستيابی خاص
*
اطلاعات قابل استفاده در تمامی برنامه : نظير آمارهائی كه فعاليت هائی خاص از يك سايت را ثبت می نمايد .
با توجه به اين كه ASP.NET از يك معماری disconnected استفاده می نمايد ، لازم است كه با هر درخواست اطلاعات state ذخيره و آنها را در زمان مورد نياز بازيابی كرد .
استراتژی انتخاب شده برای ذخيره سازی state می تواند بطرز كاملا" محسوسی بر روی پارامترهائی نظير كارآئی ، قابليت گسترش و امنيت يك برنامه وب تاثيرگذار باشد .
Powered by vBulletin™ Version 4.2.0 Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.