محدودیتهای نسخهگذاری در .NET
اشارهگرهای مدیریت شده در NET.
$.ajax({ headers: { 'Authorization': 'Bearer ' + jwtToken },
Cookie - قسمت سوم
Cookie - قسمت اول: مقدمه، تاریخچه، معرفی، و شرح کامل
Cookie - قسمت دوم: کوکی در جاوا اسکریپت
نکته مهم: خواندن قسمتهای قبلی این سری (مخصوصا قسمت اول) برای درک بهتر مطالب پیشنهاد میشود.
.
کوکی در ASP.NET - بخش اول
در قسمتهای قبلی مقدمات و مباحث کلی راجع به کوکیها و انواع آن، شرح کامل خواص، نحوه رفتار مرورگرها با انواع کوکیها و درنهایت نحوه کار کردن با کوکیها در سمت کلاینت با استفاده از زبان محبوب جاوا اسکریپت پرداخته شد.
در ادامه این سری مطالب به نحوه برخورد ASP.NET با کوکیها و چگونگی کار کردن با کوکی در سمت سرور آشنا خواهیم شد. در بخش اول این قسمت مباحث ابتدایی و اولیه برای کار با کوکیها در ASP.NET ارائه میشود. در بخش دوم مباحث پیشرفتهتر همچون SubCookieها در ASP.NET و نیز سایر نکات ریز کار با کوکیها در ASP.NET بحث خواهد شد.
.
.
Response و Request در ASP.NET
در قسمت اول این سری به مفاهیم Http Response و Http Request اشاره کوتاهی شده بود. بهصورت خلاصه، درخواستی که از سمت یک کلاینت به یک وب سرور ارسال میشود Request و پاسخی که وب سرور به آن درخواست میدهد Response نامیده میشود.
در ASP.NET، کلیه اطلاعات مرتبط با درخواست رسیده از سمت یک کلاینت در نمونهای منحصر به فرد از کلاس HttpRequest نگهداری میشود. محل اصلی نگهداری این نمونه در پراپرتی Request از نمونه جاری کلاس System.Web.HttpContext (قابل دسترسی ازطریق HttpContext.Current) است. البته کلاس Page هم یک پراپرتی با نام Request دارد که دقیقا از همین پراپرتی کلاس HttpContext استفاده میکند.
همچنین کلیه اطلاعات مرتبط با پاسخ ارسالی وب سرور به سمت کلاینت مربوطه در نمونهای از کلاس HttpResponse ذخیره میشود. محل اصلی نگهداری این نمونه نیز در پراپرتی Response از نمونه جاری کلاس HttpContext است. همانند Request، کلاس Page یک پراپرتی با نام Response برای نگهداری این نمونه دارد که این هم دقیقا از پراپرتی متناظر در کلاس HttpContext استفاده میکند.
.
.
کوکیها در Response و Request
هر دو کلاس HttpResponse و HttpRequest یک پراپرتی با عنوان Cookies (^ و ^) دارند که مخصوص نگهداری کوکیهای مربوطه هستند. این پراپرتی از نوع System.Web.HttpCookieCollection است که یک کالکشن مخصوص برای ذخیره کوکیهاست.
- این پراپرتی (Cookies) در کلاس HttpRequest محل نگهداری کوکیهای ارسالی توسط مرورگر در درخواست متناظر آن است. کوکیهایی که مرورگر با توجه به شرایط جاری و تنظیمات کوکیها اجازه ارسال به سمت سرور را به آنها داده و در درخواست ارسالی ضمیمه کرده است (با استفاده از هدر :Cookie که در قسمت اول شرح داده شد) و ASP.NET پس از پردازش و Parse دادهها، درون این پراپرتی اضافه کرده است.
- این پراپرتی (Cookies) در کلاس HttpResponse محل ذخیره کوکیهای ارسالی از وب سرور به سمت مرورگر کلاینت در پاسخ به درخواست متناظر است. کوکیهای درون این پراپرتی پس از بررسی و استخراج دادههای موردنیاز توسط ASP.NET در هدر پاسخ ارسالی ضمیمه خواهند شد (با استفاده از هدر :Set-Cookie که در قسمت اول توضیح داده شد).
.
.
ایجاد و بهروزرسانی کوکی در ASP.NET
برای ایجاد یک کوکی و ارسال آن به سمت کلاینت همانطور که در بالا نیز اشاره شد، باید از پراپرتی Response.Cookies از کلاس HttpContext استفاده کرد. برای ایجاد یک کوکی روشهای مختلفی وجود دارد.
- در روش اول با استفاده از ویژگی مخصوص ایندکسر کلاس HttpCookieCollection عملیات تولید کوکی انجام میشود. در این روش، ابتدا بررسی میشود که کوکی موردنظر در لیست کوکیهای جاری وجود دارد یا خیر. درصورتیکه با این نام قبلا یک کوکی ثبت شده باشد، مقدار کوکی موجود بروزرسانی خواهد شد. اما اگر این نام وجود نداشته باشد یک کوکی جدید با این نام به لیست افزوده شده و مقدار آن ثبت میشود. مثال:
HttpContext.Current.Response.Cookies["myCookie"].Value = "myCookieValue";
- روش بعدی استفاده از متد Add در کلاس HttpCookieCollection است. در این روش ابتدا یک نمونه از کلاس HttpCookie ایجاد شده و سپس این نمونه به لیست کوکیها اضافه میشود. کد زیر چگونگی استفاده از این روش را نشان میدهد:
var myCookie = new HttpCookie("myCookie", "myCookieValue"); HttpContext.Current.Response.Cookies.Add(myCookie);
- روش دیگر استفاده از متد Set کلاس HttpCookieCollection است. تفاوت این متد با متد Add در این است که متد Set ابتدا سعی میکند عملیات update انجام دهد. یعنی عملیات افزودن تنها وقتیکه نام کوکی موردنظر در لیست کوکیها یافته نشود انجام خواهد شد. برای مثال:
HttpContext.Current.Response.Cookies.Set(new HttpCookie("myCookie", "myCookieValue"));
نکته: باتوجه به توضیحات بالا، متد Set اجازه افزودن دو کوکی با یک نام را نمیدهد. برای اینکار باید از متد Add استفاده کرد. درباره این موضوع در قسمت بعدی بیشتر توضیح داده خواهد شد.
- روش دیگری که برای ایجاد یکی کوکی میتوان از آن استفاده کرد، بکارگیری متد AppnedCookie از کلاس HttpResponse است. در این روش نیز ابتدا باید یک نمونه از کلاس HttpCookie تولید شود. این روش همانند استفاده از متد Add از کلاس HttpCookieCollection است. کد زیر مثالی از این روش را نشان میدهد:
HttpContext.Current.Response.AppendCookie(new HttpCookie("myCookie", "myCookieValue"));
- روش بعدی استفاده از متد SetCookie از کلاس HttpResponse است. فرق این متد با متد AppendCookie در این است که در متد SetCookie ابتدا وجود یک کوکی با نام ارائه شده بررسی میشود و درصورت وجود، مقدار این کوکی بروزرسانی میشود. درصورتیکه قبلا یک کوکی با این نام وجود نداشته باشد، یک کوکی جدید به لیست کوکیها اضافه میشود. این روش همانند استفاده از متد Set از کلاس HttpCookieCollection است. نمونهای از نحوه استفاده از این متد در زیر آورده شده است:
HttpContext.Current.Response.SetCookie(new HttpCookie("myCookie", "myCookieValue"));
نکته: تمامی فرایندهای نشان داده شده در بالا تنها موجب تغییر محتویات کالکشن کوکیها درون HttpContext میشود و تا زمانیکه توسط وب سرور با استفاده از دستور Set-Cookie به سمت مرورگر ارسال نشوند تغییری در کلاینت بوجود نخواهند آورد.
برای آشنایی بیشتر با این روند کد زیر را برای تعریف یک کوکی جدید درنظر بگیرید:
HttpContext.Current.Response.Cookies["myCookie"].Value = "myValue";
همانطور که مشاهده میکنید دستور ایجاد یک کوکی با نام و مقدار وارده در هدر پاسخ تولیدی توسط وب سرور گنجانیده شده است.
نکته: در ASP.NET به صورت پیش فرض از مقدار "/" برای پراپرتی Path استفاده میشود.
.
خواص کوکی در ASP.NET
برای تعیین یا تغییر خواص یک کوکی در ASP.NET باید به نمونه HttpCookie مربوطه دست یافت. سپس با استفاده از پراپرتیهای این کلاس میتوان خواص موردنظر را تعیین کرد. برای مثال:
var myCookie = new HttpCookie(string.Empty); myCookie.Name = "myCookie"; myCookie.Value = "myCookieValue"; myCookie.Domain = "dotnettip.info"; myCookie.Path = "/post"; myCookie.Expires = new DateTime(2015, 1, 1); myCookie.Secure = true; myCookie.HttpOnly = true;
نکته مهم: امکان تغییر خواص یک کوکی به صورت مستقیم در سمت سرور وجود ندارد. درواقع برای اعمال این تغییرات در سمت کلاینت باید به ازای هر کوکی موردنظر یک کوکی جدید با مقادیر جدید ایجاد و به کالکشن کوکیها در Http Response مربوطه اضافه شود تا پس از قرار دادن دستور Set-Cookie متناظر در هدر پاسخ ارسالی به سمت کلاینت و اجرای آن توسط مرورگر، مقادیر خواص مورنظر در سمت کلاینت بروزرسانی شوند. دقت کنید که تمامی نکات مرتبط با هویت یک کوکی که در قسمت اول شرح داده شد در اینجا نیز کاملا صادق است.
روش دیگری نیز برای تعیین برخی خواص کوکیها به صورت کلی در فایل وب کانفیگ وجود دارد. برای اینکار از تگ httpCookies در قسمت system.web استفاده میشود. برای مثال:
<httpCookies domain="www.example.com" httpOnlyCookies="true" requireSSL="true" />
این امکان از ASP.NET 2.0 به بعد اضافه شده است. با استفاده از این تگ، تنظیمات اعمال شده برای تمامی کوکیها درنظر گرفته میشود. البته درصورتیکه تنظیم موردنظر برای کوکی به صورت صریح آورده نشده باشد. برای نمونه به کد زیر دقت کنید:
var myCookie = new HttpCookie("myCookie", "myCookieValue"); myCookie.Domain = "test.com"; HttpContext.Current.Response.Cookies.Add(myCookie); var myCookie2 = new HttpCookie("myCookie2", "myCookieValue2"); myCookie2.HttpOnly = false; myCookie2.Secure = false; HttpContext.Current.Response.Cookies.Add(myCookie2);
با استفاده از تنظیمات تگ httpCookies که در بالا نشان داده شده است، هدر پاسخ تولیدی توسط وب سرور به صورت زیر خواهد بود:
همانطور که میبینید تنها مقادیر پراپرتیهایی که صراحتا برای کوکی آورده نشده است از تنظیمات وب کانفیگ خوانده میشود.
.
.
حذف کوکی در ASP.NET
برای حذف یک کوکی در ASP.NET یک روش کلی وجود دارد که در قسمتهای قبلی نیز شرح داده شده است، یعنی تغییر خاصیت Expires کوکی به تاریخی در گذشته. برای نمونه داریم:
var myCookie = new HttpCookie("myCookie", "myCookieValue"); myCookie.Expires = DateTime.Now.AddYears(-1);
نکته مهم: در کلاس HttpCookieCollection یک متد با نام Remove وجود دارد. از این متد برای حذف یک کوکی از لیست موجود در این کلاس استفاده میشود. دقت کنید که حذف یک کوکی از لیست کوکیها با استفاده از این متد تاثیری بر موجودیت آن کوکی در سمت کلاینت نخواهد گذاشت و تنها روش موجود برای حذف یک کوکی در سمت کلاینت همان تنظیم مقدار خاصیت Expires است.
.
خواندن کوکی در ASP.NET
برای خواندن مقدار یک کوکی ارسالی از مرورگر کلاینت در ASP.NET، باتوجه به توضیحات ابتدای این مطلب، طبیعی است که باید از پراپرتی Request.Cookies در نمونه جاری از کلاس HttpContext استفاده کرد. برای این کار نیز چند روش وجود دارد.
- روش اول استفاده از ایندکسر کلاس HttpCookieCollection است. برای اینکار نیاز به نام یا ایندکس کوکی موردنظر در لیست مربوطه داریم. برای مثال:
var myCookie = HttpContext.Current.Request.Cookies["myCookie"];
- یا این نمونه با استفاده از ایندکسر عددی:
var myCookie = HttpContext.Current.Request.Cookies[0];
- روش دیگری که برای خواند مقدار یک کوکی میتوان بکار برد، استفاده از متد Get از کلاس HttpCookieCollection است. این متد همانند ایندکسر این کلاس نیاز به نام یا ایندکس کوکی موردنظر دارد. برای نمونه:
var myCookie = HttpContext.Current.Request.Cookies.Get("myCookie");
- یا استفاده از ایندکس کوکی:
var myCookie = HttpContext.Current.Request.Cookies.Get(0);
.
.
بحث و نتیجه گیری
تا اینجا با مفاهیم اولیه درباره نحوه برخورد ASP.NET با کوکیها آشنا شدیم. روشهای مختلف ایجاد و یا بهروزرسانی کوکیها نشان داده شد. با تعیین انواع خواص کوکیها آشنا شدیم. نحوه حذف یک کوکی در ASP.NET را دیدیم. روشهای خواندن مقادیر کوکیها را نیز مشاهده کردیم.
باز هم تاکید میکنم که تمامی تغییرات اعمالی در سمت سرور تا زمانیکه بهصورت دستورات Set-Cookie در هدر پاسخ وب سرور قرار نگیرند هیچ کاری در سمت کلاینت انجام نمیدهند.
در قسمت بعدی این سری مطالب به مباحث پیشرفتهتری چون SubCookieها در ASP.NET و هویت منحصر به فرد کوکیها در سمت سرور پرداخته میشود.
.
.
منابع
لینکهای هفته دوم دی
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- شیوهنامه فارسیپسند گوگلخوان، نسخه ۰.۱.۱ (کار جالبی است. آشنایی با افزونه استایلیش)
ASP. Net
طراحی و توسعه وب
PHP
اسکیوال سرور
سی شارپ
SharePoint
عمومی دات نت
ویندوز
مسایل اجتماعی و انسانی برنامه نویسی
متفرقه
- آشنایی با لولههای یاهو و همچنین نمونهای دیگر
مفهوم تعریف نوع decimal پیشفرض در SQL Server
فرض کنید از EF پیش از EF Core استفاده میکنید که به صورت پیشفرض، نوع System.Decimal را در مدلهای شما به همان decimal در SQL Server نگاشت میکند. فکر میکنید در این حالت خروجی کوئریهای زیر چه چیزی خواهد بود؟
select '0.4400' as Expected , cast(0.4400 as decimal) as Actual select '1.3200' as Expected, cast(1.3200 as decimal) as Actual select '1.7600' as Expected, cast(1.7600 as decimal) as Actual select '65.0000' as Expected, cast(65.0000 as decimal) as Actual select '99.50' as Expected, cast(99.50 as decimal) as Actual
این خروجی را در تصویر ذیل مشاهده میکنید. در اینجا خصوصا به مورد صفر دقت کنید:
علت اینجا است که از دیدگاه SQL Server، نوع decimal پیشفرض، دقیقا به معنای decimal(18,0) است که به آرگومان اول آن، precision و به آرگومان دوم آن، scale میگویند. یعنی حداکثر چه تعداد رقم دسیمال، پیش از ممیز و چه تعداد عدد دسیمال، پس از ممیز قرار است در این نوع داده ذخیره شوند.
بنابراین باتوجه به اینکه در حالت پیشفرض، مقدار scale و یا همان تعداد ارقام مجاز پس از ممیز، صفر است، عدد ارائه شده، به نزدیکترین عدد صحیح ممکن، گرد خواهد شد.
به همین جهت برای رفع این مشکل، باید دقیقا مشخص کرد که scale نوع دادهای decimal مدنظر چیست. برای مثال میتوان از decimal(10,4) استفاده کرد که در اینجا، نتایج صحیحی را ارائه میدهد:
همچنین به عنوان تمرین، مثال زیر را حل کنید!
select iif(cast(0.1 + 0.2 as decimal) = 0, 'true', 'false')
بنابراین باید بهخاطر داشت، اگر از EF 6x (پیش از EF Core) استفاده میشود، حتما نیاز است مقادیر precision و scale را دقیقا مشخص کنیم؛ وگرنه حالت پیشفرض آن decimal(18,0) است:
modelBuilder.Properties<decimal>().Configure(x => x.HasPrecision(18, 6));
رفتار EF Core با نوع دادهای decimal
رفتار EF Core با نوع دادهای decimal بهبود یافته و حالت پیشفرض آن، بدون هیچگونه تنظیمی، نگاشت به decimal(18,2) است. به علاوه اگر این پیشفرض را هم تغییر ندهیم، در حین اعمال Migration، پیام اخطاری را نمایش میدهد:
No store type was specified for the decimal property 'Price' on entity type 'Product'. This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values in 'OnModelCreating' using 'HasColumnType', specify precision and scale using 'HasPrecision', or configure a value converter using 'HasConversion'.
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { configurationBuilder.Properties<decimal>().HavePrecision(18, 6); }
دو مطلب مرتبط
- از نوعهای دادهای float و یا double در مدلهای EF خود استفاده نکنید.
- همیشه مراقب بزرگ شدن اعداد و مبالغ و جمع نهایی آنها باشید!
که وارد قسمت مدیریتی میشویم طبق تصویر زیر :
در مرحله بعد با مراجعه به ویژوال استودیو پروژه خود را با ویژوال استودیو ایجاد میکنیم و پروژه خود را به Team Foundation Serve مایکروسافت متصل خواهیم کرد . با کلیک بر روی Team از منوی ویژوال استودیو Manage Connections را انتخاب کرده و طبق تصویر زیر اطلاعات در خواستی را وارد میکنیم و بر روی Connect کلیک میکنیم
در مرحله بعد بر روی Solution پروژه راست کلیک کرده و Add Solution to Source Control را انتخاب میکنیم و با مشاهده پنجره Add Solution to Source Control بر روی ok کلیک کرده و با مراجعه به Solution Explorer پروژه مشاهده میکنیم که علامت + سبز رنگ در کنار Itemهای پروژه مشاهد میشود و این نشان دهنده این میباشد که پروژه ما هنوز به Team Foundation Server مایکروسافت منتقل نشده است . و برای انتقال بر روی Solution پروژه راست کلیلک کرده و ..Check In را انتخاب میکنیم و در قسمت Comment میتوانیم توضیحاتی را اضافه کرده و دوباره بر روی Check کلیک کرده و Itemهای پروژه ما منتقل سرور مایکروسافت میشود .
روش دوم :
در این روش ما نیاز به Windows Server 2012 R2 و نرم افزار 2015 Microsoft Team Foundation Server داریم و مراحل را در این روش طبق مقاله زیر جلو میبریم که میتوانید به مراجعه به لینک زیر به مقاله دسترسی داشته باشید .
Endpoint xyz contains authorization metadata, but a middleware was not found that supports authorization. Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code.
محل صحیح تعریف میانافزار Authorization در ASP.NET Core 3.0
توصیه شدهاست میانافزار جدید Authorization، با فراخوانی متد UseAuthorization، بلافاصله پس از فراخوانی متد UseAuthentication معرفی شود. در این حالت این میانافزار، با یک Policy پیشفرض تنظیم میشود که قابل تغییر و بازنویسی است:
public void Configure(IApplicationBuilder app) { ... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); }
در ASP.NET Core 3.0، پس از تنظیمات فوق است که قطعه کد زیر و اعمال فیلتر Authorize، مجددا کار میکند:
public class HomeController : ControllerBase { [Authorize] public IActionResult BuyWidgets() { ... } }
میتوان AuthorizeFilter را به صورت یک فیلتر سراسری (global filter in MVC) تعریف کرد تا به تمام کنترلرها و اکشن متدها اعمال شود. هرچند این روش هنوز هم در ASP.NET Core 3.0 کار میکند، اما روش توصیه شدهی جدید آن به صورت زیر است:
public void Configure(IApplicationBuilder app) { ... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute().RequireAuthorization(); }); }
[AllowAnonymous] public class HomeController : ControllerBase { ... }
سفارشی سازی سیاستهای Authorization در ASP.NET Core 3.0
اگر بخواهیم DefaultPolicy میانافزار Authorization را بازنویسی کنیم، میتوان از متد services.AddAuthorization به صورت زیر استفاده کرد که در آن authentication و داشتن یک Scope خاص را اجباری میکند:
public void ConfigureServices(IServiceCollection services) { ... services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .RequireScope("MyScope") .Build(); }); }
با تنظیم FallbackPolicy، میتوان تمام endpointهایی را که توسط فیلتر [Authorize] و یا ()RequireAuthorization محافظت نشدهاند، محافظت کرد.
DefaultPolicy توسط فیلتر [Authorize] و یا ()RequireAuthorization مورد استفاده قرار میگیرد؛ اما FallbackPolicy زمانی بکار خواهد رفت که هیچ نوع سیاست دسترسی تنظیم نشدهباشد. حالت پیشفرض FallbackPolicy، پذیرش تمام درخواستها بدون اعتبارسنجی است.
کارکرد مثال زیر با مثالهای قبلی که از DefaultPolicy استفاده میکردند، یکی است و هدف آن، اجبار به اعتبارسنجی کاربران در همهجا (تمام endpoints)، منهای مواردی است که از فیلتر AllowAnonymous استفاده میشود:
public void ConfigureServices(IServiceCollection services) { services.AddAuthorization(options => { options.FallbackPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .RequireScope("MyScope") .Build(); }); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } [AllowAnonymous] public class HomeController : ControllerBase { }
امکان اعمال سفارشی میانافزار Authorization به Endpointهای مجزا
برای مثال در مبحث «بررسی سلامت برنامه»، فریمورک، اعتبارسنجی درخواستهای آنرا پیش بینی نکردهاست؛ اما اینبار میتوان اعتبارسنجی را به endpoint آن اعمال کرد:
public void Configure(IApplicationBuilder app) { ... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints .MapHealthChecks("/healthz") .RequireAuthorization(new AuthorizeAttribute(){ Roles = "admin", }); }); }