مطالب
Cookie - قسمت اول

مقدمه 

مدت زیادی است که کوکی‌ها در عرصه وب نقش مهمی ایفا میکنند، اما متاسفانه مفهوم روشن و واضحی از آن و نحوه کار آن در اختیار اکثر کاربران و توسعه دهندگان وب نیست. شاید اولین مشکل ناشی از سوءتفاهم‌های بسیاری باشد که درباره کوکی وجود دارد. مثلا برخی آن را ابزاری صرفا برای جاسوسی از کاربران اینترنتی میدانند. برخی دیگر، از آنها و نحوه کارکردشان کلا صرفنظر میکنند. مشکل دیگری که در رابطه با کوکی‌ها میتوان برشمرد، عدم وجود رابط کاربری مناسب برای بررسی و مدیریت کوکی هاست. اما با وجود این مشکلات و برخی دیگر امروزه کوکی‌ها جزئی بسیار مهم در وب هستند که درصورت حذفشان، بسیاری از وب سایتها و برنامه‌های مبتنی بر وب از کار خواهند افتاد.
یک کوکی (cookie به معنی شیرینی یا کلوچه! که با عناوین دیگری چون Http Cookie و Web Coockie و Browser Cookie نیز شناخته میشود)، به داده‌های ارسالی از یک وب سرور (که معمولا بصورت داده‌های متنی کدگذاری شده هستند) اطلاق میشود که در مخزنی مخصوص در مرورگر کاربر به هنگام بازدید از یک سایت ذخیره میشود. وقتیکه کاربر سایت مذبور را در آینده دوباره مرور کند، این داده‌های ذخیره شده توسط مروگر به وب سرور ارسال میشود تا مثلا فعالیتهای قبلی کاربر مورد بررسی قرار گیرد. کوکی‌ها برای فراهم کردن مکانیزمی قابل اعتماد جهت ذخیره فعالیتهای قبلی یا آخرین وضعیت کاربر در یک وب‌سایت طراحی شده‌اند. با اینکه کوکی‌ها دسترسی بسیار محدودی در سمت کلاینت دارند (تقریبا هیچ دسترسی‌ای به هیچیک از منابع سیستم کاربر ندارند) اما با پیگیری هوشمند و هدفمند برخی از آنها میتوان به داده‌هایی از تاریخچه فعالیتهای کاربر در یک مرورگر و سایت خاص دست یافت که به نوعی نقض حریم شخصی کاربران به حساب می‌آید.

نکته: درواقع میتوان گفت که از کوکی به نوعی برای فراهم کردن "حافظه" موقت برای مرورگرها در ارتباط با وب سرورها استفاده میشود.

پروتوکل HTTP که برای تبادل داده‌ها میان مرورگر و وب سرور در بارگذاری صفحات وب استفاده میشود، پروتوکلی بدون حالت یا وضعیت (state-less) است. بدین معنی که به محض ارسال داده‌های یک صفحه وب به سمت مرورگری که آنرا درخواست کرده، وب سرور هیچ چیزی از این تبادل داده را ذخیره و نگهداری نمیکند. بنابراین در درخواست‌های دوباره و سه باره و ... بعدی، وب سرور با آنها همچون اولین درخواست برخورد کرده و رفتاری کاملا یکسان در برخورد با این درخواستها نشان خواهد و دقیقا همان داده‌ها را به سمت مرورگر ارسال خواهد کرد.
این رفتار در موارد زیادی میتواند دردسرساز باشد. مثلا وب سرور نمیتواند بفهمد که یک کاربر لاگ‌آن (LogOn یا همان SignIn) کرده و یا اینکه یکسری تنظیمات شخصی اعمال کرده است، چون جایی برای ذخیره  و نگهداری این حالات یا وضعیتها در پروتوکل HTTP وجود ندارد. خوشبختانه وجود کوکی‌ها یکی از بهترین راه حل‌ها برای رفع مشکلات اشاره شده است.

بنابراین همانطور که اشاره شده یکی از مهمترین انواع کاربردهای کوکی‌ها در زمینه اعتبار سنجی کاربران است. با استفاده از این نوع کوکی وب سایتها میتوانند از وضعیت ورود یا خروج کاربران و نیز انواع دسترسی‌ها و تنظیمات آنها باخبر شوند. البته با توجه به حساسیت این موضوع، درباره نحوه ذخیره داده‌ها در این نوع کوکی‌ها باید دقت خاصی اعمال شود. اگر در این زمینه سهل انگاری هایی انجام شود، ممکن است خوراک جذابی برای هکرها فراهم شود! تبلیغات درون سایتها نیز از قسمتهایی است که استفاده بسیاری از کوکی میکند که بعضا موجب بروز خطراتی برای کاربران میشود.

تاریخچه
واژه Cookie از عبارت Magic Cookie برگرفته شده است. به طور خلاصه Magic Cookie به مجموعه‌ای از داده‌های «بدون نیاز به تغییر» میگویند که بین برنامه‌هایی که در ارتباط با یکدیگرند، ردوبدل میشود. داده‌های موجود در Magic Cookieها معمولا برای سمت دریافت کننده مفهوم خاصی ندارد و به نوعی برای ذخیره وضعیت «سمت دریافت کننده» در «برنامه ارسال کننده» و استفاده از آن در ارتباطهای بعدی کاربرد دارد. به بیان دیگر حالت یا وضعیت یا تنظیمات «برنامه مقصد در برنامه مبدأ» با استفاده از کوکی در «خود برنامه مقصد» نگهداری میشود!
در سال 1994 آقای Lou Montulli هنگامیکه در شرکت Netscape Communications در توسعه یک برنامه تجاری تحت وب مشارکت داشت، ایده استفاده از این تکنولوژی را در ارتباطات وب ارائه داد که بعدها عنوان HTTP Coockie را بخود گرفت. برای اولین بار از این کوکی‌ها در نسخه 0.9 بتای نت اسکیپ که در همان سال ارائه شد، پشتیبانی و استفاده شد. مروگر IE هم در سال 1995 و در نسخه 2.0 آن، پشتیبانی از کوکی را آغاز کرد. آقای مانتولی پتنت (Patent) تکنولوژی کوکی را در سال 1995 ارائه داد اما ثبت نهایی آن به دلیل مشکلات و مباحث حریم شخصی کاربران تا سال 1998 طول کشید.

کوکی واقعا چیست؟
یک کوکی در واقع یک فایل متنی کوچک است که در قسمتی مشخص از کامپیوتر کلاینت که توسط مرورگر تنظیم شده است، ذخیره میشود. این فایل متنی کوچک حاوی اطلاعات زیر است:
- یک جفت داده نام-مقدار (name-value pair) که داده اصلی کوکی را نگهداری میکند.
- خاصیتی برای مشخص کردن زمان انقضای کوکی (پس از این زمان این فایل متنی کوچک از درون مرورگر حذف خواهد شد)
- خاصیتهایی برای مشخص کردن محدوده‌ها و مسیرهای قابل دسترسی کوکی
- خاصیتهایی برای تعیین نحوه تبادل داده‌های کوکی و نوع دسترسی به این داده‌ها (مثلا الزام به استفاده از پروتوکلهای امن)
 
انواع کوکی
بطور کلی دو نوع اصلی کوکی وجود دارد:
1. Session cookie
از این نوع کوکی برای نگهداری موقت داده‌ها نظیر داده‌های مربوط به وضعیت یک کاربر، تنها در زمان مرور وب سایت استفاده میشود. معمولا با بستن مرورگر (یا اتمام سشن) این کوکی‌ها ازبین میروند.
2. Persistent cookie 
برخلاف کوکی‌های سشنی این نوع کوکی‌ها در سیستم کلاینت به صورت دائمی ذخیره میشوند. معمولا دارای یک تاریخ انقضا هستند که پس از آن از بین میروند. در طول زمان حیات این کوکی ها، مرورگرها داده‌های ذخیره شده در آنها را با توجه به تنظیمات درونشان در هر درخواست به سمت وب سرور سایت مربوطه ارسال میکنند.

با توجه به کاربردهای فراوان کوکی، دسته بندیها و انواع دیگری از کوکی را هم میتوان نام برد. مانند انواع زیر:
Secure cookie 
معمولا به کوکی‌هایی که خاصیت امن (Secure Attribute) در آنها فعال است این عنوان اطلاق میشود. این نوع از کوکی‌ها تنها قابل استفاده در ارتباطهای امن (با استفاده از پروتوکل HTTPS) هستند. این خاصیت اطمینان میدهد که داده‌های موجود هنگام تبادل بین سرور و کلاینت همواره کدگذاری میشود.
HttpOnly cookie
در این کوکی‌ها خاصیت HttpOnly فعال است، که موجب میشود که از آنها تنها در ارتباطات از نوع HTTP و HTTPS بتوان استفاده کرد. در سایر روشهای دسترسی به کوکی (مثلا از طریق برنامه نویسی سمت کلاینت) نمیتوان به محتوای این نوع از کوکی‌ها دسترسی پیدا کرد.
Third-party cookie 
این نوع از کوکی‌ها در مقابل کوکی‌های First party (یا شخص اول) وجود دارند. کوکی‌های شخص اول توسط وب سایت جاری تولید شده اند، یعنی نشانی دامِین این کوکیها مربوط به سایت جاری است. منظور از سایت یا دامین جاری، سایتی است که آدرس آن در نوار آدرس مرورگر نشان داده میشود. کوکی‌های Third party (یا شخص سوم) به آن دسته از کوکی‌ها میگویند که توسط دامین یا وب سایت دیگری غیر از وب سایت جاری تولید و مدیریت میشوند. مثلا کوکی‌های مربوط به سایتهای تبلیغاتی. البته در مرورگرهای مدرن این نوع از کوکی‌ها به دلیل مشکلات امنیتی و نقض حریم شخصی کاربران عموما بلاک میشوند.
Supercookie 
یک سوپرکوکی به آن دسته از کوکی‌ها گفته میشود که خاصیت دامین آنها به یک پسوند خیلی کلی مثل com. تنظیم شده باشد. به دلیل مسائل امنیتی بیشتر مرورگرهای مدرن تمامی انواع این سوپرکوکی‌ها را بلاک میکنند. امروزه لیستی از این پسوندهای کلی با عنوان Public Suffix List موجود است که در مرورگرهای مدرن برای کنترل کوکی‌ها استفاده میشود.
 
