اشتراکها
اشتراکها
بهترین ادیتور bootstrap
چندی قبل مطلب «نرمال سازی اطلاعات کاربران در حین ثبت نام» را در سایت جاری مطالعه کردید. پیاده سازی یک چنین قابلیتی به صورت توکار در ASP.NET Core Identity پیش بینی شدهاست. همچنین تمام اعتبارسنجهای نامهای کاربران، کلمات عبور آنها، ایمیلهای آنها و غیره را نیز میتوان سفارشی سازی کرد و بجای سرویسهای پیشفرض آنها معرفی و جایگزین نمود.
سفارشی سازی نرمال سازها
اگر به طراحی جداول ASP.NET Core Identity دقت کنید، تعدادی فیلد اضافی حاوی کلمهی Normalized را هم مشاهده خواهید کرد. برای مثال:
در جدول کاربران، فیلدهای Email و UserName به همراه دو فیلد اضافهی NormalizedEmail و NormalizedUserName وجود دارند.
مقدار دهی و مدیریت این فیلدهای ویژه به صورت خودکار توسط کلاسی به نام UpperInvariantLookupNormalizer صورت میگیرد:
این کلاس اینترفیس ILookupNormalizer را پیاده سازی کرده و تنها کاری را که انجام میدهد، تبدیل نام کاربر، نام نقشها و یا ایمیل کاربر به حالت upper case آن است. اما هدف اصلی از آن چیست؟
همانطور که در مطلب «نرمال سازی اطلاعات کاربران در حین ثبت نام» نیز عنوان شد، برای مثال ایمیلهای جیمیل را میتوان با چندین حالت مختلف ثبت کرد و یک کاربر به این صورت میتواند شرط یکتا بودن آدرس ایمیلهای تنظیم شدهی در کلاس IdentityServicesRegistry را دور بزند:
به همین جهت برای سفارشی سازی آن کلاس CustomNormalizer با سفارشی سازی UpperInvariantLookupNormalizer پیاده سازی شدهاست.
چون تنها یک اینترفیس ILookupNormalizer وجود دارد، باید بر اساس محتوای کلیدی که به آن ارسال میشود:
تصمیمگیری کرد که آیا ایمیل است یا خیر. چون از این نرمال کننده هم برای ایمیلها و هم برای نامها استفاده میشود. سپس میتوان منطقهای سفارشی خود مانند حذف نقطههای اضافی ایمیلها و یا حذف کاراکترهای اضافی اعمالی به نامهای کاربری را اعمال کرد.
پس از تدارک کلاس CustomNormalizer، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
یکبار CustomNormalizer را به عنوان پیاده سازی کنندهی ILookupNormalizer معرفی کردهایم. همچنین یکبار هم سرویس توکار UpperInvariantLookupNormalizer را به سرویس سفارشی خودمان هدایت کردهایم. به این ترتیب مطمئن خواهیم شد که همواره از CustomNormalizer ما استفاده خواهد شد.
بنابراین دیگر نیازی نیست تا در حین ثبتنام نسبت به تمیزسازی ایمیلها و یا نامهای کاربری اقدام کنیم. سرویس ILookupNormalizer در پشت صحنه به صورت خودکار در تمام مراحل ثبت نام و به روز رسانیها توسط ASP.NET Core Identity استفاده میشود.
سفارشی سازی UserValidator
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کاربران است که با پیاده سازی اینترفیس IUserValidator ارائه شدهاست:
این سرویس پیشفرض و توکار، تنظیمات Options.User.RequireUniqueEmail، Options.User.AllowedUserNameCharacters و امثال آنرا در مورد نامهای کاربری و ایمیلها بررسی میکند (تنظیم شدهی در متد setUserOptions کلاس IdentityServicesRegistry).
بنابراین اگر قصد تهیهی یک IUserValidator جدید را داشته باشیم، از تمام تنظیمات و بررسیهای پیش فرض سرویس توکار UserValidator فوق محروم میشویم. به همین جهت برای سفارشی سازی این سرویس، از خود کلاس UserValidator ارث بری کرده و سپس base.ValidateAsync آنرا فراخوانی میکنیم. با اینکار سبب خواهیم شد تا تمام اعتبارسنجیهای پیشفرض ASP.NET Core Identity اعمال شده و پس از آن منطقهای سفارشی اعتبارسنجی خود را که در کلاس CustomUserValidator قابل مشاهده هستند، اضافه میکنیم.
در اینجا برای مثال در متد validateEmail سفارشی تهیه شده، لیست یک سری fake email provider اضافه شدهاند (مدخل EmailsBanList در فایل appsettings.json برنامه) تا کاربران نتوانند از آنها جهت ثبتنام استفاده کنند و یا در متد validateUserName سفارشی، اگر نام کاربری برای مثال عددی وارد شده بود، یک new IdentityError بازگشت داده میشود.
پس از تدارک کلاس CustomUserValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
یکبار CustomUserValidator را به عنوان پیاده سازی کنندهی IUserValidator معرفی کردهایم. همچنین یکبار هم سرویس توکار UserValidator را به سرویس سفارشی خودمان هدایت کردهایم. به این ترتیب مطمئن خواهیم شد که همواره از CustomUserValidator ما استفاده خواهد شد (حتی اگر UserValidator اصلی از سیستم تزریق وابستگیها درخواست شود).
سفارشی سازی PasswordValidator
مراحل سفارشی سازی اعتبارسنج کلمات عبور نیز همانند تهیهی CustomUserValidator فوق است.
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کلمات عبور کاربران است که با پیاده سازی اینترفیس IPasswordValidator ارائه شدهاست:
در این کلاس، از اطلاعات متد setPasswordOptions کلاس IdentityServicesRegistry
که از فایل appsettings.json و مدخل PasswordOptions آن تامین میشود:
جهت اعتبارسنجی کلمات عبور وارد شدهی توسط کاربران در حین ثبت نام و یا به روز رسانی اطلاعات خود، استفاده میشود.
بنابراین در اینجا نیز ارائهی یک پیاده سازی خام از IPasswordValidator سبب خواهد شد تا تمام اعتبارسنجیهای توکار کلاس PasswordValidator اصلی را از دست بدهیم. به همین جهت کار را با ارث بری از همین کلاس توکار شروع کرده و ابتدا متد base.ValidateAsync آنرا فراخوانی میکنیم تا مطمئن شویم، مدخل PasswordOptions تنظیمات یاد شده، حتما پردازش خواهند شد. سپس منطق سفارشی خود را اعمال میکنیم.
برای مثال در کلاس CustomPasswordValidator تهیه شده، به مدخل PasswordsBanList فایل appsettings.json مراجعه شده و کاربران را از انتخاب کلمات عبوری به شدت ساده، منع میکند.
پس از تدارک کلاس CustomPasswordValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
یکبار CustomPasswordValidator را به عنوان پیاده سازی کنندهی IPasswordValidator معرفی کردهایم. همچنین یکبار هم سرویس توکار PasswordValidator را به سرویس سفارشی خودمان هدایت کردهایم. به این ترتیب مطمئن خواهیم شد که همواره از CustomPasswordValidator ما استفاده خواهد شد (حتی اگر PasswordValidator اصلی از سیستم تزریق وابستگیها درخواست شود).
پردازش نتایج اعتبارسنجها
این اعتبارسنجها در خروجیهای IdentityResult تمام متدهای ASP.NET Core Identity ظاهر میشوند. بنابراین فراخوانی سادهی UpdateUserAsync اشتباه است و حتما باید خروجی آنرا جهت پردازش IdentityResult آن بررسی کرد. به همین جهت تعدادی متد الحاقی به کلاس IdentityExtensions اضافه شدهاند تا کارکردن با IdentityResult را سادهتر کنند.
متد AddErrorsFromResult خطاهای حاصل از عملیات ASP.NET Core Identity را به ModelState جاری اضافه میکند. به این ترتیب میتوان این خطاها را به کاربر در Viewهای برنامه و در قسمت اعتبارسنجی مدل آن نمایش داد.
و یا متد DumpErrors تمام خطاهای موجود در IdentityResult را تبدیل به یک رشته میکند. برای مثال میتوان این رشته را در Remote validationها مورد استفاده قرار داد.
استفادهی از این متدهای الحاقی را در کنترلرهای برنامه میتوانید مشاهده کنید.
استفادهی از اعتبارسنجها جهت انجام Remote validation
اگر به RegisterController دقت کنید، اکشن متدهای ValidateUsername و ValidatePassword قابل مشاهده هستند:
این اکشن متدها توسط سرویسهای
تزریق شدهی به سازندهی کلاس، پیاده سازی شدهاند. در مورد تامین آنها و سفارشی سازی آنها نیز پیشتر بحث شد. این اینترفیسها دقیقا همان وهلههای CustomUserValidator و CustomPasswordValidator را در اختیار ما قرار میدهند. تنها کاری را که باید انجام دهیم، فراخوانی متد ValidateAsync آنها است. این متد یک خروجی از نوع IdentityResult را دارد. به همین جهت متد DumpErrors را برای پردازش این نتیجه تدارک دیدیم.
به این ترتیب کاربران در حین ثبت نام، راهنمای بهتری را جهت انتخاب کلمات عبور و نام کاربری مشاهده خواهند کرد و این بررسیها نیز Ajax ایی هستند و پیش از ارسال فرم نهایی به سرور اتفاق میافتند.
برای فعالسازی Remote validation، علاوه بر ثبت اسکریپتهای Ajax ایی، خواص کلاس RegisterViewModel نیز از ویژگی Remote استفاده میکنند:
یک نکته: برای اینکه Remote Validation را به همراه ValidateAntiForgeryToken استفاده کنیم، تنها کافی است نام فیلد مخفی آنرا به لیست AdditionalFields به نحوی که مشاهده میکنید، اضافه کنیم.
کدهای کامل این سری را در مخزن کد DNT Identity میتوانید ملاحظه کنید.
سفارشی سازی نرمال سازها
اگر به طراحی جداول ASP.NET Core Identity دقت کنید، تعدادی فیلد اضافی حاوی کلمهی Normalized را هم مشاهده خواهید کرد. برای مثال:
در جدول کاربران، فیلدهای Email و UserName به همراه دو فیلد اضافهی NormalizedEmail و NormalizedUserName وجود دارند.
مقدار دهی و مدیریت این فیلدهای ویژه به صورت خودکار توسط کلاسی به نام UpperInvariantLookupNormalizer صورت میگیرد:
public class UpperInvariantLookupNormalizer : ILookupNormalizer
همانطور که در مطلب «نرمال سازی اطلاعات کاربران در حین ثبت نام» نیز عنوان شد، برای مثال ایمیلهای جیمیل را میتوان با چندین حالت مختلف ثبت کرد و یک کاربر به این صورت میتواند شرط یکتا بودن آدرس ایمیلهای تنظیم شدهی در کلاس IdentityServicesRegistry را دور بزند:
identityOptionsUser.RequireUniqueEmail = true;
چون تنها یک اینترفیس ILookupNormalizer وجود دارد، باید بر اساس محتوای کلیدی که به آن ارسال میشود:
public override string Normalize(string key)
پس از تدارک کلاس CustomNormalizer، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
services.AddScoped<ILookupNormalizer, CustomNormalizer>(); services.AddScoped<UpperInvariantLookupNormalizer, CustomNormalizer>();
بنابراین دیگر نیازی نیست تا در حین ثبتنام نسبت به تمیزسازی ایمیلها و یا نامهای کاربری اقدام کنیم. سرویس ILookupNormalizer در پشت صحنه به صورت خودکار در تمام مراحل ثبت نام و به روز رسانیها توسط ASP.NET Core Identity استفاده میشود.
سفارشی سازی UserValidator
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کاربران است که با پیاده سازی اینترفیس IUserValidator ارائه شدهاست:
public class UserValidator<TUser> : IUserValidator<TUser> where TUser : class
بنابراین اگر قصد تهیهی یک IUserValidator جدید را داشته باشیم، از تمام تنظیمات و بررسیهای پیش فرض سرویس توکار UserValidator فوق محروم میشویم. به همین جهت برای سفارشی سازی این سرویس، از خود کلاس UserValidator ارث بری کرده و سپس base.ValidateAsync آنرا فراخوانی میکنیم. با اینکار سبب خواهیم شد تا تمام اعتبارسنجیهای پیشفرض ASP.NET Core Identity اعمال شده و پس از آن منطقهای سفارشی اعتبارسنجی خود را که در کلاس CustomUserValidator قابل مشاهده هستند، اضافه میکنیم.
public override async Task<IdentityResult> ValidateAsync(UserManager<User> manager, User user) { // First use the built-in validator var result = await base.ValidateAsync(manager, user).ConfigureAwait(false); var errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList(); // Extending the built-in validator validateEmail(user, errors); validateUserName(user, errors); return !errors.Any() ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray()); }
پس از تدارک کلاس CustomUserValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
services.AddScoped<IUserValidator<User>, CustomUserValidator>(); services.AddScoped<UserValidator<User>, CustomUserValidator>();
سفارشی سازی PasswordValidator
مراحل سفارشی سازی اعتبارسنج کلمات عبور نیز همانند تهیهی CustomUserValidator فوق است.
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کلمات عبور کاربران است که با پیاده سازی اینترفیس IPasswordValidator ارائه شدهاست:
public class PasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
private static void setPasswordOptions(PasswordOptions identityOptionsPassword, SiteSettings siteSettings) { identityOptionsPassword.RequireDigit = siteSettings.PasswordOptions.RequireDigit; identityOptionsPassword.RequireLowercase = siteSettings.PasswordOptions.RequireLowercase; identityOptionsPassword.RequireNonAlphanumeric = siteSettings.PasswordOptions.RequireNonAlphanumeric; identityOptionsPassword.RequireUppercase = siteSettings.PasswordOptions.RequireUppercase; identityOptionsPassword.RequiredLength = siteSettings.PasswordOptions.RequiredLength; }
"PasswordOptions": { "RequireDigit": false, "RequiredLength": 6, "RequireLowercase": false, "RequireNonAlphanumeric": false, "RequireUppercase": false },
بنابراین در اینجا نیز ارائهی یک پیاده سازی خام از IPasswordValidator سبب خواهد شد تا تمام اعتبارسنجیهای توکار کلاس PasswordValidator اصلی را از دست بدهیم. به همین جهت کار را با ارث بری از همین کلاس توکار شروع کرده و ابتدا متد base.ValidateAsync آنرا فراخوانی میکنیم تا مطمئن شویم، مدخل PasswordOptions تنظیمات یاد شده، حتما پردازش خواهند شد. سپس منطق سفارشی خود را اعمال میکنیم.
برای مثال در کلاس CustomPasswordValidator تهیه شده، به مدخل PasswordsBanList فایل appsettings.json مراجعه شده و کاربران را از انتخاب کلمات عبوری به شدت ساده، منع میکند.
پس از تدارک کلاس CustomPasswordValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
services.AddScoped<IPasswordValidator<User>, CustomPasswordValidator>(); services.AddScoped<PasswordValidator<User>, CustomPasswordValidator>();
پردازش نتایج اعتبارسنجها
این اعتبارسنجها در خروجیهای IdentityResult تمام متدهای ASP.NET Core Identity ظاهر میشوند. بنابراین فراخوانی سادهی UpdateUserAsync اشتباه است و حتما باید خروجی آنرا جهت پردازش IdentityResult آن بررسی کرد. به همین جهت تعدادی متد الحاقی به کلاس IdentityExtensions اضافه شدهاند تا کارکردن با IdentityResult را سادهتر کنند.
public static void AddErrorsFromResult(this ModelStateDictionary modelStat, IdentityResult result)
public static string DumpErrors(this IdentityResult result, bool useHtmlNewLine = false)
استفادهی از این متدهای الحاقی را در کنترلرهای برنامه میتوانید مشاهده کنید.
استفادهی از اعتبارسنجها جهت انجام Remote validation
اگر به RegisterController دقت کنید، اکشن متدهای ValidateUsername و ValidatePassword قابل مشاهده هستند:
[AjaxOnly, HttpPost, ValidateAntiForgeryToken] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public async Task<IActionResult> ValidateUsername(string username, string email)
[AjaxOnly, HttpPost, ValidateAntiForgeryToken] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public async Task<IActionResult> ValidatePassword(string password, string username)
IPasswordValidator<User> passwordValidator, IUserValidator<User> userValidator,
به این ترتیب کاربران در حین ثبت نام، راهنمای بهتری را جهت انتخاب کلمات عبور و نام کاربری مشاهده خواهند کرد و این بررسیها نیز Ajax ایی هستند و پیش از ارسال فرم نهایی به سرور اتفاق میافتند.
برای فعالسازی Remote validation، علاوه بر ثبت اسکریپتهای Ajax ایی، خواص کلاس RegisterViewModel نیز از ویژگی Remote استفاده میکنند:
[Required(ErrorMessage = "(*)")] [Display(Name = "نام کاربری")] [Remote("ValidateUsername", "Register", AdditionalFields = nameof(Email) + "," + ViewModelConstants.AntiForgeryToken, HttpMethod = "POST")] [RegularExpression("^[a-zA-Z_]*$", ErrorMessage = "لطفا تنها از حروف انگلیسی استفاده نمائید")] public string Username { get; set; }
یک نکته: برای اینکه Remote Validation را به همراه ValidateAntiForgeryToken استفاده کنیم، تنها کافی است نام فیلد مخفی آنرا به لیست AdditionalFields به نحوی که مشاهده میکنید، اضافه کنیم.
کدهای کامل این سری را در مخزن کد DNT Identity میتوانید ملاحظه کنید.
مطالب
ASP.NET MVC #19
مروری بر امکانات Caching اطلاعات در ASP.NET MVC
در برنامههای وب، بالاترین حد کارآیی برنامهها از طریق بهینه سازی الگوریتمها حاصل نمیشود، بلکه با بکارگیری امکانات Caching سبب خواهیم شد تا اصلا کدی اجرا نشود. در ASP.NET MVC این هدف از طریق بکارگیری فیلتری به نام OutputCache میسر میگردد:
using System.Web.Mvc;
namespace MvcApplication16.Controllers
{
public class HomeController : Controller
{
[OutputCache(Duration = 60, VaryByParam = "none")]
public ActionResult Index()
{
return View();
}
}
}
همانطور که ملاحظه میکنید، OutputCache را به یک اکشن متد یا حتی به یک کنترلر نیز میتوان اعمال کرد. به این ترتیب HTML نهایی حاصل از View متناظر با اکشن متد جاری فراخوانی شده، Cache خواهد شد. سپس زمانیکه درخواست بعدی به سرور ارسال میشود، نتیجه دریافت شده، همان اطلاعات Cache شده قبلی است و عملا در سمت سرور کدی اجرا نخواهد شد. در اینجا توسط پارامتر Duration، مدت زمان معتبر بودن کش حاصل، برحسب ثانیه مشخص میشود. VaryByParam مشخص میکند که اگر متدی پارامتری را دریافت میکند، آیا باید به ازای هر مقدار دریافتی، مقادیر کش شده متفاوتی ذخیره شوند یا خیر. در اینجا چون متد Index پارامتری ندارد، از مقدار none استفاده شده است.
مثال یک
یک پروژه جدید خالی ASP.NET MVC را آغاز کنید. سپس کنترلر جدید Home را نیز به آن اضافه نمائید:
using System;
using System.Web.Mvc;
namespace MvcApplication16.Controllers
{
public class HomeController : Controller
{
[OutputCache(Duration = 60, VaryByParam = "none")]
public ActionResult Index()
{
ViewBag.ControllerTime = DateTime.Now;
return View();
}
}
}
همچنین کدهای View متد Index را نیز به نحو زیر تغییر دهید:
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>@ViewBag.ControllerTime</p>
<p>@DateTime.Now</p>
در اینجا نمایش دو زمان دریافتی از کنترلر و زمان محاسبه شده در View را مشاهده میکنید. هدف این است که بررسی کنیم آیا فیلتر OutputCache بر روی این دو مقدار تاثیری دارد یا خیر.
برنامه را اجرا نمائید. سپس چند بار صفحه را Refresh کنید. مشاهده خواهید کرد که هر دو زمان یاد شده تا 60 ثانیه، تغییری نخواهند کرد و حاصل نهایی از Cache خواهنده میشود.
کاربرد یک چنین حالتی برای مثال نمایش اطلاعات بازدیدهای یک سایت است. نباید به ازای هر کاربر وارد شده به سایت، یکبار به بانک اطلاعاتی مراجعه کرد و آمار جدیدی را تهیه نمود. یا برای نمونه اگر جایی قرار است اطلاعات وضعیت آب و هوا نمایش داده شود، بهتر است این اطلاعات، مثلا هر نیم ساعت یکبار به روز شود و نه به ازای هر بازدید جدید از سایت، توسط صدها بازدید کننده همزمان. یا برای مثال کش کردن خروجی فید RSS یک بلاگ به مدت چند ساعت نیز ایده خوبی است. از این لحاظ که اگر اطلاعات بلاگ شما روزی یکبار به روز میشود، نیازی نیست تا به ازای هر برنامه فیدخوان، یکبار اطلاعات از بانک اطلاعاتی دریافت شده و پروسه رندر نهایی فید صورت گیرد. منوهای پویای یک سایت نیز در همین رده قرار میگیرند. دریافت اطلاعات منوهای پویای سایت به ازای هر درخواست رسیده کاربری جدید، کار اشتباهی است. این اطلاعات نیز باید کش شوند تا بار سرور کاهش یابد. البته تمام اینها زمانی میسر خواهند شد که اطلاعات سمت سرور کش شوند.
مثال دو
همان مثال قبلی را در اینجا جهت بررسی پارامتر VaryByParam به نحو زیر تغییر میدهیم:
using System;
using System.Web.Mvc;
namespace MvcApplication16.Controllers
{
public class HomeController : Controller
{
[OutputCache(Duration = 60, VaryByParam = "none")]
public ActionResult Index(string parameter)
{
ViewBag.Msg = parameter ?? string.Empty;
ViewBag.ControllerTime = DateTime.Now;
return View();
}
}
}
در اینجا یک پارامتر به متد Index اضافه شده است. مقدار آن به ViewBag.Msg انتساب داده شده و سپس در View ، در بین تگهای h2 نمایش داده خواهد شد. همچنین یک فرم ساده هم جهت ارسال parameter به متد Index اضافه شده است:
@{
ViewBag.Title = "Index";
}
<h2>@ViewBag.Msg</h2>
<p>@ViewBag.ControllerTime</p>
<p>@DateTime.Now</p>
@using (Html.BeginForm())
{
@Html.TextBox("parameter")
<input type="submit" />
}
اکنون برنامه را اجرا کنید. در TextBox نمایش داده شده یکبار مثلا بنویسید Test1 و فرم را به سرور ارسال نمائید. سپس مقدار Test2 را وارد کرده و ارسال نمائید. در بار دوم، خروجی صفحه همانند زمانی است که مقدار Test1 ارسال شده است. علت این است که مقدار VaryByParam به none تنظیم شده است و صرفنظر از ورودی کاربر، همان اطلاعات کش شده قبلی بازگشت داده خواهد شد. برای رفع این مشکل، متد Index را به نحو زیر تغییر دهید، به طوریکه مقدار VaryByParam به نام پارامتر متد جاری اشاره کند:
[OutputCache(Duration = 60, VaryByParam = "parameter")]
public ActionResult Index(string parameter)
در ادامه مجددا برنامه را اجرا کنید. اکنون یکبار مقدار Test1 را به سرور ارسال کنید. سپس مقدار Test2 را ارسال نمائید. مجددا همین دو مرحله را با مقادیر Test1 و Test2 تکرار کنید. مشاهده خواهید کرد که اینبار اطلاعات بر اساس مقدار پارامتر ارسالی کش شده است.
تنظیمات متفاوت OutputCache
الف) VaryByParam : اگر مساوی none قرار گیرد، همواره همان مقدار کش شده قبلی نمایش داده میشود. اگر مقدار آن به نام پارامتر خاصی تنظیم شود، اطلاعات کش شده بر اساس مقادیر متفاوت پارامتر دریافتی، متفاوت خواهند بود. در اینجا پارامترهای متفاوت را با یک «,» میتوان از هم جدا ساخت. اگر تعداد پارامترها زیاد است میتوان مقدار VaryByParam را مساوی با * قرار داد. در این حالت به ازای مقادیر متفاوت دریافتی پارامترهای مختلف، اطلاعات مجزایی در کش قرار خواهد گرفت. این روش آخر آنچنان توصیه نمیشود چون سربار بالایی دارد و حجم بالایی از اطلاعات بر اساس پارامترهای مختلف، باید در کش قرار گیرند.
ب) Location : مکان قرارگیری اطلاعات کش شده را مشخص میکند. مقدار آن نیز بر اساس یک enum به نام OutputCacheLocation مشخص میگردد. در این حالت برای مثال میتوان مکانهای Server، Client و ServerAndClient را مقدار دهی نمود. مقدار Downstream به معنای کش شدن اطلاعات بر روی پروکسی سرورهای بین راه و یا مرورگرها است. پیش فرض آن Any است که ترکیبی از Server و Downstream میباشد.
اگر قرار است اطلاعات یکسانی به تمام کاربران نمایش داده شود، مثلا محتوای لیست یک منوی پویا، محل قرارگیری اطلاعات کش باید سمت سرور باشد. اگر نیاز است به ازای هر کاربر محتوای اطلاعات کش شده متفاوت باشد، بهتر است محل سمت کلاینت را مقدار دهی نمود.
ج) VaryByHeader : اطلاعات، بر اساس هدرهای مشخص شده، کش میشوند. برای مثال مرسوم است که از Accept-Language در اینجا استفاده شود تا اطلاعات مثلا فرانسوی کش شده، به کاربر آلمانی تحویل داده نشود.
د) VaryByCustom : در این حالت نام یک متد استاتیک تعریف شده در فایل global.asax.cs باید مشخص گردد. توسط این متد کلید رشتهای اطلاعاتی که قرار است کش شود، بازگشت داده خواهد شد.
ه) SqlDependency : در این حالت اطلاعات تا زمانیکه تغییری در جداول بانک اطلاعاتی SQL Server صورت نگیرد، کش خواهد شد.
و) Nostore : به پروکسی سرورهای بین راه و همچنین مرورگرها اطلاع میدهد که اطلاعات را نباید کش کنند. اگر قسمت اعتبار سنجی این سری را به خاطر داشته باشید، چنین تعریفی در قسمت Remote validation بکارگرفته شد:
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
و یا میتوان برای اینکار یک فیلتر سفارشی را نیز تهیه کرد:
using System;
using System.Web.Mvc;
namespace MvcApplication16.Helper
{
/// <summary>
/// Adds "Cache-Control: private, max-age=0" header,
/// ensuring that the responses are not cached by the user's browser.
/// </summary>
public class NoCachingAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
filterContext.HttpContext.Response.CacheControl = "private";
filterContext.HttpContext.Response.Cache.SetMaxAge(TimeSpan.FromSeconds(0));
}
}
}
کار این فیلتر اضافه کردن هدر «Cache-Control: private, max-age=0» به Response است.
استفاده از فایل Web.Config برای معرفی تنظیمات Caching
یکی دیگر از تنظیمات ویژگی OutputCache، پارامتر CacheProfile است که امکان تنظیم آن در فایل web.config نیز وجود دارد. برای نمونه تنظیمات زیر را به قسمت system.web فایل وب کانفیگ برنامه اضافه کنید:
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Aggressive" location="ServerAndClient" duration="300"/>
<add name="Mild" duration="100" location="Server" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
سپس مثلا برای استفاده از پروفایلی به نام Aggressive، خواهیم داشت:
[OutputCache(CacheProfile = "Aggressive", VaryByParam = "parameter")]
public ActionResult Index(string parameter)
استفاده از ویژگی به نام donut caching
تا اینجا به این نتیجه رسیدیم که OutputCache، کل خروجی یک View را بر اساس پارامترهای مختلفی که دریافت میکند، کش خواهد کرد. در این بین اگر بخواهیم تنها قسمت کوچکی از صفحه کش نشود چه باید کرد؟ برای حل این مشکل قابلیتی به نام cache substitution که به donut caching هم معروف است (چون آنرا میتوان به شکل یک donut تصور کرد!) در ASP.NET MVC قابل استفاده است.
@{ Response.WriteSubstitution(ctx => DateTime.Now.ToShortTimeString()); }
همانطور که ملاحظه میکنید برای تعریف یک چنین اطلاعاتی باید از متد Response.WriteSubstitution در یک view استفاده کرد. در این مثال، نمایش زمان جاری معرفی شده، صرف نظر از وضعیت کش صفحه جاری، کش نخواهد شد.
عکس آن هم ممکن است. فرض کنید که صفحه جاری شما از سه partial view تشکیل شده است. هر کدام از این partial viewها نیز مزین به OutpuCache هستند. اما صفحه اصلی درج کننده اطلاعات این سه partial view فاقد ویژگی Output کش است. در این حالت تنها اطلاعات این partial viewها کش خواهند شد و سایر قسمتهای صفحه با هر بار درخواست از سرور، مجددا بر اساس اطلاعات جدید به روز خواهند شد. حالت توصیه شده نیز همین مورد است و متد Response.WriteSubstitution را صرفا جهت اطلاعات عمومی درنظر داشته باشید.
استفاده از امکانات Data Caching به صورت مستقیم
مطالبی که تا اینجا عنوان شدند به کش کردن اطلاعات Response اختصاص داشتند. اما امکانات Caching موجود، به این مورد خلاصه نشده و میتوان اطلاعات و اشیاء را نیز کش کرد. برای مثال اطلاعات «با سطح دسترسی عمومی» دریافتی از بانک اطلاعاتی توسط یک کوئری را نیز میتوان کش کرد. جهت انجام اینکار میتوان از متدهای HttpRuntime.Cache.Insert و یا HttpContext.Cache.Insert استفاده کرد. استفاده از HttpContext.Cache.Insert حین نوشتن Unit tests دردسر کمتری دارد و mocking آن ساده است؛ از این جهت که بر اساس HttpContextBase تعریف شدهاست.
در ادامه یک کلاس کمکی نوشتن اطلاعات در cache و سپس بازیابی آنرا ملاحظه میکنید:
using System;
using System.Web;
using System.Web.Caching;
namespace MvcApplication16.Helper
{
public static class CacheManager
{
public static void CacheInsert(this HttpContextBase httpContext, string key, object data, int durationMinutes)
{
if (data == null) return;
httpContext.Cache.Add(
key,
data,
null,
DateTime.Now.AddMinutes(durationMinutes),
TimeSpan.Zero,
CacheItemPriority.AboveNormal,
null);
}
public static T CacheRead<T>(this HttpContextBase httpContext, string key)
{
var data = httpContext.Cache[key];
if (data != null)
return (T)data;
return default(T);
}
public static void InvalidateCache(this HttpContextBase httpContext, string key)
{
httpContext.Cache.Remove(key);
}
}
}
و برای استفاده از آن در یک اکشن متد، ابتدا نیاز است فضای نام این کلاس تعریف شود و سپس برای نمونه متد HttpContext.CacheInsert در دسترس خواهد بود. HttpContext یکی از خواص تعریف شده در شیء کنترلر است که با ارث بری کنترلرها از آن، همواره در دسترس میباشد.
در اینجا برای نمونه اطلاعات یک لیست جنریک دریافتی از بانک اطلاعاتی را مثلا 10 دقیقه (بسته به پارامتر durationMinutes آن) میتوان کش کرد و سپس توسط متد CacheRead آنرا دریافت نمود. اگر متد CacheRead نال برگرداند به معنای خالی بودن کش است. بنابراین یکبار اطلاعات را از بانک اطلاعاتی دریافت نموده و سپس آنرا کش خواهیم کردیم.
البته هستند ORMهایی که یک چنین کارهایی را به صورت توکار پشتیبانی کنند. به مکانیزم آن، Second level cache هم گفته میشود؛ به علاوه امکان استفاده از پروایدرهای دیگری را بجز کش IIS برای ذخیره سازی موقتی اطلاعات نیز فراهم میکنند.
همچنین باید دقت داشت این اعداد مدت زمان، هیچگونه ضمانتی ندارند. اگر IIS احساس کند که با کمبود منابع مواجه شده است، به سادگی شروع به حذف اطلاعات موجود در کش خواهد کرد.
نکته امنیتی مهم!
به هیچ عنوان از OutputCache در صفحاتی که نیاز به اعتبار سنجی دارند، استفاده نکنید و به همین جهت در قسمت کش کردن اطلاعات، بر روی «اطلاعاتی با سطح دسترسی عمومی» تاکید شد.
فرض کنید کارمندی به صفحه مشاهده فیش حقوقی خودش مراجعه کرده است. این ماه هم اضافه حقوق آنچنانی داشته است. شما هم این صفحه را به مدت سه ساعت کش کردهاید. آیا میتوانید تصور کنید اگر همین گزارش کش شده با این اطلاعات، به سایر کارمندان نمایش داده شود چه قشقرقی به پا خواهد شد؟!
بنابراین هیچگاه اطلاعات مخصوص به یک کاربر اعتبار سنجی شده را کش نکنید و «تنها» اطلاعاتی نیاز به کش شدن دارند که عمومی باشند. برای مثال لیست آخرین اخبار سایت؛ لیست آخرین مدخلهای فید RSS سایت؛ لیست اطلاعات منوی عمومی سایت؛ لیست تعداد کاربران مراجعه کننده به سایت در طول یک روز؛ گزارش آب و هوا و کلیه اطلاعاتی با سطح دسترسی عمومی که کش شدن آنها مشکل ساز نباشد.
به صورت خلاصه هیچگاه در کدهای شما چنین تعریفی نباید مشاهده شود:
[Authorize]
[OutputCache(Duration = 60)]
public ActionResult Index()
جناب نصیری با تشکر از مطلبتون.ی سوال داشتم اینکه استفاده از فرم ایجکس (Ajax.BeginForm ) موجود در Razor بهتر و راحتر نیست؟یا اینکه نسبت به jquery محدودیت خاصی داره؟
- نیاز به vs2012 داره.
- نمیشه یک فرم رو strongly typed تعریف کرد مثل مثال بالا:
- نمیشه یک فرم رو strongly typed تعریف کرد مثل مثال بالا:
@model IEnumerable<KhazarCo.Models.Type>
بازخوردهای پروژهها
استفاده از یک فرم از پیش طراحی شده به عنوان Background
با عرض سلام و خسته نباشید
روشی وجود داره که از یک فرم کاغذی اسکن تهیه کرده و از آن به عنوان background استفاده کرده و فیلدهای درون آن را به کمک این برنامه پر کرده و یک نسخه جدید پرینت نمود؟
برای مطالعهی این مقاله شما باید به مواردی از قبیل کتابخانهی AngularJs، تعاملات بین کلاینت و سرور و همچنین معماری RESTful تسلط کافی داشته باشید و ما از توضیح و تفصیلی این سرفصلها اجتناب میکنیم.
همانطور که ملاحظه میکنید تمام پروسهی دریافت، در یک خط خلاصه شده است. همچنین میبینید که restAngular دارای Promise نیز هست. در تکه کد اول، تمامی userها به صورت Get دریافت میشوند. در تکه کد دوم مشاهده میکنید که عملیات درخواست لیست ماشینهای کاربر چگونه در یک خط خلاصه میگردد. حال فرض کنید قصد داشتیم تا این عملیات را با وابستگی http پیاده سازی نماییم. برای این کار باید چندین خط کد را درون یک سرویس جا میدادیم و آنگاه از متد سرویس در کنترلر استفاده میکردیم.
توجه داشته باشید هنگامیکه یک وابستگی به module اضافه میگردد، با حروف کوچک یعنی restangular اضافه میگردد؛ اما هنگامیکه به کنترلر اضافه میشود، به صورت Restangular و با R بزرگ اضافه میگردد.
در این قسمت قصد داشتیم تا شما را با این کتابخانه کمی آشنا کنیم. در قسمت بعدی سعی میکنیم تا با مثالهایی کاربردی، شما را بیشتر با این سرویس خارق العاده آشنا کنیم.
خیلی خوب بپردازیم به اصل مطلب:
Restangular چیست؟
کتابخانه RestAngular بنا به گفته ناشر در مستندات Github آن، یک سرویس توسعه داده شده AngularJs میباشد که کدهای نوشته شدهی برای پیاده سازی فرآیندهای Request/Response کلاینت و سرور را به حداقل رسانده است. این فرآیندها در قالب درخواستهای GET، POST، DELETE و Update صورت میپذیرند. این سرویس برای وب اپلیکیشنهایی که که دادههای خود را از APIهای RESTful همانند Microsoft ASP.NET Web API 2 دریافت میکنند نیز بسیار کارآمد است.
کد زیر یک فرآیند درخواست GET را نمایش میدهد که در آن از سرویس RestAngular استفاده شده است:
// Restangular returns promises Restangular.all('users').getList() // GET: /users .then(function(users) { // returns a list of users $scope.user = users[0]; // first Restangular obj in list: { id: 123 } }) // Later in the code... // Restangular objects are self-aware and know how to make their own RESTful requests $scope.user.getList('cars'); // GET: /users/123/cars // You can also use your own custom methods on Restangular objects $scope.user.sendMessage(); // POST: /users/123/sendMessage // Chain methods together to easily build complex requests $scope.user.one('messages', 123).one('from', 123).getList('unread'); // GET: /users/123/messages/123/from/123/unread
در اینجا همچنین قادرید درخواستهای خود را به صورت سفارشی نیز اعمال کنید (تکه کد سوم) و در آخر مشاهده میکنید که پیچیدهترین عملیاتهای درخواست کاربر را میتوان در یک خط خلاصه نمود. برای نمونه کد آخر یک دستور GET تو در تو را به چه سادگی پیاده سازی کرده است.
وابستگی ها
شروع پروژه
برای شروع پروژه و استفاده از RestAngular، پس از اضافه کردن اسکریپت رفرنسها و وابستگیها (lodash و AngularJs) باید وابستگی restAngular را به صورت زیر به angular.module اضافه نمایید:
// Add Restangular as a dependency to your app angular.module('your-app', ['restangular']); // Inject Restangular into your controller angular.module('your-app').controller('MainCtrl', function($scope, Restangular) { // ... });
برخی از متدهای RestAngular
در این بخش قصد داریم تا برخی از پر کاربردترین متدهای RestAngular را تشریح کنیم:
نام متد | پارامترهای ارسالی | توضیحات |
one | route, id | این متد یک RestAngular object ایجاد میکند که از آدرسی که در route قرار داده شده با id مشخص دریافت میشود. |
all | route | این متد یک RestAngular object که لیستی از المنت هایی را که در آدرس route قرار دارد، دریافت مینماید. |
oneUrl | route, url | این متد یک RestAngular object ایجاد میکند که یک المنت از url خاصی را بازگشت میدهد. مانند: Restangular.oneUrl( 'googlers' , 'http://www.google.com/1' ).get(); |
allUrl | route, url | این متد مانند متد قبل است با این تفاوت که یک مجموعه را بازگشت میدهد. |
copy | formElement | این متد یک کپی از المنتهای یک فرم را ایجاد میکند که ما میتوانیم آنها را تغییر دهیم. |
restangularizeElement | parent,element, route, queryParams | یک المنت جدید را به صورت Restangularize تغییر میدهد. |
restangularizeCollection | parent, element, route, queryParams | یک کالکشن Collection را به صورت Restangularize تغییر میدهد. |
نظرات اشتراکها
دایرکتوری فونتهای فارسی
دوستان عزیز اگر در سایت آوب فونت ، فونت مورد نظر خود را انتخاب نمودید در قسمت نسخه تحت وب وارد شوید برای مثال فونت یکان
http://awebfont.ir/fonts?cat_id=1&fonts_id=1116#font-face
برای استفاده از این فونت در وب سایت شما 2 متد مختلف وجود دارد
http://awebfont.ir/fonts?cat_id=1&fonts_id=1116#font-face
برای استفاده از این فونت در وب سایت شما 2 متد مختلف وجود دارد
- دریافت کد استایل خودکار در این متد وب سایت آوب فونت با توجه به مرورگر بیننده صفحه مناسبترین و کم حجمترین فرمت را انتخاب میکنید
- کد استایل به صورت دستی در این روش شما خود براساس نیازخود فرمتهای فونت نظر خود را انتخاب میکنید
لطفا چک فرمائید
نظرات مطالب
خلاصه اشتراکهای روز پنج شنبه 12 آبان 1390
سلام؛ نسخهultimate خود ویژوال استودیو ابزار stress/load test رو به صورت توکار داره: (^)
پایین صفحه لینکی که معرفی کردم، یک سری ویدیو هم برای آموزش آن معرفی کرده.
پایین صفحه لینکی که معرفی کردم، یک سری ویدیو هم برای آموزش آن معرفی کرده.