موارد استفاده از کوکی
- مدیریت جلسات (Session Management):
از کوکی میتوان برای نگهداری داده‌های مربوط به یک کاربر در بازدید از صفحات سایت و تعامل با آنها (که ممکن است در زمانهای مختلف رخ دهد) استفاده کرد. یکی از موارد بسیار پرکاربرد در این زمینه، کوکی‌های تعیین اعتبار یک کاربر است که پس از ورود به سایت در هر درخواست توسط مرورگر به سمت سرور ارسال میشود. 
مثال دیگری در مورد این کاربرد نگهداری از داده‌های سبد خرید یک کاربر است. این داده‌ها را میتوان قبل از تسویه نهایی درون یک کوکی ذخیره کرد. معمولا در تمام این موارد از یک کلید منحصر به فرد که در سمت سرور تولید شده و درون کوکی به همراه سایر اطلاعات ذخیره میشود، برای تعیین هویت کاربر استفاده میشود. 
- شخصی سازی (Personalization):
یکی دیگر از موارد پرکاربرد کوکیها ذخیره تنظیمات یا داده‌های مرتبط با شخصی سازی تعامل کاربر با سایت است. مثلا میتوان تنظیمات مربوط به استایل یک سایت یا زبان انتخابی برای یک کاربر مشخص را درون کوکی در سمت کلاینت ذخیره کرد. سایتهای بزرگ معمولا از این روش برای ذخیره تنظیمات استفاده میکنند، مثل گوگل و ویکیپدیا. همچنین میتوان از کوکی برای ذخیره شناسه آخرینکاربری که در سایت لاگ‌آن کرده استفاده کرد تا در مراجعه بعدی به عنوان اولین انتخاب در صفحه ورود به سایت به کاربر نمایش داد (هرچند این کار معایب خودش را دارد).
- پیگیری یا ردیابی (Tracking):
از کوکی‌ها میتوان برای پیگیری بازدیدهای یک کاربر در یک کلاینت از یک سایت یا مسیری به خصوص از یک سایت بهره برد. بدین صورت که برای هر کاربر یک کد شناسایی منحصر به فرد تولید شده و درون کوکی مخصوص این کار ذخیره میشود. سپس برای هردرخواست میتوان مثلا نشانی صفحه موردنظر و زمان و تاریخ آن را درون یک منبع ذخیره کرد تا برای استفاده‌های آتی به کار روند.
البته کاربردها و استفاده‌های دیگری نیز برای کوکی میتوان برشمرد که بدلیل طولانی شدن بحث از آنها صرفنظر میشود.
 
ایجاد کوکی
همانطور که در بالا نیز اشاره شد، کوکی‌ها داده هایی هستند که توسط وب سرور برای ذخیره در کلاینت (مرورگر) تولید میشوند. مرورگرها نیز باید این داده‌ها را بدون هیچ تغییری در هر درخواست عینا به سمت سرور برگردانند. درواقع با استفاده از کوکی‌ها میتوان به ارتباط بدون حالت HTTP به نوعی خاصیتی از جنس state اضافه کرد. به غیر از خود وب سرور، برای تنظیم و یا تولید کوکی میتوان از زبان‌های برنامه نویسی سمت کلاینت (مثل جاوا اسکریپت) البته درصورت پشتیبانی توسط مرورگر نیز استفاده کرد.
در جدیدترین استانداردهای موجود (RFC 6265) درباره کوکی آورده شده که مرورگرها باید بتوانند حداقلهای زیر را پشتیبانی کنند:
- توانایی ذخیره حداقل 3000 کوکی
- توانایی ذخیره کوکیها با حجم حداقل 4 کیلوبایت
- توانایی ذخیره و نگهداری حداقل 50 کوکی به ازای هر سایت یا دامین
 
نکته: توانایی مرورگرهای مدرن در مدیریت کوکی‌ها ممکن است فراتر از استانداردهای اشاره شده در بالا باشد.
 
انتقال داده‌های صفحات وب سایتها از طریق پروتوکل HTTP انجام میشود. مرورگر برای بارگذاری صفحه موردنظر کاربر از یک وب سایت، معمولا یک متن کوتاه به وب سرور مربوطه ارسال میکند که به آن HTTP Request میگویند. مثلا برای دریافت صفحه https://www.dntips.ir/index.html درخواستی به شکل زیر به سمت وب سرور ارسال میشود:
GET /index.html HTTP/1.1
Host: www.dotnettips.info 
مثلا نمونه یک درخواست کامل خام (Raw) از صفحه اول سایت جاری در نرم افزار Fiddler بصورت زیر است:
GET https://www.dntips.ir/ HTTP/1.1
Host: www.dotnettips.info
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: BlogPost-1175=NLOpR%2fgHcUGqPL8dZYv3BDDqgd4xOtiiNxHIp1rD%2bAQ%3d; BlogComment-5002=WlS1iaIsiBnQN1UDD4p%2fHFvuoxC3b8ckbw78mAWXZOSWMpxPlLo65%2bA40%2flFVR54; ReaderEmail=DP%2bx4TEtMT2LyhNQ5QqsArka%2fWALP5LYX8Y
وب سرور با ارسال محتویات صفحه موردنظر به این درخواست پاسخ میدهد که به آن HTTP Response میگویند.
در پاسخ ارسالی، وب سرور میتواند با استفاده از یک header مخصوص با نام Set-Cookie یک کوکی را ایجاد کند. در زیر یک نمونه از این پاسخها را مشاهده میکنید:
 HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: cookieName=cookieValue
Set-Cookie: 
cookieName2=cookieValue2; Expires=Thr, 10-Jun-2021 10:18:14 GMT
...
نمونه پاسخ ارسالی خام (Raw) در نرم افزار Fiddler مربوط به درخواست صفحه اول سایت جاری بصورت زیر است:
HTTP/1.1 200 OK
Date: Wed, 30 Jan 2013 20:25:15 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-Compressed-By: HttpCompress
Set-Cookie: .ASPXROLES=NzZ9qIRpCWHofryYglbsQFv_SSGPn7ivo0zKFoS94gcODVdIKQAe_IBwuc-TQ-03jGeIkZabTuxA0A3k2-nChy7iAWw9rPMYXSkqzMkizRFkDC0k3gQTkdLqLmmeIfnL9UjfMNWO8iVkYQrSv24ecbpFDSQCH827V2kEj8k2oCm_5sKRSmFpifh4N7kinEi0vomG1vW4Rbg9JWMhCgcvndvsFsXxpj-NiEikC1RqHpiLArIyalEMEN-cIuVtRe7uoo938u9l-7OXb8yzXucVl4bdqPy2DXM3ddWzb3OH1jSFM6gxwJ8qRZDlSGmEEbhji7rA-efI4aYGTKx6heWfUsY6E2k73jJLbuZ3RB4oNwRYmz8FRB0-vm1pO7rhF1JIoi1YB17ez-Ox5chNEFkPVREanHVU9DxboJ5dKgN-2B5udUFPunnshbN8EBhixbFQOpqRiiOK4uWWaWy3rVEJYpCCDBRctKCfEyYD1URFYeajB0AXmiMUTcGeuUtwb-XFjbQZnbylmMF3EJgG16bcc1IEkTAUv1JfKjaql0XGWJI1; path=/; HttpOnly
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 106727

<!DOCTYPE html>
<html>
...
وب سرور دستور Set-Cookie را تنها برای ثبت کوکی در مرورگر در پاسخ ارسالی قرار میدهد. برای آشنایی بیشتر با این هدر و ساختار آن به RFC 6265 مراجعه کنید. این دستور برای مرورگر مشخص میکند که (درصورت پشتیبانی از کوکی و فعال بودن آن در مروگر) در درخواستهای بعدی باید این کوکی‌ها را در متن درخواست ارسالی به سمت سرور ضمیمه کند. البته اینکار با توجه به تنظیمات خاصیتهای کوکی مربوطه  انجام میشود که در ادامه بحث میشود. برای ارسال کوکی به سمت وب سرور، مرورگر از هدر Cookie در درخواست خود استفاده میکند.
مثلا با توجه به مثال قبل برای درخواست صفحه https://www.dntips.ir/index2.html مرورگر میتواند متن زیر را به سمت وب سرور ارسال میکند:
 GET /index2.html HTTP/1.1
Host: www.example.org
Cookie: cookieName=cookieValue; cookieName2=cookieValue2
Accept: */*
این درخواست که برای صفحه دیگری از همان سایت قبلی است، با درخواست اول متفاوت است. با استفاده از این درخواست وب سرور میفهمد که این درخواست با درخواست قبلی مرتبط است. وب سرور در پاسخ ارسالی میتواند کوکی‌های دیگری نیز ثبت کند.
وب سرور میتواند مقدار یک کوکی ثبت شده در مروگر را با استفاده از دستوری مشابه Set-Cookie: cookieName=cookieNewValue در پاسخ ارسالی به سمت کلاینت تغییر دهد. مرورگر با دیدن این خط در پاسخ دریافتی از وب سرور مقدار کوکی را به روز رسانی میکند. البته به شرطی که سایر خواص تنظیم شده برای کوکی عینا یکسان باشد.
 
نکته: identity یا هویت کوکی با استفاده از تمام خواص آن به جز expires یا max-age تعیین میشود.
 
مقدار یک کوکی میتواند شامل هر کاراکتر قابل چاپ اَسکی (از کاراکتر ! تا کاراکتر ~ یا از کد یونیکد u0021\ تا u007E\) به غیر از کاراکترهای , و ; و فضای خالی باشد (استفاده از این سه کاراکتر و سایر کاراکترهای غیر اسکی تنها با انکدینگ میسر است). نام یک کوکی نیز از این قاعده پیروی میکند، البته شامل کاراکتر = نیز نمیتواند باشد چون این کاراکتر نقش جداکننده «مقدار» از «نام» کوکی را ایفا میکند. استانداردهای کوکی که در RFC 6265 آورده شده است، محدودیتهای بیشتری نیز دارد که ممکن است توسط مروگرهای امروزی رعایت نشود!
 
نکته: انکدینگ کوکیها کمی بحث دارد. ازآنجاکه کوکی‌ها به صورت هدرهای پروتوکل HTTP انتقال داده میشوند، بنابراین کاراکترهای موجود در آن باید تنها از نوع ASCII باشند. اما چون ارسال کننده و دریافت کننده نهایی کوکی یک وب سرور یکسان است بنابراین وب سرور برای ذخیره کاراکترهای غیر اسکی میتواند از انکدینگ خاص خود استفاده کند. مثلا عموم وب سرورها  و نیز مرورگرها از URL Encoding برای انکدینگ کوکی‌ها استفاده میکنند (^). ظاهرا در تمام مرورگرهای مدرن برای ذخیره کوکی ها، حداقل نام و مقدار کوکی به صورت جداگانه (مثلا برای ذخیره کاراکترهای نامعتبر) انکد میشود به غیر از کاراکتر تساوی (=) بین نام و مقدار کوکی.
 
با استفاده از زبانهای برنامه نویسی سمت کلاینت نیز میتوان کوکی‌ها را مدیریت کرد. مثلا در جاوا اسکریپت از document.cookie برای اینکار استفاده میشود. نحوه کاربرد و استفاده از این پراپرتی کمی غیرعادی است. مثلا دستور 'document.cookie = 'dummy=a11a یک کوکی با نام dummy و با مقدار a11a ایجاد میکند! در ادامه با این دستور و نحوه کارکردن با کوکی در جاوا اسکریپت بیشتر آشنا میشویم.
 
نکته: برای انکد رشته‌ها در جاوا اسکریپت از دستور escape استقاده میشود. عملیات عکس آن با دستور unescape انجام میشود.
 
نکته: با اینکه استاندارد تعریف کوکی مشخص کرده که برای تعریف کوکی وجود عبارتی به صورت name=value اجباری است، اما ظاهرا بیشتر مرورگرها صحت تعریف کوکی و  اعتبار آنرا برای پیروی از این طرح بررسی نمیکنند و بنابراین میتوان صرفا با استفاده از یک رشته بدون علامت مساوی یک کوکی را ایجاد کرد.
 
خواص یک کوکی
به غیر از نام و مقدار، کوکی‌ها خواص دیگری همچون دامین (domain)، مسیر (path)، تاریخ انقضا (expiration date) یا حداکثر طول عمر (maximum age)، و Secure و HttpOnly دارند که میتوانند توسط وب سرور و یا با استفاده از زبانهای برنامه نویسی کلاینتی تنظیم شوند. مرورگرها این خاصیتها را به وب سرور ارسال نمیکنند. تنها مقادیری که به سمت وب سرور برگشت داده میشوند، نام و مقدار کوکی‌هاست. مرورگرها با استفاده از خواص کوکی زمان حذف کوکی و یا کنترل دسترسی به مقدار آن با توجه به آدرس جاری مرورگر و نیز اینکه آیا اصلا کوکی را به وب سرور ارسال کنند یا نه، را تعیین میکنند. خواص کوکی در ادامه شرح داده شده است:
 
1. تاریخ انقضا و حداکثر طول عمر (Expires و Max-Age)
با استفاده از یکی از این دو خاصیت، تاریخی که دیگر نیازی نیست تا کوکی به سمت سرور ارسال شود، تعیین میشود. بنابراین پس از این تاریخ، ممکن است کوکی از مخزن مرورگر پاک شود. برپایه اطلاعات موجود در RFC 6265، در خاصیت Expires، تاریخ انقضای کوکی باید به فرمت “Wdy, DD Mon YYYY HH:MM:SS GMT” باشد. مثل Mon, 17-Mar-2014 1:00:00 GMT. همچنین خاصیت Max-Age طول عمر کوکی را برحسب ثانیه از لحظه دریافت توسط مرورگر مشخص میکند. به نمونه‌های زیر توجه کنید:
Set-Cookie: cookie1=abc; Expires=Mon, 17-Mar-2014 01:00:00 GMT; ...
Set-Cookie: cookie2=123; ...
Set-Cookie: cookie3=abc; Expires=Thu, 01-Jan-1970 00:00:01 GMT; ...
Set-Cookie: cookie3=abc; max-age=31536000; ... 
 ...... 
در دستور اول، cookie1 برای حذف در تاریخ مشخص شده تنظیم شده است. در خط دوم که بدون این دو خاصیت است، یک نوع کوکی سشنی تعریف شده است. این کوکی پس از بسته شدن مرورگر (اتمام سشن) از حافظه پاک خواهد شد.

نکته: اتمام یک سشن برای کوکی‌های سشنی دقیقا به معنی بستن مرورگر (یا تب مربوطه در مروگرهای مدرن) است.

دستور سوم که تاریخ انقضای کوکی را به تاریخی در گذشته تنظیم کرده است به مرورگر اعلام میکند که باید cookie3 را پاک کند. این روش استاندارد برای حذف یک کوکی است.

نکته: استفاده از روش تنظیم یک تاریخ انقضا در گذشته برای حذف یک کوکی تنها وقتی کار خواهد که سایر خواص تعیین شده در دستور Set-Cookie با مقادیر موجود در حافظه مرورگر دقیقا یکی باشد تا هویت کوکی موردنظر به صورت منحصربه فرد تعیین شود. 

در خط چهارم به مرورگر اعلام میشود که cookie4 باید دقیقا یک سال پس از لحظه دریافت کوکی، حذف شود. 

نکته: خاصیت max-age در مرورگر IE8 و نسخه‌های قبل از آن پشتیبانی نمیشود. 

نکته: گزینه ای که معمولا در صفحات لاگ‌آن (LogOn) یا ساین‌این (SignIn) برای ذخیره داده‌های کاربر وجود دارد (مثل «مرا به خاطر بسپار»)، مرتبط با این خاصیت از کوکی هاست. در صورت عدم انتخاب این گزینه معمولا یک کوکی سشنی (بدون خاصیت expires) ایجاد میشود. اما با انتخاب این گزینه، یک کوکی ماندگار (Persistent) با خاصیت expires برابر با تاریخی در آینده ایجاد میشود تا درصورت بسته شدن مرورگر (اتمام سشن) داده‌های کاربر پاک نشود. 
 
نکته: تاریخ انقضای کوکی با استفاده از تاریخ کلاینت تعیین میشود. متاسفانه هیچ راه مستقیمی برای همزمانی این تاریخ با تاریخ سرور وجود ندارد. 
 
2. دامین و مسیر (Domain و Path)
خاصیتهای دامین و مسیر کوکی، محدوده قابل دسترس کوکی را مشخص میکنند. با استفاده از این دو خاصیت مرورگر متوجه میشود که آیا کوکی را در موقعیت و آدرس جاری باید به سمت وب سرور ارسال کند یا خیر. همچنین دسترسی به کوکی‌ها در سمت کلاینت با توجه به این دو خاصیت محدود میشود. مقدار پیش فرض این دو خاصیت برابر مسیر و دامین جاری مرورگر است که اگر مقداری برای این دو خاصیت تعیین نشود، به کوکی تعلق میگیرد.
 
نکته: منظور از وضعیت جاری، موقعیتی است که کوکی مذبور در آن ایجاد شده است. مثلا آدرس صفحه ای که هدر Set-Cookie را ارسال کرده و یا آدرس صفحه ای که در آن با استفاده از دستوری مشابه ;'document.cookie = 'a=b کوکی مربوطه ایجاد شده است. به عنوان نمونه اگر یک کوکی در صفحه جاری همین سایت ایجاد شود و خاصیتهای دامین و مسیر آن مقداردهی نشود، مقدار دامین به www.dotnettips.info و مقدار مسیر آن به post/ تنظیم خواهد شد.

نکته: مرورگر بررسی دامین کوکی را ازطریق «مقایسه از انتها» انجام میدهد. یعنی اگر مثلا دامین یک کوکی برابر dotnettips.info باشد، این کوکی در ساب دامین‌های d1.dotettips.info و یا www.dotnettips.info و از این قبیل در دسترس خواهد بود. برای کسب اطلاعات بیشتر میتوان به RFC 6265 (قسمت Domain Matching) مراجعه کرد.
 
نکته: بررسی مسیر کوکی برخلاف دامین آن، ازطریق «مقایسه از ابتدا» انجام میشود. یعنی آدرس صفحه جاری پس از مقدار دامین سایت باید با مقدار مشخص شده در خاصیت مسیر شروع شود. مثلا مسیر یک کوکی برابر post/ و دامین آن نیز برابر www.dotnettips.info باشد، این کوکی در آدرسهایی چون www.dotnettips.info/post/1286 و یا www.dotnettips.info/post/1 و یا www.dotnettips.info/post/test/test2 و ... در دسترس خواهد بود. برای کسب اطلاعات بیشتر میتوان به RFC 6265 (قسمت Paths and Path-Match) مراجعه کرد. 

برای روشنتر شدن مطلب به هدرهای Set-Cookie زیر توجه کنید:
Set-Cookie: MyCookie1=hi; Domain=d1.d2.com; Path=/employee ...
Set-Cookie: MyCookie2=bye; Domain=.d3.com; Path=/ ...
Set-Cookie: MyCookie3=nth ... 
 ...... 
اولین دستور به مرورگر میگوید تا یک کوکی با نام MyCookie1 و مقدار hi را با دامین d1.d2.com و مسیر employee/ ثبت کند. بنابراین مرورگر از این کوکی تنها درصورتیکه آدرس درخواست موردنظر شامل d1.d2.com/employee باشد، استفاده میکند.
دستور دوم به مرورگر میگوید تا از کوکی MyCookie2 در مسیرهای شامل d3.com. استفاده کند.
در دستور سوم دامین و مسیر با توجه به آدرس صفحه جاری تنظیم میشود. در واقع تنها مسیرهایی که شامل آدرس صفحه جاری باشند به این کوکی دسترسی دارند.

نکته: مقدار domain تنها میتواند مربوط به دامین اصلی جاری و یا زیرمجموعه‌های آن باشد. یعنی نمیتوان یک کوکی با دامین www.d1.com در صفحه‌ای با آدرس www.d2.com ایجاد کرد.

نکته: همچنین کوکی هایی که مثلا دارای دامین www.dotnettips.info هستند از آدرسی نظیر my.dotnettips.info در دسترس نیستند. کوکی‌ها تنها در دامین و ساب دامین‌های مربوط به خود قابل خواندن هستند.

نکته: اگر مقدار خاصیت domain کوکی به چیزی شبیه dotnettip.info تنظیم شود آنگاه این کوکی در آدرسهایی چون www.dotnettips.info و یا d1.dotnettips.info نیز در دسترس است.

نکته: اگر برای خاصیت path مقدار / تنظیم شود، بدین معنی است که کوکی در تمام محدوده دامین کوکی در دسترس است. 

3. Secure و HttpOnly
این دوخاصیت برخلاف خواص قبلی مقداری را تنظیم نمیکنند! بلکه همانند یک flag عمل کرده که هر کدام رفتار خاصی را برای مرورگر الزام میکنند.
خاصیت Secure مرورگر را مجبور به استفاده از ارتباطات امن و کدگذاری شده (Https) برای تبادل داده‌های کوکی میکند. درضمن طبیعی است که وب سرور دستور ثبت چنین کوکی‌هایی را خود از طریق یک ارتباط امن به مرورگر ارسال کند تا مبادا طی یک فرایند خرابکارانه داده‌های مهم درون کوکی در بین راه دزدیده نشود. 

نکته: یک کوکی Secure تنها درصورتی به سمت سرور ارسال میشود که درخواست مذکور با استفاده از SSL و ازطریق پروتوکل HTTPS ایجاد شده باشد.

خاصیت HttpOnly به مرورگر اعلام میکند که استفاده از این کوکی تنها در ارتباطات از نوع پروتوکل HTTP مجاز است. بنابراین سایر روشهای دسترسی موجود (مثل document.cookie در جاوا اسکریپت) برای این نوع کوکی‌ها کار نخواهد کرد. درواقع نحوه برخورد با این نوع کوکی‌ها در سمت سرور با سایر انواع کوکی تفاوتی ندارد و تنها در سمت کلاینت و در مرورگر است که رفتاری متفاوت متوجه این کوکی‌ها میشود و اجازه دسترسی به برنامه‌های سمت کلاینت داده نمیشود.

نکته: این خاصیت ابتدا توسط مایکروسافت در نسخه IE 6 SP1 معرفی شد و بعدها بتدریج توسط سایر مرورگرها نیز پشتیبانی شد. این ویژگی همانطور که از آن برمی‌آید برای مقابله با حملات XSS پیاده سازی شده است. البته علاوه برای جلوگیری از دسترسی به این کوکی‌ها از طریق document.cookie، در مرورگرهای مدرن از دسترسی به هدر این کوکی‌ها ازطریق متدهای شی XMLHttpRequest نیز جلوگیری میشود.

نکته: امکان تنظیم این خاصیت از طریق document.cookie در جاوا اسکریپت وجود ندارد!
 
مدیریت کوکی‌ها در مرورگر
همانطور که قبلا اشاره شد هویت یک کوکی با استفاده تمامی خواص آن به جز expires یا max-age مشخص میشود. یعنی ترکیب name-domain-path-secure/httponly هویت یک کوکی را منحصربه‌فرد میکند. بنابراین تا زمانیکه حتی یکی از این خواص دو کوکی با هم فرق داشته باشد این دو کوکی از هم متمایز خواهند بود. دو کوکی زیر را درنظر بگیرید:
Set-Cookie: cookie1=value1
Set-Cookie: cookie1=value2; domain=dotnettips.info;
 با اینکه به نظر میرسد که این دو کوکی یکسان هستند و اجرای دستور دوم موجب بازنویسی کوکی اول میشود، اما بررسی یک درخواست ارسالی از این صفحه نشان میدهد که دو کوکی مجزا با نام مشابه به سمت سرور ارسال میشود:
Cookie: cookie1=value1; cookie1=value2 
حال اگر کوکی سومی به صورت زیر تعریف شود:
Set-Cookie: cookie1=value3; domain=dotnettips.info; path=/post
وضعیت از این نیز پیچیده‌تر میشود:
Cookie: cookie1=value1; cookie1=value2; cookie1=value3
بنابراین اجرای دستور زیر در همان صفحه:
Set-Cookie: cookie1=value4
تنها مقدار کوکی اول را تغییر خواهد داد. یعنی در درخواست ارسالی به سمت سرور خواهیم داشت:
Cookie: cookie1=value4; cookie1=value2; cookie1=value3
بنابراین دقت مضاعف به این نکته که «هویت یک کوکی با استفاده از تمامی خواص آن به جز expires یا max-age تعیین میشود» مهم است. برای قسمت «به جز expire یا max-age» هم به مثال زیر توجه کنید:
Set-Cookie: cookie2=value1; max-age=1000
بنابراین خواهیم داشت:
Cookie: cookie1=value1; cookie1=value2; cookie1=value4; cookie2=value1
یک کوکی با طول عمر 1000 ثانیه تولید میکند. بنابراین با دستور زیر میتوان مقدار همین کوکی را تغییر داد:
Set-Cookie: cookie2=value2
پس داریم:
Cookie: cookie1=value4; cookie1=value2; cookie1=value3; cookie2=value2
هرچند در دستور آخر به نظر میرسد که کوکی آخر به نوع سشنی تغییر یافته است (چون خاصیت expires یا max-age ندارد) اما درواقع این چنین نیست. تنها اتفاقی که رخ داده است این است که مقدار کوکی مذبور تغییر یافته است، درحالیکه تغییری در خاصیت expires یا max-age آن رخ نداده است.

نکته: با تغییر خواص یک کوکی، میتوان آنرا از نوع سشنی به نوع ماندگار (Persistent) تغییر داد، اما عکس این عملیات ممکن نیست.

SubCookie
بدلیل محدودیت موجود در تعداد کوکی‌ها به ازای هر دامین، روشی برای نگهداری تعداد بیشتری تنظیمات درون همین تعداد محدود کوکی‌ها توسط توسعه گران ابداع شده است. در این روش از طرح ساده ای که نمونه ای از آن در زیر نشان داده شده است برای نگهداری داده‌های چندین کوکی درون یک کوکی استفاده میشود:
Set-Cookie: cookieName=cookie1=value1&cookie2=value2&cookie3=value3&cookie4=value4; path=/
در نمونه بالا با اینکه عملا تنها یک کوکی تعریف شده است اما درواقع داده‌های 4 کوکی مختلف درون یک کوکی آورده شده است. تنها عیب این روش این است که زحمت بیشتری برای استخراج داده‌های کوکی‌ها باید کشید. البته امروزه برخی از فریمورکها امکاناتی جهت کار با این کوکی‌ها فراهم کرده اند.
 
Cookie2
در ابتدای هزاه سوم! مدتی بحثی مطرح شد برای بهبود کارایی و امنیت کوکی‌ها و پیشنهادهایی مبنی بر پیاده سازی نوع جدیدی از کوکی‌ها با عنوان Cookie2 نیز ارائه شد. حتی در نسخه جدیدی از استانداردهای HTTP State Management Mechanism که در RFC 2965 آورده شده است کلا به این نوع جدید از کوکی‌ها پرداخته شده است. هرچند برخی از مرورگرها پشتیبانی از این نوع جدید را آغاز کردند (مثل Opera) اما بعدها به دلیل عدم استقبال از آن، این نوع از کوکی‌ها منسوخ شد و حالت آن در نسخه‌های جدید استانداردها به Obsolete تغییر یافت (RFC 6265)! درهرصورت برای آشنایی با این نوع کوکی‌ها میتوان به مراجع زیر رجوع کرد:

 
منابع:
بازخوردهای دوره
طراحی روابط و ارجاعات در RavenDB
با سلام.
یک سوال در خصوص طراحی روابط  یک بلاگ دارم:
فرض کنید میخواهیم بخش تایم لاین را به برنامه اضافه کنیم بدین شرح: هر کاربری بتواند مشترک بلاگ‌های مورد علاقه اش شود و هر بار که به صفحه اول برنامه مراجعه میکند جدیدترین پست‌های بلاگ هایی که مشترک آنها بوده ببیند.
شاید یک نوع طراحی اینگونه باشد که جدولی داشته باشیم به نام «اشتراک» که در آن فیلد «نام کاربری نویسنده و نام کاربری مشترک» مورد نظر درج شود. سپس یک جدول هم داشته باشیم مثلاً به نام Timeline با فیلدهای «نام کاربری نویسنده، نام کاربری گیرنده، متن کامل مطلب و تاریخ ارسال».
هر زمان مطلب جدیدی منتشر شد، به ازای هر مشترک در جدول اشتراک، یک رکورد در جدول Timeline درج شود، در این حالت کار خواندن مطالب جدیدی که باید به کاربر نشان دهیم ساده میشود اما اگر یک کاربر مثلاً 10000 تا مشترک داشته باشد پس به ازای هر مطلب جدیدی که مینویسد باید 10000 رکورد در جدول Timeline درج شود، و اگر 100 نفر بخواهند مطلب بنویسند فکر کنم سیستم از کار بی افتد.
ممکن است من را راهنمایی کنید.  
ویرایش:
در این لینک صفحه 26   مطلب زیر رو هم پیدا کرم.

مطالب
ساده ترین روش کار با Github در ویندوز
در  این صفحه  یک برنامه مختص ویندوز قرار داده شده است که شعار آن بدین شکل است :"کار با گیت هاب تا بحال تا این حد آسان نبوده است". موقعی که فایل را دانلود کنید، بعد از اجرا، شروع به دانلود و نصب برنامه اصلی خواهد کرد که در حال حاضر حجم فعلی آن حدود 45 مگابایت است. بعد از اینکه برنامه را نصب کرده و آن را اجرا کنید، از شما درخواست اطلاعات لاگین را می‌کند. اطلاعات ورود به GitHub را وارد کنید تا با اکانت شما در سایت ارتباط برقرار کند و خود را با آن سینک نماید.
برای ایجاد یک repository جدید می‌توانید از دکمه‌ی Add، که در بالا سمت چپ قرار دارد استفاده کنید. در اولین کادر متنی، یک نام و در دومین کادر، متن مسیر ذخیره پروژه را اختصاص دهید. در قسمت git ignore می‌توانید مشخص کنید که چه فایل‌هایی توسط سیستم گیت ردیابی نشوند و در زمان سینک کردن یا انتشار محتوا، به سیستم گیت اضافه نشوند. این گزینه را می‌توانید none انتخاب کنید تا شاید بعدا بخواهید دستی آن را تغییر دهید. ولی با این حال این گزینه شامل قالب‌های از پیش آماده‌ای است که ممکن است کار را برای شما راحت کند. مثلا گزینه‌ی پیش فرض Windows، در مورد فایل‌هایی با پسوند doc یا docx و ... می‌باشد. برای اطلاع از روش کار این فایل، مطالب اینجا را مطالعه فرمایید.

در صورتیکه فایل‌های شما برای انشار نهایی آماده هستند، پروژه خود را در لیست سمت چپ برنامه انتخاب کنید تا در بالا و سمت راست برنامه، گزینه‌ی Publish Repository دیده شود و با انتخاب آن، یک نام را که قبلا وارد کرده اید و یک توضیح مختصر را از شما می‌خواهد. به صورت پیش فرض انتشارها عمومی و رایگان هستند. در صورتی که اگر بخواهید این انتشار را تنها برای خود و به صورت احتصاصی انجام دهید، باید هزینه آن را پرداخت کنید.

در صورتیکه دوست دارید در پروژه‌ای مشارکت داشته باشید، ابتدا پروژه مورد نظر را در سایت گیت هاب Fork  کنید و سپس از طریق گزینه‌ی Add در برنامه عمل کنید و اینبار در سربرگ‌های بالا، به جای Create گزینه‌ی Clone را انتخاب نمایید. در این حالت لیستی از پروژه‌های Fork شده نمایش داده می‌شوند و با انتخاب هر کدام، پروژه بر روی سیستم شما کپی خواهد شد.

بعد از انتخاب گزینه‌ی Clone، از شما محل ذخیره‌ی پروژه را خواهد پرسید و بعد از تایید آن، مقدار زمان کمی برای کپی کردن پروژه خواهد خواست. پس از آن لیستی از همه‌ی تغییرات و مشارکت‌ها به شما نمایش داده می‌شود و در صورتیکه دوست دارید به تغییری در قبل برگردید تا کارتان را از آن شروع کنید، می‌توانید از گزینه‌ی Revert استفاده کنید. برای یادگیری سایر اصطلاحات فنی گیت و گیت‌هاب می‌توانید از مسیرهای آموزشی آن استفاده کنید.

حال با خیال راحت روی پروژه کار کنید و تغییرات را روی آن اعمال کنید و بعد از اینکه کارتان تمام شد، دوباره به برنامه باز گردید و پروژه را در لیست انتخاب کرده و در سمت راست بالای صفحه، گزینه‌ی Sync Now را انتخاب کنید تا مشارکت جدید شما به سیستم گیت هاب اعمال شود و حالا اگر به صفحه‌ی پروژه در سایت گیت هاب بروید، می‌بینید که شما به عنوان یک مشارکت کننده‌ی جدید اضافه شده‌اید. پس با هر بار تغییر نسخه‌ی پروژه می‌توانید آن را با سیستم گیت سینک نمایید.

گزینه‌ی تنظیمات که در کنار عبارت Sync Now قرار دارد و با رنگ آبی در شکل مشخص شده است نیز به شما اجازه‌ی تغییر فایل‌های تنظیماتی از قبیل gitignore یا gitattribute را می‌دهد.

در صورتی که برای پروژه‌ای در گیت هاب شاخه‌ها یا branches تعریف شده باشند، در اینجا هم می‌توانید شاخه‌ی مورد نظر را انتخاب کنید:

مطالب
استفاده از popBox برای کوچک کردن خودکار تصاویر بزرگ

یکی از دوستان در قسمت تماس با من پیغام گذاشته بود که چگونه تصاویر یک صفحه را می‌شود به صورت موقت کوچک کرد؟ برای نمونه، این مشکلی است که در فوروم‌ها زیاد وجود دارد. گاهی از اوقات یکی از کاربران تصویری را ارسال می‌کند که اندازه‌ی آن در حد یک تابلوی دیواری است!
با استفاده از محصول سورس بازی به نام popBox می‌توان تصاویر دلخواهی را ابتدا با اندازه‌ی کوچک در صفحه نشان داد و پس از کلیک بر روی آن، تصویر به اندازه اصلی آن نمایش داده خواهد شد. به این صورت صفحه از شکل نخواهد افتاد. این اسکریپت از آدرس زیر قابل دریافت است:
http://www.c6software.com/products/popbox/default.aspx
نحوه استفاده و مثال‌های مربوطه هم در همان آدرس قابل مشاهده است و نکته خاصی ندارد.

اما این روش یا روش‌های مشابه آن یک مشکل دارند. اعمال آن‌ها باید به صورت دستی صورت گیرد. یعنی باید دقیقا مشخص کنیم که این تصویر معلوم در ابتدا به اندازه‌ی مشخصی نمایش داده شود و سایر تنظیمات مربوطه اعمال گردد. این روش در سایت‌های پویا عملا پاسخگو نخواهد بود. زمانیکه کاربری مطلبی را ارسال می‌کند یا باید در سورس‌های موجود دست برد و به تصاویر ارسالی به صورت خودکار تنظیمات مربوط به popbox‌ را اضافه کرد، یا اینکه هر بار مطالب را پس از ارسال به صورت دستی ویرایش کرد یا روش‌هایی مانند این.

با استفاده از jQuery ، اسکریپت selectAll.js را به این مجموعه اضافه کردم. ابتدا تمامی تصاویر صفحه پیدا می‌شوند و سپس به آنهایی که طول و عرض بزرگتری از حد مجاز دارند به صورت خودکار ویژگی‌های popBox افزوده خواهد شد. چون این روش اسکریپتی است : الف) نیازی به تغییر در سورس‌های موجود نیست. ب) توسط هر تکنولوژی سمت سروری نیز قابل استفاده است. ج) به صورت خودکار به تمامی تصاویر بزرگ صفحه اعمال می‌شود و نیازی به ویرایش دستی هیچ موردی جهت اعمال ویژگی‌های مورد نظر وجود نخواهد داشت.

نکته جالبی که با jQuery وجود دارد، روال رخ‌داد گردان document.ready آن است. این رخداد پیش از window.onload استاندارد و زمانیکه DOM آماده استفاده است فراخوانی می‌شود.

برای استفاده از آن ابتدا باید دو فولدر scripts و images به سرور کپی شوند. سپس سه سطر زیر را به هدر صفحه اضافه کنید (به سورس صفحه sample.html فایل پیوست می‌توان مراجعه کرد):

<script src="scripts/jquery.min.js" type="text/javascript"></script>
<script src="scripts/PopBox.js" type="text/javascript"></script>
<script type="text/javascript" src="scripts/selectAll.js"></script>

در مورد سایر تنظیمات، فایل selectAll.js را گشوده و موارد زیر را در صورت لزوم ویرایش نمائید:
popBoxWaitImage.src = "images/spinner40.gif";
popBoxRevertImage = "images/magminus.gif";
popBoxPopImage = "images/magplus.gif";
popBoxRevertText = "کوچک‌ نمایی";
popBoxPopText = "بزرگ‌ نمایی";
popBoxCaptionMoreText = "بیشتر";
popBoxCaptionLessText = "کمتر";
MaximumAllowedWidth = 500;
MaximumAllowedHeight = 480;

در این فایل قسمتی از تابع rescaleImage از سایت stack overflow قرض گرفته شد و تغییرات لازم جهت اعمال ویژگی‌های popbox‌ به آن اعمال گردید. با استفاده از این تابع نسبت‌های طول و عرض تصویر به میزان ثابتی کوچک می‌شوند و تصویر کوچک شده با کیفیت بهتری نمایش داده خواهد شد.
موارد ذکر شده را از این آدرس می‌توانید دریافت نمائید.


مطالب
بررسی Source Generators در #C - قسمت اول - معرفی
Source Generators که به همراه C# 9.0 ارائه شدند، یک فناوری نوین meta-programming است و به عنوان جزئی از پروسه‌ی استاندارد کامپایل برنامه، ظاهر می‌شود. هدف اصلی از ارائه‌ی Source Generators، تولید کدهای تکراری مورد استفاده‌ی در برنامه‌ها است. برای مثال بجای انجام کارهای تکراری مانند پیاده سازی متدهای GetHashCode، ToString و یا حتی یک AutoMapper و یا Serializer، برای تمام کلاس‌های برنامه، Source Generators می‌توانند آن‌ها را به صورت خودکار پیاده سازی کنند و همچنین با هر تغییری در کدهای کلاس‌ها، این پیاده سازی‌ها به صورت خودکار به روز خواهند شد. مزیت این روش نه فقط تولید پویای کدها است، بلکه سبب بهبود کارآیی برنامه هم خواهند شد؛ از این جهت که برای مثال می‌توان اعمالی مانند Serialization را بدون انجام Reflection در زمان اجرا، توسط آن‌ها پیاده سازی کرد.


زمانیکه پروسه‌ی کامپایل برنامه شروع می‌شود، در این بین، به مرحله‌ی جدیدی به نام «تولید کدها» می‌رسد. در این حالت، کامپایلر تمام اطلاعاتی را که در مورد پروژه‌ی جاری در اختیار دارد، به تولید کننده‌ی کد معرفی شده‌ی به آن ارائه می‌دهد. بر اساس این اطلاعات غنی ارائه شده‌ی توسط کامپایلر، تولید کننده‌ی کد، شروع به تولید کدهای جدیدی کرده و آن‌ها را در اختیار ادامه‌ی پروسه‌ی کامپایل، قرار می‌دهد. پس از آن، کامپایلر با این کدهای جدید، همانند سایر کدهای موجود در پروژه رفتار کرده و عملکرد عادی خودش را ادامه می‌دهد.

یک برنامه می‌تواند از چندین Source Generators نیز استفاده کند که روش قرار گرفتن آن‌‌ها را در پروسه‌ی کامپایل، در شکل زیر مشاهده می‌کنید:



Source Generators از یکدیگر کاملا مستقل هستند و اطلاعات آن‌ها Immutable است. یعنی نمی‌توان اطلاعات تولیدی توسط یک Source Generator را در دیگری تغییر داد و تمام فایل‌های تولیدی توسط انواع Source Generators موجود، به پروسه‌ی کامپایل نهایی اضافه می‌شوند. هرچند زمانیکه فایلی توسط یک تولید کننده‌ی کد، به کامپایلر اضافه می‌شود، بلافاصله اطلاعات آن در کل برنامه و IDE و تمام Source Generators موجود دیگر، قابل مشاهده و استفاده است.


مقایسه‌ای بین تولید کننده‌های کد و فناوری IL Weaving

Source Generators، تنها راه و روش تولید کد، نیستند و پیش از آن روش‌هایی مانند استفاده از T4 templates ، Fody ، PostSharp و امثال آن نیز ارائه شده‌است. در ادامه مقایسه‌ای را بین تولید کننده‌های کد و فناوری IL Weaving را که پیشتر در سری AOP در این سایت مطالعه کرده‌اید، مشاهده می‌کنید:
تولید کننده‌های کد:
- تنها می‌توانند فایل‌های جدید را اضافه کنند. یعنی «در حین» پروسه‌ی کامپایل ظاهر می‌شوند و به عنوان یک مکمل، تاثیر گذارند. برای مثال نمی‌توانند محتوای یک خاصیت یا متد از پیش موجود را تغییر دهند. اما می‌توانند هر نوع کد partial ای را «تکمیل» کنند.
- محتوای اضافه شده‌ی توسط یک تولید کننده‌ی کد، بلافاصله توسط Compiler شناسایی شده و بررسی می‌شود و همچنین در Intellisense ظاهر شده و به سادگی قابل دسترسی است. همچنین، قابلیت دیباگ نیز دارد.

IL Weaving:
- می‌توانند bytecode برنامه را تغییر دهند. یعنی «پس از» پروسه‌ی کامپایل ظاهر شده و کدهایی را به اسمبلی نهایی تولید شده اضافه می‌کنند. در این حالت محدودیتی از لحاظ محل تغییر کدها وجود ندارد. برای مثال می‌توان بدنه‌ی یک متد یا خاصیت را بطور کامل بازنویسی کرد و کارکردهایی مانند تزریق کدهای caching و logging را دارند.
- کدهایی که توسط این پروسه اضافه می‌شوند، در حین کدنویسی متداول، قابلیت دسترسی ندارند؛ چون پس از پروسه‌ی کامپایل، به فایل باینری نهایی تولیدی، اضافه می‌شوند. بنابراین قابلیت دیباگ به همراه سایر کدهای برنامه را نیز ندارند. به علاوه چون توسط کامپایلر در حین پروسه‌ی کامپایل، بررسی نمی‌شوند، ممکن است به همراه قطعه کدهای غیرقابل اجرایی نیز باشند و دیباگ آن‌ها بسیار مشکل است.



آینده‌ی Reflection به چه صورتی خواهد شد؟

هرچند Reflection کار تولید کدی را انجام نمی‌دهد، اما یکی از کارهای متداول با آن، یافتن و محاسبه‌ی اطلاعات خواص و فیلدهای اشیاء، در زمان اجرا است و مزیت کار کردن با آن نیز این است که اگر خاصیتی یا فیلدی تغییر کند، نیازی به بازنویسی قسمت‌های پیاده سازی شده‌ی با Reflection نیست. به همین جهت برای مثال تقریبا تمام کتابخانه‌های Serialization، از Reflection برای پیاده سازی اعمال خود استفاده می‌کنند.
امروز، تمام اینگونه عملیات را توسط Source Generators نیز می‌توان انجام داد و این فناوری جدید، قابلیت به روز رسانی خودکار کدهای تولیدی را با کم و زیاد شدن خواص و فیلدهای کلاس‌ها دارد و نمونه‌ای از آن، Source Generator توکار مرتبط با کار با JSON در دات نت 6 است که به شدت سبب بهبود کارآیی برنامه، در مقایسه با استفاده‌ی از Reflection می‌شود؛ چون اینبار تمام محاسبات دقیق مرتبط با Serialization به صورت خودکار در زمان کامپایل برنامه انجام می‌شود و جزئی از خروجی برنامه‌ی نهایی خواهد شد و دیگر نیازی به محاسبه‌ی هرباره‌ی اطلاعات مورد نیاز، در زمان اجرای برنامه نیست.
نمونه‌ای از روش دسترسی به اطلاعات کلاس‌ها و خواص و فیلدهای آن‌ها را در زمان کامپایل برنامه توسط Source Generators، در مثال قسمت بعد، مشاهده خواهید کرد.


وضعیت T4 templates چگونه خواهد شد؟

در سال‌های آغازین ارائه‌ی دات نت، استفاده از T4 templates جهت تولید کدها بسیار مرسوم بود؛ اما با ارائه‌ی Source Generators، این ابزار نیز منسوخ شده در نظر گرفته می‌‌شود.
T4 Templates همانند Source Generators تنها کدها و فایل‌های جدیدی را تولید می‌کنند و توانایی تغییر کدهای موجود را ندارند. اما مشکل مهم آن، داشتن Syntax ای خاص است که توسط اکثر IDEها پشتیبانی نمی‌شود. همچنین عموما اجرای آن‌ها نیز دستی است و برخلاف Source Generators، با تغییرات کدها، به صورت خودکار به روز نمی‌شوند.


تغییرات زبان #C در جهت پشتیبانی از تولید کننده‌های کد

از سال‌های اول ارائه‌ی زبان #C، واژه‌ی کلیدی partial، جهت فراهم آوردن امکان تقسیم کدهای یک کلاس، به چندین فایل، میسر شد که از این قابلیت در فناوری T4 Templates زیاد استفاده می‌شد. اکنون با ارائه‌ی تولید کننده‌های کد، واژه‌ی کلیدی partial را می‌توان به متدها نیز افزود تا پیاده سازی اصلی آن‌ها، در فایلی دیگر، توسط تولید کننده‌های کد انجام شود. تا C# 8.0 امکان افزودن واژه‌ی کلیدی partial به متدهای خصوصی یک کلاس و آن هم از نوع void وجود داشت و در C# 9.0 به متدهای عمومی کلاس‌ها نیز اضافه شده‌است و اکنون این متدها می‌توانند void هم نباشند:
partial class MyType
{
   partial void OnModelCreating(string input); // C# 8.0

   public partial bool IsPet(string input);  // C# 9.0
}

partial class MyType
{
   public partial bool IsPet(string input) =>
     input is "dog" or "cat" or "fish";
}
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایل‌های config
منظور این بود که اگر فایل متنی JSON مرتبط را در نوت‌پد (و امثال آن و یا با برنامه نویسی) باز کردید و تغییر دادید (ذخیره کردید، تاریخ Last modified آن را تغییر دادید)، بلافاصله تغییرات آن در برنامه منعکس می‌شوند؛ چون یک file watcher برای تغییرات آن فایل در پشت صحنه فعال است. مانند file watcher فایل معروف web.config در برنامه‌های ASP.NET که آن هم یک کانفیگ است البته از نوع XML و اگر تغییری در آن داده شود، کل برنامه را ری‌استارت می‌کند. اما در اینجا برنامه با تغییرات فایل JSON کانفیگ آن ری‌استارت نخواهد شد. فقط «یک نکته: بارگذاری مجدد اطلاعات فایل config در ASP.NET Core 1.1» قابل استفاده خواهد بود.
ایجاد رابط کاربری هم برای آن مساله‌ای است شخصی که در نهایت شامل deserialization و تبدیل آن به شیء و تغییر آن در برنامه و در آخر serialization این شیء و بازگشت به حالت JSON آن است. عموما از کتابخانه‌ی JSON.NET برای انجام اینکار استفاده می‌کنند.
نظرات مطالب
بررسی Bad code smell ها: دلال یا Middle Man
View Model در MVVM شبیه به کنترلرها در MVC هستند. این‌ها بالاترین لایه برنامه هستند که با تزریق امکانات دیگر سرویس‌های برنامه به آن‌ها امکان استفاده‌ی نهایی از آن‌ها فراهم می‌شود. بنابراین این‌ها بله ... دلال سرویس‌های برنامه به رابط کاربری آن هستند، اما با مفهوم فوق یکی نیستند. کار کنترلرها یا ViewModelها مدیریت و سامان دهی درخواست‌ها و هدایت آن‌ها به سرویس‌های متناظر، گاهی از اوقات ترکیب چند سرویس با هم و ارائه‌ی یک پاسخ به مصرف کننده نهایی است. بنابراین این‌ها واسط بین View برنامه و Model هستند و مدیریت کننده‌ی تعاملات با برنامه.
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
هدف از زیر ساخت بومی سازی در ASP.NET Core، حذف عبارات و رشته‌های درج شده‌ی در کلاس‌ها و ویووهای مختلف برنامه و انتقال آن‌ها به فایل‌های منبع resx است و سپس استفاده‌ی از آن‌ها توسط تزریق وابستگی‌ها. به این ترتیب می‌توان بر اساس نوع فرهنگ درخواستی کاربر جاری، رشته‌های درج شده را به صورت پویا، در زمان اجرای برنامه، بر اساس ترجمه‌های آن‌ها به کاربر نمایش داد.


نحوه‌ی تعیین فرهنگ ترد جاری در ASP.NET Core

در نگارش‌های پیشین ASP.NET، برای تعیین فرهنگ ترد جاری، از یکی از دو روش ذیل استفاده می‌شود:
الف) افزودن مدخل بومی سازی به فایل web.config
<system.web>
    <globalization uiCulture="fa-IR" culture="fa-IR" />
</system.web>
ب) و یا تعیین فرهنگ ترد با کدنویسی مستقیم در فایل global.asax
protected void Application_BeginRequest()
{
   Thread.CurrentThread.CurrentCulture = new CultureInfo("fa-IR");
   Thread.CurrentThread.CurrentUICulture = new CultureInfo("fa-IR");
}
در ASP.NET Core با حذف شدن System.Web و همچنین فایل global.asax، برای تعیین فرهنگ پیش فرض ترد جاری، به همراه فرهنگ‌هایی که برنامه از آن‌ها پشتیبانی می‌کند، به صورت ذیل عمل می‌شود:
public void Configure(IApplicationBuilder app)
{
    app.UseRequestLocalization(new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture(new CultureInfo("fa-IR")),
        SupportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fa-IR")
        },
        SupportedUICultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fa-IR")
        }
    });
در اینجا با مراجعه به کلاس آغازین برنامه و افزودن تنظیمات میان افزار RequestLocalization، می‌توان فرهنگ پیش فرض درخواست جاری و یا فرهنگ‌های پشتیبانی شده را مشخص کرد.
- تنظیمات SupportedCultures بر روی نمایش تاریخ، ساعت و واحد پولی تاثیر دارند. همچنین می‌توانند بر روی نحوه‌ی مقایسه‌ی حروف و مرتب سازی آن‌ها تاثیر داشته باشند.
- تنظیمات SupportedUICultures مشخص می‌کنند که کدامیک از فایل‌های resx برنامه که مداخل ترجمه‌های آن‌را به زبان‌های مختلف مشخص می‌کنند، باید بارگذاری شوند.
- تنظیم DefaultRequestCulture در صورت مشخص نشدن فرهنگ ترد جاری مورد استفاده قرار می‌گیرد.

یک مثال: هر ترد در دات نت دارای اشیاء CurrentCulture و CurrentUICulture است. اگر فرهنگ ترد جاری به en-US تنظیم شده باشد، متد DateTime.Now.ToLongDateString، خروجی نمونه Thursday, February 18, 2016 را نمایش می‌دهد.


زمانیکه میان افزار RequestLocalization فعال می‌شود، سه تامین کننده‌ی پیش فرض (مقدار‌های پیش فرض خاصیت RequestCultureProviders شیء RequestLocalizationOptions فوق)، جهت مشخص ساختن فرهنگ ترد جاری بکار گرفته خواهند شد:
الف) از طریق کوئری استرینگ با فعال سازی QueryStringRequestCultureProvider
http://localhost:5000/?culture=es-MX&ui-culture=es-MX
http://localhost:5000/?culture=es-MX
برای مثال در اینجا QueryStringRequestCultureProvider به دنبال کوئری استرینگ‌های culture و یا ui-culture گشته و با رسیدن به es-MX، فرهنگ جاری را به اسپانیایی مکزیکی تنظیم می‌کند. در این حالت اگر فقط culture ذکر شود، ui-culture نیز به همان مقدار تنظیم خواهد شد.
ب) از طریق نام کوکی با فعال سازی CookieRequestCultureProvider
CookieRequestCultureProvider کوکی ویژه‌ای را با نام پیش فرض AspNetCore.Culture. ایجاد می‌کند. این کوکی برای ردیابی اطلاعات بومی سازی انتخابی کاربر بکار می‌رود. برای مثال اگر به مقدار ذیل تنظیم شود:
 c='en-UK'|uic='en-US'
c آن به معنای culture و uic آن به معنای ui-culture خواهد بود.
ج) از طریق هدر مخصوص Accept-Language با فعال سازی AcceptLanguageHeaderRequestCultureProvider که می‌تواند به همراه درخواست HTTP ارسال شود.

اگر تمام این حالت‌ها تنظیم نشده بودند، آنگاه از مقدارDefaultRequestCulture  استفاده می‌شود. برای مثال اگر مرورگر به صورت پیش فرض هدر Accept-Language را en-US ارسال می‌کند :


دیگر کار به پردازش مقدارDefaultRequestCulture  نخواهد رسید.

اکنون اگر علاقمند بودید تا به کاربر امکان انتخاب زبانی را بدهید، یک چنین اکشن متدی را طراحی کنید:
public IActionResult SetFaLanguage()
{
    Response.Cookies.Append(
        CookieRequestCultureProvider.DefaultCookieName,
        CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(new CultureInfo("fa-IR"))),
        new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
    );
 
    return RedirectToAction("GetTitle");
}
این اکشن متد بر اساس تامین کننده‌ی کوکی ردیابی زبان انتخاب شده‌ی توسط کاربر و یا CookieRequestCultureProvider کار می‌کند و توسط آن، فرهنگ جاری برنامه به زبان فارسی تنظیم می‌شود. هرگاه که این اکشن متد فراخوانی شود، کوکی AspNetCore.Culture. به مقدار c=fa-IR|uic=fa-IR تنظیم می‌شود:


از اینجا به بعد است که اگر نام کنترلر شما TestLocalController باشد، فایل منبع متناظر با آن یعنی Controllers.TestLocalController.fa.resx، به صورت خودکار بارگذاری و پردازش خواهد شد. در غیر اینصورت فایل نمونه‌ی ختم شده‌ی به en.resx پردازش می‌شود؛ چون این زبان به صورت پیش فرض در هدر Accept-Language قید شده‌است.


آماده سازی برنامه برای کار با فایل‌های منبع زبان‌های مختلف

ابتدا پوشه‌ی جدیدی را به نام Resources به ریشه‌ی پروژه اضافه کنید. سپس به کلاس آغازین برنامه مراجعه کرده و محل یافت شدن این پوشه را معرفی کنید:
public void ConfigureServices(IServiceCollection services)
{
    services.AddLocalization(options => options.ResourcesPath = "Resources");
    services.AddMvc()
        .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
        .AddDataAnnotationsLocalization();
در اینجا سرویس جدید Localization، به لیست سرویس‌های ثبت شده‌ی در IoC Container اضافه می‌شود. همچنین توسط خاصیت  ResourcesPath  آن مشخص شده‌است که فایل‌های resx را باید از کجا دریافت کند.
به علاوه به سرویس ASP.NET MVC، تنظیمات بومی سازی Viewها و DataAnnotations نیز اضافه شده‌اند. تنظیم suffix به معنای  view file suffix و یا مثلا fr در نام فایل Index.fr.cshtml است.


نحوه‌ی تعریف و پوشه بندی فایل‌های منبع زبان‌های مختلف

تا اینجا پوشه‌ی جدید Resources را به پروژه اضافه، معرفی و سرویس‌های مرتبط را نیز فعال کردیم. پس از آن نوبت به افزودن فایل‌های resx است. برای این منظور بر روی پوشه‌ی منابع کلیک راست کرده و گزینه‌ی add->new item را انتخاب کنید.


در اینجا با جستجوی resource، می‌توان فایل resx جدیدی را به پروژه اضافه کرد؛ اما ... انتخاب نام آن باید بر اساس نکات ذیل باشد:
الف) برای کنترلرها یکی از دو مسیر / دار و یا نقطه دار جستجو می‌شوند:
Resources/Controllers.HomeController.fr.resx
Resources/Controllers/HomeController.fr.resx

در اینجا fr ذکر شده، همان LanguageViewLocationExpanderFormat.Suffix است که پیشتر بحث شد. قسمت ابتدایی Controllers همیشه ثابت است (یا به صورت نام یک پوشه و یا به عنوان قسمت اول نام فایل). سپس نام کلاس کنترلر به همراه نام فرهنگ مدنظر باید ذکر شوند. قسمت نام پوشه‌ی Resources را نیز به services.AddLocalization معرفی کرده‌ایم.

ب) برای Viewها نیز همان حالت‌های / دار و یا نقطه دار بررسی می‌شوند:
Resources/Views.Home.About.fr.resx
Resources/Views/Home/About.fr.resx


برای تمام فایل‌ها و کلاس‌ها می‌توان فایل منبع ایجاد کرد

در این نگارش از ASP.NET، در حالت کلی، نام یک فایل منبع، همان نام کامل کلاس آن است؛ منهای فضای نام آن (اگر این فایل منبع در همان اسمبلی قرار گیرد). برای مثال اگر می‌خواهید برای کلاس Startup برنامه، فایل منبعی را درست کنید و نام کامل آن با درنظر گرفتن فضای نام، معادل LocalizationWebsite.Web.Startup است، ابتدای فضای نام آن‌را حذف کنید و سپس آن‌را ختم به fa.resx کنید؛ مثلا Startup.fa.resx
اگر محل واقع شدن فایل‌های resx در همان اسمبلی اصلی پروژه باشند، نیازی به ذکر فضای نام پیش فرض پروژه نیست. برای مثال اگر فضای نام پیش فرض پروژه‌ی وب جاری MyLocalizationWebsite.Web است، بجای نام فایل MyLocalizationWebsite.Web.Controllers.HomeController.fr.resx می‌توانید به صورت خلاصه بنویسید Controllers.HomeController.fr.resx. در غیراینصورت (استفاده از اسمبلی‌های دیگر)، ذکر کامل فضای نام مرتبط هم الزامی است.


چند نکته:
- اگر ResourcesPath را در services.AddLocalization معرفی نکنید، مسیر پیش فرض یافتن فایل‌های resx مربوط به کنترلرها، پوشه‌ی ریشه‌ی پروژه است و برای Viewها، همان پوشه‌ی محل واقع شدن View متناظر خواهد بود.
- اینکه کدام فایل منبع در برنامه بارگذاری می‌شود، دقیقا مرتبط است با فرهنگ ترد جاری و این فرهنگ به صورت پیش فرض en-US است (چون همواره در هدر Accept-Language ارسالی توسط مرورگر وجود دارد). برای تغییر آن، از نکته‌ی اکشن متد public IActionResult SetFaLanguage ابتدای بحث استفاده کنید (در غیراینصورت در آزمایشات خود شاهد بارگذاری فایل‌های منبع دیگری بجز en.resx‌ها نخواهید بود).
- فایل‌های منبع را به صورت کامپایل شده در پوشه‌ی bin برنامه خواهید یافت:



خواندن اطلاعات منابع در کنترلرهای برنامه

فرض کنید کنترلری را به نام TestLocalController ایجاد کرده‌ایم. بنابراین فایل منبع فارسی متناظر با آن Controllers.TestLocalController.fa.resx خواهد بود؛ با این محتوای نمونه:


محتوای این کنترلر نیز به صورت ذیل است:
using System;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.Extensions.Localization;
 
namespace Core1RtmEmptyTest.Controllers
{
    public class TestLocalController : Controller
    {
        private readonly IStringLocalizer<TestLocalController> _stringLocalizer;
        private readonly IHtmlLocalizer<TestLocalController> _htmlLocalizer;
 
        public TestLocalController(
            IStringLocalizer<TestLocalController> stringLocalizer,
            IHtmlLocalizer<TestLocalController> htmlLocalizer)
        {
            _stringLocalizer = stringLocalizer;
            _htmlLocalizer = htmlLocalizer;
        }
 
        public IActionResult Index()
        {
            var name = "DNT";
            var message = _htmlLocalizer["<b>Hello</b><i> {0}</i>", name];
            ViewData["Message"] = message;
            return View();
        }
 
        [HttpGet]
        public string GetTitle()
        {
            var about = _stringLocalizer["About Title"];
            return about;
        }
 
        public IActionResult SetFaLanguage()
        {
            Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(new CultureInfo("fa-IR"))),
                new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
            );
 
            return RedirectToAction("GetTitle");
        }
    }
}
در اینجا نحوه‌ی دسترسی به فایل‌های منبع را در کنترلرها مشاهده می‌کنید. سرویس IStringLocalizer برای خواندن key/valueهای معمولی طراحی شده‌است و سرویس IHtmlLocalizer برای خواندن key/valueهای تگ دار، بکار می‌رود. علت تنظیم شدن پارامتر جنریک آن‌ها به نام کنترلر جاری این است که این سرویس‌ها بدانند دقیقا چه نوعی را قرار است بارگذاری کنند و دقیقا باید به دنبال کدام فایل بگردند. این سرویس‌ها یک کلید را می‌گیرند و یک خروجی و مقدار را باز می‌گردانند.
اگر برنامه را در حالت معمولی اجرا کنید و سپس آدرس http://localhost:7742/testlocal/gettitle را درخواست کنید، عبارت About Title را مشاهده می‌کنید؛ به دو علت:
الف) هنوز فرهنگ پیش فرض ترد جاری همان en-US است که توسط مرورگر ارسال شده‌است.
ب) چون فایل resx متناظر با فرهنگ پیش فرض ترد جاری یافت نشده‌است، مقدار همان کلید درخواستی بازگشت داده می‌شود؛ یعنی همان About Title.

برای رفع این مشکل آدرس http://localhost:7742/testlocal/SetFaLanguage را درخواست کنید. به این صورت با تنظیم کوکی ردیابی فرهنگ ترد جاری به زبان فارسی، خروجی GetTile این‌بار «درباره» خواهد بود.


خواندن اطلاعات منابع در Viewهای برنامه

فرض کنید فایل Views.TestLocal.Index.fa.resx (فایل منبع کنترلر TestLocal و ویوو Index آن به زبان فارسی) دقیقا همان محتوای فایل Controllers.TestLocalController.fa.resx فوق را دارد (اگر نام پوشه‌ی Views را تغییر داده‌اید، قسمت ابتدایی نام فایل Views را هم باید تغییر دهید). برای دسترسی به اطلاعات آن در یک ویوو، می‌توان از سرویس IViewLocalizer  به نحو ذیل استفاده کرد:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
 
@{
}
Message @ViewData["Message"]
<br/>
@Localizer["<b>Hello</b><i> {0}</i>", "DNT"]
<br/>
@Localizer["About Title"]
در اینجا ViewData، از همان اطلاعات اکشن متد Index استفاده می‌کند.
Localizer از طریق تزریق سرویس IViewLocalizer  به View برنامه تامین می‌شود. این سرویس در پشت صحنه از همان IHtmlLocalizer استفاده می‌کند و در حین استفاده‌ی از آن، اطلاعات تگ‌ها انکد (encoded) نخواهند شد (به همین جهت برای کار با کلیدها و مقادیر تگ‌دار توصیه می‌شود).


استفاده از اطلاعات منابع در DataAnnotations

قسمت اول فعال سازی بومی سازی DataAnnotations با ذکر AddDataAnnotationsLocalization در متد ConfigureServices، در ابتدای بحث انجام شد و همانطور که پیشتر نیز عنوان گردید، در این نگارش از ASP.NET، برای تمام کلاس‌های برنامه می‌توان فایل منبع ایجاد کرد. برای مثال اگر کلاس RegisterViewModel در فضای نام ViewModels.Account قرار گرفته‌است، نام فایل منبع آن یکی از دو حالت / دار و یا نقطه دار ذیل می‌تواند باشد:
Resources/ViewModels.Account.RegisterViewModel.fr.resx
Resources/ViewModels/Account/RegisterViewModel.fr.resx

محتوای این کلاس را در ذیل مشاهده می‌کنید:
using System.ComponentModel.DataAnnotations;
 
namespace Core1RtmEmptyTest.ViewModels.Account
{
    public class RegisterViewModel
    {
        [Required(ErrorMessage = "EmailReq")]
        [EmailAddress(ErrorMessage = "EmailType")]
        [Display(Name = "Email")]
        public string Email { get; set; }
    }
}
در این حالت مقداری که برای ErrorMessage ذکر می‌شود، کلیدی است که باید در فایل منبع جستجو شود:


یک نکته: هیچ الزامی ندارد که کلیدها را به این شکل وارد کنید. از این جهت که اگر این کلید در فایل منبع یافت نشد و یا فرهنگ ترد جاری با فایل‌های منبع مهیا تطابقی نداشت، عبارتی را که کاربر مشاهده می‌کند، دقیقا معادل «EmailReq» خواهد بود. بنابراین در اینجا می‌توانید کلید را به صورت کامل، مثلا مساوی «The Email field is required» وارد کنید و همین عبارت را به عنوان کلید در فایل منبع ذکر کرده و مقدار آن‌را مساوی ترجمه‌ی آن قرار دهید. این نکته در تمام حالات کار با کنترلرها و ویووها نیز صادق است.


استفاده از یک منبع اشتراکی

اگر می‌خواهید تعدادی از منابع را در همه‌جا در اختیار داشته باشید، روش کار به این صورت است:
الف) یک کلاس خالی را به نام SharedResource دقیقا با فرمت ذیل در پوشه‌ی Resources ایجاد کنید:
// Dummy class to group shared resources
namespace Core1RtmEmptyTest
{
   public class SharedResource
   {
   }
}
ب) اکنون فایل‌های منبع خود را در پوشه‌ی Resources، دقیقا با این نام‌های خاص ایجاد کنید:
SharedResource.fa.resx
SharedResource.en-US.resx
و امثال آن

ج) برای استفاده‌ی از این منبع اشتراکی در کلاس‌های مختلف برنامه تنها کافی است در حین تزریق وابستگی‌ها، نوع آرگومان جنریک IStringLocalizer را به SharedResource تنظیم کنید:
 IStringLocalizer<SharedResource> sharedLocalizer
و یا حتی در ویووهای برنامه نیز می‌توان از آن استفاده کرد:
 @inject IHtmlLocalizer<SharedResource> SharedLocalizer
مطالب
آشنایی با CLR: قسمت دهم
در سلسله مقالات قبلی ما فصل اول از بخش اول را به پایان بردیم و مبحث آشنایی با CLR و نحوه‌ی اجرای برنامه را یاد گرفتیم. در این سلسله مقالات که مربوط به فصل دوم از بخش اول است، در مورد نحوه‌ی ساخت و توزیع برنامه صحبت می‌کنیم.

در طی این سال‌ها ویندوز به ناپایداری و پپیچیدگی متهم شده است. صرف نظر از این که ویندوز شایستگی این اتهامات را دارد یاخیر، این اتهامات نتیجه‌ی چند عامل است:
اول از همه برنامه‌ها از dll هایی استفاده می‌کنند که بسیاری از آن‌ها نوشته‌ی برنامه نویسانشان نیست و توسط توسعه دهندگان دیگر ارائه شده‌اند و توسعه دهندگان مربوطه نمی‌توانند صد در صد مطمئن شوند که افراد دیگر، به چه نحوی از dll آن‌ها استفاده می‌کنند و در عمل ممکن هست باعث دردسرهای زیادی شود که البته این نوع مشکلات عموما از قبل خودشان را نشان نمی‌دهند، چرا که توسط سازنده‌ی برنامه تست و دیباگ شده‌اند.
موقعی کاربرها بیشتر دچار دردسر می‌گردند که برنامه‌های خودشان را به روز می‌کنند و عموما شرکت‌ها در آپدیت‌ها، فایل‌های جدید زیادی را روی سیستم کاربر منتقل می‌کنند که ممکن هست سازگاری با فایل‌های قبلی موجود نداشته باشند و از آنجا که همیشه تست این مورد برای توسعه دهنده امکان ندارد، به مشکلاتی بر می‌خورند و نمی‌توانند صد در صد مطمئن باشند که تغییرات جدید باعث تاثیر ناخوشایند نمی‌شود.
مطمئن هستم شما بسیاری از این مشکلات را دیده‌اید که کاربری یک برنامه را نصب می‌کند و شما متوجه می‌شوید که یک برنامه‌ی از قبل نصب شده به خاطر آن دچار مشکل می‌شود و این مورد به DLL hell مشهور هست. این مورد باعث ایجاد ترس و لرز برای کاربر شده تا با دقت بیشتری به نصب برنامه‌ها بپردازد.

دومین مورد مربوط به نصب برنامه‌ها است که متهم به پیچیدگی است. امروزه هر برنامه‌ای که روی سیستم نصب می‌شود، بر همه جای سیستم تاثیر می‌گذارد. یک برنامه را نصب می‌کنید و به هر دایرکتوری تعدادی فایل کپی می‌شود. تنظیمات ریجستری را آپدیت می‌کند، یک آیکن روی دسکتاپ و یکی هم start menu یا مترو را اضافه می‌کند. به این معنی که یک نصب کننده به عنوان یک موجودیت واحد شناخته نمی‌شود. شما نمی‌تونید راحت از یک برنامه بکاپ بگیرید. باید فایل‌های مختلفش را جمع آوری کنید و تنظیمات ریجیستری را ذخیره کنید. عدم امکان انتقال یک برنامه به یک سیستم دیگر هم وجود دارد که باید مجدد برنامه را نصب کنید و نکته‌ی نهایی، حذف برنامه که گاهی اوقات حذف کامل نیست و به شکل نامنظم و کثیفی اثراتش را به جا می‌گذارد.

سومین مورد امنیت هست. موقعی که کاربر برنامه‌ای را نصب می‌کند انواع فایل‌ها از شرکت و تولید کننده‌های مختلف روی سیستم نصب می‌شوند. گاهی اوقات برنامه‌ها بعضی از فایل هایشان را از روی اینترنت دریافت می‌کنند و کاربر اصلا متوجه موضوع نمی‌شود و این فایل‌ها می‌توانند هر کاری از حذف فایل از روی سیستم گرفته تا ارسال ایمیل را انجام بدهند که این موارد باعث وحشت کاربرها از نصب یک برنامه‌ی جدید می‌شود که این مورد را با قرار دادن یک سیستم امنیت داخلی با اجازه و عدم اجازه کاربر می‌شود تا حدی رفع کرد.

دات نت فریمورک هم این معضل را به طور عادی در زمینه‌ی DLL hellدارد که در فصل آتی حل آن بررسی خواهد شد. ولی بر خلاف COM، نوع‌های موجود در دات نت نیازی به ذخیره تنظیمات در ریجستری ندارند؛ ولی متاسفانه لینک‌های میانبر هنوز وجود دارند. در زمینه امنیت دات نت شامل یک مدل امنیتی به نام Code Access security می‌باشد؛ از آنجا که امنیت ویندوز بر اساس هویت کاربر تامین می‌شود. code access security به برنامه‌های میزبان مثل sql server اجازه می‌دهد که مجوز مربوطه را خودشان بدهند تا بدین صورت بر اعمال کامپوننت‌های بار شده نظارت داشته باشند که البته این مجوز‌ها در حد معمولی و اندک هست. ولی اگر برنامه خود میزبان که به طور محلی روی سیستم نصب می‌شوند، باشد دسترسی کاملب به مجوزها را دارد. پس بدین صورت کاربر این اجازه را دارد که بر آن چیزی که روی سیستم نصب یا اجرا می‌شود، نظارت داشته باشه تا کنترل سیستم به طور کامل در اختیار او باشد.
در قسمت بعدی با نحوه توزیع برنامه آشنا خواهیم شد.
مطالب
رمزنگاری کانکشن استرینگ در ASP.Net

ذخیره کردن رشته اتصالی به دیتابیس، به صورت یک رشته مشخص در کدهای برنامه، کاری است مزموم. زیرا پس از هر بار تغییر این مورد، نیاز خواهد بود تا تمامی سورس‌ها تغییر کنند و اگر از حالت web application استفاده کرده باشید، مجبور خواهید شد یکبار دیگر برنامه را کامپایل و دایرکتوری bin روی سرور را به روز کنید. به همین جهت، استاندارد برنامه‌های ASP.Net این است که این رشته اتصالی را در فایل web.config ذخیره کنیم تا با هر بار تغییر پارامترهای مختلف آن (مثلا تغییر نام سرور، یا تعویض ماهیانه پسوردها)، مجبور به کامپایل مجدد برنامه نشویم. شبیه به همین مورد در برنامه‌های PHP هم رایج است و عموما این مشخصات در فایل config.php و یا با اسامی شبیه به این صورت می‌گیرد.
در ASP.Net 1.x قسمت خاصی برای کانکشن استرینگ وجود نداشت اما از ASP.Net 2 به بعد ، قسمت ویژه‌ای مخصوص این کار در فایل web.config در نظر گرفته شده است.
خیلی هم خوب! اما این تجربه تلخ کاری را (که یکبار برای من رخ داد) هم همواره در نظر داشته باشید:
امکان خوانده شدن محتوای فایل کانفیگ، توسط همسایه شما در همان هاست اشتراکی که الان از آن دارید استفاده می‌کنید. عموما هاست‌های اینترنتی اشتراکی هستند و نه dedicated و نه فقط مختص به شما. از یک سرور برای سرویس دهی به 100 ها سایت استفاده می‌شود. یکبار در یکی از سایت‌ها دیدم که فایل machine.config سرور را هم محض نمونه خوانده بودند چه برسد به فایل متنی کانفیگ شما! یا تصور کنید که وب سرور هک شود. عموما اس کیوال سرور بر روی سرور دیگری قرار دارد. به همین جهت رمزنگاری این رشته باز هم ضریب امنیت بیشتری را به همراه خواهد داشت.
به همین منظور رمزنگاری قسمت کانکشن استرینگ فایل وب کانفیگ الزامی است، چون آن‌هایی که به دنبال اطلاعاتی اینگونه هستند دقیقا می‌دانند باید به کجا مراجعه کنند.

راه حل‌ها:

الف) از وب کانفیگ برای این‌کار استفاده نکنید. یک فایل class library‌ درست کنید (یک dll مجزا) و ارجاعی از این فایل را به پروژه خود اضافه کنید و از رشته اتصالی قرار گرفته در آن استفاده کنید. این فایل را هم می‌توان با روش‌های obfuscation محافظت کرد تا امنیت اطلاعات داخل آن‌را تا حد قابل قبولی بالا برد. همچنین می‌توان برای این فایل کتابخانه، امضای دیجیتال درنظر گرفت. زیرا امضای دیجیتال سبب می‌شود تا تغییر فایل dll رشته اتصالی، با یک کپی و paste معمولی قابل انجام نباشد (تمامی dll ها و اسمبلی‌های دیگری که ارجاعی از آن‌را در خود دارند باید یکبار دیگر هم کامپایل و به سرور منتقل شوند). این یک نوع اطمینان خاطر است اما در بلند مدت شاید تکرار اینکار خسته کننده باشد.

ب)استفاده از روش استاندارد رمزنگاری قسمت‌های مختلف کانکشن استرینگ فایل web.config
برای مشاهده نحوه انجام اینکار با برنامه نویسی به این مقاله مراجعه نمائید.
مزیت: نیازی به کد نویسی برای رمزگشایی و استفاده از آن نیست و اینکار به صورت خودکار توسط ASP.Net انجام می‌شود.
ایراد:فایل حاصل قابل انتقال نیست. چون رمزنگاری بر اساس کلیدهای منحصربفرد سرور شما ایجاد می‌شوند، این فایل از یک سرور به سرور دیگر قابل انتقال و استفاده نخواهد بود. یعنی اگر بر روی کامپیوتر برنامه نویسی شما این‌کار صورت گرفت، برنامه در سرور کار نخواهد کرد. البته شاید ایراد آنچنانی نباشد و فقط باید یکبار دیگر روی هاست نیز این کار را تکرار کرد. اما باید درنظر داشت که همسایه محترم شما نیز می‌تواند بر روی همان هاست به سادگی فایل شما را رمزگشایی کند! بنابراین نباید اصلا به این روش در هاست‌های اشتراکی دل خوش کرد.

ج)بکارگیری روش‌های غیراستاندارد رمزنگاری
منظور از غیراستاندارد، حالت‌های دیگر استاندارد رمزنگاری و رمزگشایی نسبت به روش استاندارد ارائه شده توسط مایکروسافت است (که همه از آن مطلع هستند). به شخصه از این روش در هاست‌ها استفاده می‌کنم. (مثلا، البته با کمی تغییر و پیچ و تاب بیشتر)
الگوریتم‌های رمزنگاری و رمزگشایی در یک فایل dll به برنامه اضافه می‌شوند (بنابراین این فایل قرار نیست تغییر کند). رشته رمزنگاری شده در فایل web.config قرار می‌گیرد. بدیهی است در هر بار اتصال به دیتابیس این رشته باید رمزگشایی شود اما سربار آن بسیار کم است و اصلا مشهود نیست. در هر حال این هزینه‌ای است که باید پرداخت شود. بدست آوردن ساده کانکشن استرینگ یعنی امکان پاک کردن سریع کل اطلاعات شما.

د)اگر سرور dedicated است حتما از روش windows authentication استفاده کنید
برای مثال یک سرور dedicated مخصوص کار ویژه‌ای تهیه کرده اید یا در شبکه اینترانت یک شرکت برنامه شما نصب شده است.
روش اعتبار سنجی از نوع ویندوزی برای اتصال به اس کیوال سرور نسبت به حالت sql server authentication امن تر است، زیرا نیازی نیست تا در وب کانفیگ نام کاربری یا پسوردی را مشخص نمائید و همچنین در این حالت پسوردها در شبکه منتقل نمی‌شوند (در حالت sql server authentication اینطور نیست). اما عموما در هاست‌های اشتراکی برای ساده تر کردن کار ، از این روش استفاده نمی‌کنند.
بنابراین در اینجا حتی اگر شخصی به رشته اتصالی شما دسترسی پیدا کند، کار خاصی را نمی‌تواند انجام دهد چون هیچگونه نام کاربری یا پسوردی در آن لحاظ نشده است.
در این روش به صورت پیش فرض از اکانت ASP.Net استفاده می‌شود. یعنی تمام برنامه‌ها محدود به یک اکانت خواهند شد.
برای تغییر این مورد دو کار را می‌توان انجام داد : استفاده از impersonation یا مطالعه قسمت بعد (ه)
توصیه: از روش impersonation به دلیل اینکه باید نام کاربری و کلمه عبور را باز هم به صورت واضحی ذکر نمود اجتناب کنید.

ه)ایجاد application pool مجزا به ازای هر برنامه ASP.Net در ویندوزهای سرور
Application pool که برای اولین بار در ویندوز سرور 2003 معرفی شده جهت ایزوله کردن برنامه‌های ASP.Net بکار برده می‌شود. به این صورت می‌شود برای هر pool یک اکانت ویندوزی مجزا تعریف کرد. حال می‌توان به این اکانت در اس کیوال سرور دسترسی داد. به این صورت برنامه‌های مختلف تحت یک اکانت واحد (یوزر asp.net) کار نکرده (می‌توانند هم کار کنند، اما امکان تعریف identity جدید برای کاربر آن در IIS‌ وجود دارد) و ضریب امنیتی بالاتری را تجربه خواهید کرد (در تکمیل روش (د))