مطالب
استفاده از Fluent Validation در برنامه‌های ASP.NET Core - قسمت چهارم - اعتبارسنجی Async سمت کلاینت و یا همان Remote Client Side Validation
در قسمت قبل با نحوه‌ی پیاده سازی اعتبارسنجی‌های سفارشی سمت کلاینت مخصوص کتابخانه‌ی Fluent Validation آشنا شدیم. در این قسمت، یک حالت خاص همان نوع اعتبارسنجی‌های سمت کلاینت را که remote validation نام دارد، بررسی می‌کنیم. در این حالت خاص، نیازی به کدنویسی جاوااسکریپتی خاصی نیست. چون زیرساخت آن به همراه unobtrusive jQuery Ajax خود ASP.NET Core ارائه می‌شود. در اینجا فقط نیاز است تا متادیتای خاص آن‌را تولید کنیم. به عبارتی اینبار هدف ما تنها تولید یک چنین تگ HTML ای است:
<input dir="ltr" class="form-control input-validation-error" 
type="email" 
data-val="true" 
data-val-email="'آدرس ایمیل' is not a valid email address." 
data-val-remote="این آدرس ایمیل هم اکنون مورد استفاده‌است" 
data-val-remote-url="/Home/ValidateUniqueEmail" 
data-val-required="'آدرس ایمیل' must not be empty." 
id="Email" 
name="Email" 
>
که به همراه ویژگی‌های data-val ، data-val-remote و data-val-remote-url است. همینقدر که این سه ویژگی وجود داشته باشند، مابقی منطق اعتبارسنجی سمت کلاینت آن توسط unobtrusive jQuery Ajax (ارسال خودکار Ajax ای مقدار ایمیل، به سمت سرور و دریافت پاسخ) و unobtrusive java script validation مدیریت خواهند شد.


افزودن آدرس ایمیل به مدل کاربران

به همان مدل قسمت قبل، قصد داریم خاصیت آدرس ایمیل را هم اضافه کنیم:
using System.ComponentModel.DataAnnotations;

namespace FluentValidationSample.Models
{
    public class UserModel
    {
        [Display(Name = "نام کاربری")]
        public string Username { get; set; }

        [Display(Name = "سن")]
        public int Age { get; set; }

        [Display(Name = "سابقه کار")]
        public int Experience { get; set; }

        [DataType(DataType.EmailAddress)]
        [Display(Name = "آدرس ایمیل")]
        public string Email { get; set; }
    }
}


ایجاد سرویسی برای بررسی منحصربفرد بودن آدرس ایمیل

در ادامه قصد داریم سرویسی را ایجاد کنیم که برای مثال با بانک اطلاعاتی ارتباط برقرار کرده (در اینجا جهت سهولت ارائه، از یک آرایه استفاده شده‌است) و مشخص می‌کند که آیا ایمیل دریافتی پیشتر استفاده شده‌است یا خیر:
using System.Linq;

namespace FluentValidationSample.Services
{
    public interface IUsersService
    {
        bool IsUniqueEmail(string emailAddress);
    }

    public class UsersService : IUsersService
    {
        public bool IsUniqueEmail(string emailAddress)
        {
            string[] registedEmails = {
                "email@site.com",
                "test@gmail.com"
            };
            return !registedEmails.Contains(emailAddress);
        }
    }
}
این سرویس را هم با طول عمر Scoped به برنامه معرفی می‌کنیم؛ چون طول عمر سرویس‌هایی که با بانک اطلاعاتی و DbContext کار می‌کنند، به همین نحو تعیین می‌شوند:
namespace FluentValidationSample.Web
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IUsersService, UsersService>();


ایجاد اعتبارسنج سمت سرور بررسی منحصربفرد بودن آدرس ایمیل

در ادامه یک PropertyValidator جدید را ایجاد می‌کنیم تا بتوان توسط آن مقدار ایمیل دریافتی را در سمت سرور، تعیین اعتبار کرد:
using FluentValidation.Validators;
using FluentValidationSample.Services;

namespace FluentValidationSample.ModelsValidations
{
    public class UniqueEmailValidator : PropertyValidator
    {
        private readonly IUsersService _usersService;

        public UniqueEmailValidator(IUsersService usersService)
                : base("این آدرس ایمیل هم اکنون مورد استفاده‌است")
        {
            _usersService = usersService;
        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            return context.PropertyValue != null &&
                    _usersService.IsUniqueEmail((string)context.PropertyValue);
        }
    }
}
همانطور که مشاهده می‌کنید، در اینجا تزریق سرویس سفارشی IUsersService به سازنده‌ی کلاس اعتبارسنج، مجاز است و بدون مشکل کار می‌کند.
پس از آن جهت سهولت استفاده‌ی از آن، یک متد الحاقی جدید را نیز به نام UniqueEmail به نحو زیر تعریف می‌کنیم:
using FluentValidation;
using FluentValidationSample.Services;

namespace FluentValidationSample.ModelsValidations
{
    public static class CustomValidatorExtensions
    {
        public static IRuleBuilderOptions<T, string> UniqueEmail<T>(
            this IRuleBuilder<T, string> ruleBuilder, IUsersService usersService)
        {
            return ruleBuilder.SetValidator(new UniqueEmailValidator(usersService));
        }
    }
}

یک نکته: اگر دقت کرده باشید، فضای نام این اعتبارسنج در این قسمت FluentValidationSample.ModelsValidations شده‌است:


علت اینجا است که اعتبارسنج تعریف شده نیاز دارد هم از مدل‌ها استفاده کند و هم از سرویس کاربران. سرویس کاربران هم از مدل‌ها استفاده می‌کند. به همین جهت اگر تعاریف اعتبارسنجی را داخل پروژه‌ی مدل‌ها قرار دهیم، یک وابستگی حلقوی رخ خواهد داد (وابستگی مدل‌ها به سرویس‌ها و برعکس). بنابراین بهتر است اعتبارسنج‌ها را به یک پروژه‌ی مجزا منتقل کنیم تا از بروز این cyclic dependency جلوگیری شود.


اعمال اعتبارسنجی منحصربفرد بودن ایمیل دریافتی به اعتبارسنج UserModel

پس از تهیه‌ی متد الحاقی UniqueEmail، آن‌را به RuleFor مخصوص خاصیت ایمیل اضافه می‌کنیم. در اینجا نیز تزریق وابستگی سرویس سفارشی IUsersService به سازنده‌ی کلاس اعتبارسنج مجاز است:
using FluentValidation;
using FluentValidationSample.Models;
using FluentValidationSample.Services;

namespace FluentValidationSample.ModelsValidations
{
    public class UserModelValidator : AbstractValidator<UserModel>
    {
        public UserModelValidator(IUsersService usersService)
        {
            RuleFor(x => x.Username).NotNull();
            RuleFor(x => x.Age).NotNull();
            RuleFor(x => x.Experience).LowerThan(nameof(UserModel.Age)).NotNull();
            RuleFor(x => x.Email).EmailAddress().NotNull().UniqueEmail(usersService);
        }
    }
}


ایجاد متادیتای مورد نیاز جهت unobtrusive java script validation در سمت سرور

در ادامه نیاز است ویژگی‌های data-val خاص unobtrusive java script validation را توسط FluentValidation ایجاد کنیم:
using FluentValidation;
using FluentValidation.AspNetCore;
using FluentValidation.Internal;
using FluentValidation.Resources;
using FluentValidation.Validators;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;

namespace FluentValidationSample.ModelsValidations
{
    public class RemoteClientValidator : ClientValidatorBase
    {
        public string RemoteUrl { set; get; }

        public RemoteClientValidator(PropertyRule rule, IPropertyValidator validator) :
            base(rule, validator)
        {
        }

        public override void AddValidation(ClientModelValidationContext context)
        {
            MergeAttribute(context.Attributes, "data-val", "true");
            MergeAttribute(context.Attributes, "data-val-remote", GetErrorMessage(context));
            MergeAttribute(context.Attributes, "data-val-remote-url", RemoteUrl);
        }

        private string GetErrorMessage(ClientModelValidationContext context)
        {
            var formatter = ValidatorOptions.MessageFormatterFactory().AppendPropertyName(Rule.GetDisplayName());
            string messageTemplate;
            try
            {
                messageTemplate = Validator.Options.ErrorMessageSource.GetString(null);
            }
            catch (FluentValidationMessageFormatException)
            {
                messageTemplate = ValidatorOptions.LanguageManager.GetStringForValidator<NotEmptyValidator>();
            }
            return formatter.BuildMessage(messageTemplate);
        }
    }
}
در این کدها، تنها قسمت مهم آن، متد AddValidation است که کار تعریف و افزودن متادیتاهای unobtrusive java script validation را انجام می‌دهد و برای مثال سبب رندر تگ HTML ای زیر می‌شود:
<input dir="ltr" class="form-control input-validation-error" 
type="email" 
data-val="true" 
data-val-email="'آدرس ایمیل' is not a valid email address." 
data-val-remote="این آدرس ایمیل هم اکنون مورد استفاده‌است" 
data-val-remote-url="/Home/ValidateUniqueEmail" 
data-val-required="'آدرس ایمیل' must not be empty." 
id="Email" 
name="Email" 
>
که به همراه ویژگی‌های data-val، data-val-remote و data-val-remote-url است تا unobtrusive jQuery Ajax validation را فعال کند.


افزودن اعتبارسنج‌های تعریف شده به تنظیمات برنامه

پس از تعریف UniqueEmailValidator و RemoteClientValidator، روش افزودن آن‌ها به تنظیمات FluentValidation به صورت زیر است:
namespace FluentValidationSample.Web
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IUsersService, UsersService>();

            services.AddControllersWithViews().AddFluentValidation(
                fv =>
                {
                    fv.RegisterValidatorsFromAssembly(Assembly.GetExecutingAssembly());
                    fv.RegisterValidatorsFromAssemblyContaining<RegisterModelValidator>();
                    fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;

                    fv.ConfigureClientsideValidation(clientSideValidation =>
                    {
                        // ...

                        clientSideValidation.Add(
                            validatorType: typeof(UniqueEmailValidator),
                            factory: (context, rule, validator) =>
                                        new RemoteClientValidator(rule, validator)
                                        {
                                            RemoteUrl = "/Home/ValidateUniqueEmail"
                                        });
                    });
                }
            );
        }
در اینجا یک RemoteUrl را هم مشاهده می‌کنید که به صورت زیر باید تعریف شود:
namespace FluentValidationSample.Web.Controllers
{
    public class HomeController : Controller
    {
        private readonly IUsersService _usersService;

        public HomeController(IUsersService usersService)
        {
            _usersService = usersService;
        }

         // ...

        public IActionResult ValidateUniqueEmail(string email)
        {
            return Ok(_usersService.IsUniqueEmail(email));
        }
    }
}
زمانیکه اعتبارسنجی سمت کلاینت رخ می‌دهد، آدرس ایمیل، به اکشن متد فوق ارسال شده و یک true و یا false را دریافت می‌کند که بیانگر موفقیت آمیز بودن و یا شکست اعتبارسنجی از راه دور است.


تعریف کدهای جاوا اسکریپتی مورد نیاز

پیش از هرکاری، اسکریپت‌های فایل layout برنامه باید چنین تعریفی را داشته باشند:
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
در اینجا مداخل jquery، سپس jquery.validate و بعد از آن jquery.validate.unobtrusive را مشاهده می‌کنید. در ادامه نیازی به تکمیل فایل js/site.js جهت افزودن کدهای remote client validation نیست و این کدها جزئی از کتابخانه‌ی jquery.validate.unobtrusive هستند.


آزمایش برنامه

View این قسمت نیز همانند قسمت قبل است که فقط یک آدرس ایمیل به آن اضافه شده‌است:


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


کدهای کامل این سری را تا این قسمت از اینجا می‌توانید دریافت کنید: FluentValidationSample-part04.zip
مطالب
با ASP.MVC چه مزایایی را به دست خواهیم آورد
عموما در اکثر مطالب مقایسه‌ای بین وب فرم‌ها و ASP.NET MVC به جداسازی بهتر منطق کدها از فرم‌ها و قابلیت بهتر تهیه آزمون‌های واحد اشاره می‌شود. در این مطلب از دیدگاهی دیگر به این مساله خواهیم پرداخت؛ از لحاظ فنی و جدای از مسایل یاد شده، چه مزایای دیگری را می‌توان با استفاده از ASP.NET MVC نسبت به وب فرم‌ها به دست آورد؟

1- آدرس‌های تمیزتر
در ASP.NET MVC به صورت پیش فرض از سیستم Routing موجود در زیر ساخت ASP.NET برای نمایش Urlهایی بدون پسوند استفاده می‌شود. همچنین این سیستم امکان تهیه آدرس‌هایی با سازگاری بهتر با موتورهای جستجو را نیز از ابتدا مدنظر داشته است.
بله. این زیر ساخت در اختیار وب فرم‌ها نیز هست؛ اما فرق است بین حالتی که از ابتدا مجبور شویم تمیزتر کار کنیم با زمانیکه این انتخاب را داریم و ... عموما هم از آن در وب فرم‌ها استفاده نمی‌شود.

2- عدم وابستگی الزامی به فایل‌های فیزیکی موجود در سیستم
کلیه درخواست‌ها در MVC برخلاف وب فرم‌ها در بدو امر به فایل‌های موجود در سیستم منتقل نمی‌شوند. درخواست‌ها به متدهای موجود در کنترلرها منتقل می‌شوند. همین مساله سبب می‌شود که آدرس‌ها الزاما به یک فایل فیزیکی موجود در سیستم اشاره نکنند. به این ترتیب می‌توان درخواست‌ها را بر اساس شرایط، به Viewهای مختلف هدایت کرد و نه اینکه هر درخواست ابتدا به یک view رسیده و سپس به متدی ارجاع داده شود.
این مساله از لحاظ امنیتی نیز مهم است. درخواست‌های رسیده به MVC سبب اجرای هیچ فرمی نخواهند شد. درخواست‌ها حتما باید از فیلتر یک کنترلر عبور کنند تا اجرایی شوند.

3- امکان مدیریت بهتر قسمت‌های مختلف سایت در پوشه‌های جداگانه
اگر به سورس اکثر سایت‌های مبتنی بر ASP.NET Web forms توجه کنیم، تمام فایل‌های آن‌ها در ریشه سایت قرار دارند منهای فایل‌های CSS و JS و تصاویر. در ASP.NET MVC از ابتدای کار، هر قسمت از سایت در پوشه‌های جداگانه‌ای قرار می‌گیرد و به این ترتیب مدیریت فایل‌ها و نظم دهی به آن‌ها ساده‌تر خواهد بود.

4- امکان تعریف تمام اجزای یک فرم یا view به صورت strongly typed
در ASP.NET MVC می‌توان یک کلاس را به یک فرم یا View نسبت داد. به این ترتیب کنترل‌های web forms تبدیل به خواص این کلاس در MVC خواهند شد. مزیت این امر امکان کنترل تمام اجزای فرم‌های سایت توسط کامپایلر است.
به این ترتیب اگر در طی یک حلقه، جدولی را ایجاد کنیم، تمام عناصر تشکیل دهنده این حلقه (چه کدهای آن و چه المان‌هایی که اطلاعات را در صفحه نمایش می‌دهند) نیز توسط کامپایلر قابل بررسی و خطایابی هستند.

5- مقدار دهی خودکار مدل متناظر با یک فرم یا View در ASP.NET MVC
روال متداول کار با وب فرم‌ها، قرار دادن تعدادی کنترل در صفحه و سپس دریافت دستی مقادیر آن‌ها در فایل code behind است. در MVC دیگر نیازی نیست تا این کارها را دستی انجام دهید. به یک فرم یا View کلاسی را انتساب خواهید داد. فریم ورک خواص آن‌را به صورت خودکار در حین ارسال به سرور مقدار دهی خواهد کرد. این مورد حتی در حین کار با jQuery Ajax نیز صادق  است.
این مساله کار با ORMها را نیز ساده‌تر می‌کند. از این جهت که تمام آن‌ها نهایتا با یک سری مدل و کلاس کار خواهند کرد و تمام فیلدها و جداول به تعدادی کلاس و خاصیت تعریف شده در آن‌ها نگاشت می‌شوند.
به این ترتیب چون دیگر نیازی به ارجاع مستقیم به اشیاء بصری در فایل‌های code behind که در اینجا کنترلر نام گرفته‌اند نیست، نوشتن آزمون واحد برای این کلاس‌ها نیز به شدت ساده‌تر شده است.

6- ASP.NET MVC به همراه یک فرم ساز توکار ارائه می‌شود
اگر کسی به شما گفته است که سرعت کار با ASP.NET MVC پایین است به طور قطع دو فصل اول یک کتاب MVC را بیشتر مطالعه نکرده است. در MVC یک کلاس متناظر با فرمی را طراحی می‌کنید. توسط ابزار scaffolding همراه با VS.NET از روی این کلاس و مدل، با چند کلیک یک فرم تولید خواهد شد. فرمی که حتی مقدار دهی و انتساب عناصر بصری آن به کلاس متناظر با آن نیز خودکار است.
سرعت پیاده سازی یک برنامه با ASP.NET MVC به مراتب بیشتر است از کار با وب فرم‌ها.

7- حذف View State در MVC
از آنجائیکه فرم‌های ASP.NET Web forms از نوع strongly typed نیستند (در دات نت 4 و نیم اندکی بهبود در حد گرید‌های آن حاصل شده البته)، برای اینکه پس از ارسال یک فرم به سرور باز هم کنترل‌های نمایش داده شده در صفحه همان مقادیر قبلی را نمایش دهند، مکانیزم View State به همراه ذخیره سازی اطلاعات فرم در فیلدهای مخفی فرم جاری طراحی شد.
در MVC نیازی به این مکانیزم نیست. زیرا فقط کافی است که اطلاعات مدل را مجددا به View ارسال کنیم. نمایش و انتساب نهایی آن در اینجا خودکار است. بنابراین نیازی به View State وجود ندارد.

8- کنترل بهتر بر روی اعتبار سنجی اطلاعات دریافتی
در وب فرم‌ها اگر بخواهیم سیستم اعتبارسنجی آن‌را غیرفعال کنیم، مثلا برای دریافت html از کاربر، نیاز است کلا آن‌را از کار بیندازیم (یا در سطح فرم یا در سطح کل برنامه). در MVC می‌توان این اعتبار سنجی را تنها در سطح یک خاصیت که قرار است اطلاعات HTML ایی را دریافت کند، غیرفعال کرد؛ نه برای کل فرم و نه در سطح کل برنامه.

9- امکان استفاده از فرم‌های و Viewهای Razor بجای موتور وب فرم‌ها
در وب فرم‌ها تا این زمان فقط محدود به تنها موتور نمایشی مخصوص به آن هستیم. اما در MVC این محدودیت برداشته شده و تا به حال چندین و چند View engine در این بین توسط مایکروسافت و سایر برنامه نویس‌ها طراحی شده است. مهم‌ترین آن‌ها Razor است که تمام برنامه نویس‌های MVC پس از مدتی به روان بودن و طراحی طبیعی و عالی آن اعتراف دارند.

10- امکان تعریف بیش از یک فرم در صفحه
طراحی ASP.NET Web forms از روز اول آن محدود به یک فرم در صفحه بوده است. این محدودیت در MVC برداشته شده و مزیت آن امکان ارسال اطلاعات قسمت‌های مختلف یک فرم به کنترلرهای مختلف و جداسازی بهتر کدهای قسمت‌های مختلف برنامه است.

11- امکان Refactoring بهتر کدهای تکراری در ASP.NET MVC به کمک مفهوم فیلترها
فیلترها در MVC یک سری attribute هستند که می‌توان آن‌ها را به متدهای کنترلرها اعمال کرد و به صورت خودکار توسط فریم ورک پیش یا پس از اجرای یک متد اجرا می‌شوند. به این ترتیب حجم قابل ملاحظه‌ای از if و else‌ها را می‌توان در این فیلترها کپسوله کرد و کدهای متدهای کنترلرها را تمیزتر و زیباتر نمود.

12- سازگاری کامل با jQuery و jQuery Ajax و کلا انواع و اقسام فریم‌ورک‌های جاوا اسکریپتی
در MVC وب کنترل‌ها کنار گذاشته شده‌اند و سعی شده است با وب به همان نحو که هست برخورد شود. به این ترتیب اگر نیاز داشتید، کل دکمه‌های فرم را با spanها جایگزین کرده و توسط فریم ورک‌های CSS ایی تزئین کنید، بدون نیاز به نگارش جدیدی از ASP.NET MVC، باز هم برنامه کار خواهد کرد.
یا برای کار با اجزای مختلف فرم از ده‌ها و صدها افزونه موجود برای jQuery به سادگی می‌توان استفاده کرد. برای نمونه کل سیستم اعتبار سنجی توکار MVC با اعتبار سنجی jQuery یکپارچه و جایگزین شده است.
یا برای کار با jQuery Ajax نیازی نیست تا متدی را static تعریف کنید و به این ترتیب از مزایای امنیتی توکار ASP.NET محروم شوید (مثلا دسترسی به شیء User اعتبار سنجی مبتنی بر فرم‌ها). یا اگر فرم شما 10 فیلد دارد، کل این فیلدها به صورت خودکار به خواص متناظر با آن‌ها نگاشت خواهد شد و نیازی نیست برای این مورد کد بنویسید. به علاوه باید درنظر داشت که jQuery Ajax نسبت به فریم ورک Ajax همراه با ASP.NET web forms بسیار سبک‌تر و سریعتر عمل می‌کند چون نیازی ندارد تا هر بار View state را نیز به سرور ارسال کند.
همچنین در اینجا دیگر ID کنترل‌های مورد استفاده در اسکریپت‌ها به صورت خودکار تولید نمی‌شوند و برنامه نویس از ابتدای امر کنترل کاملی را روی این مساله دارد.

13- امکانات فشرده سازی css و js بهتر
در MVC 4 سیستم bundling آن از نمونه مشابه موجود در وب فرم‌ها کامل‌تر است و جهت فشرده سازی و یکی کردن هر دو مورد فایل‌های css و js می‌تواند بکارگرفته شود؛ به همراه تنظیمات کش مرورگر و gzip خودکار حاصل. به علاوه این سیستم را سفارشی سازی نیز می‌توان ساخت و بهینه سازی عملکرد آن مطابق نیاز میسر است.

14- یکپارچگی بهتر EF Code first با MVC
عنوان شد که وجود مدل‌ها و فرم‌های strongly typed یکی از مزایای کار با MVC است و ORMها نیز نهایتا با همین کلاس‌ها هستند که کار می‌کنند. در MVC سیستم کد سازی به نام scaffolding وجود دارد (تهیه شده توسط خود مایکروسافت) که می‌تواند بر اساس مدل‌های EF code first شما، قسمت عمده‌ای از کدهای یک برنامه ASP.NET MVC را تولید کند. سپس می‌توانید به سفارشی سازی آن مشغول شوید.

15- تزریق وابستگی‌ها در MVC ساده‌تر است
در هر دو فریم ورک وب فرم‌ها و MVC امکان تزریق وابستگی‌ها وجود دارد. اما در MVC می‌توان در میانه کار وهله سازی کنترلرها، دخالت کرد و کنترل آن را کاملا در دست گرفت. همین امر سبب می‌شود حین کار با کتابخانه‌های تزریق وابستگی‌ها در ASP.NET MVC حجم کد نویسی به شدت کاهش پیدا کند.

16- امکانات امنیتی MVC بیشتر است
عنوان شد که در MVC می‌توان اعتبار سنجی را تنها در حد یک خاصیت غیرفعال کرد. فیلتر مبارزه با حملات CSRF جزئی از فریم ورک MVC است. به همراه فیلتر Authorize آن که باز هم اعمال سفارشی سیستم اعتبار سنجی مبتنی بر فرم‌ها را ساده‌تر می‌کند با امکان یکپارچگی بهتر با Role providerهای سفارشی.
و یا برای نمونه Razor به صورت پیش فرض امن طراحی شده است. خروجی Razor همواره و در بدو امر، html encoded است مگر اینکه برنامه نویس آگاهانه آن‌را تغییر دهد. این مورد مقاومت در برابر حملات XSS را بالا خواهد برد.
امکان استفاده از فیلترهای سفارشی که عنوان شد، جهت مسایل امنیتی بسیار کاربرد دارند. برای مثال بررسی referrer فرم ارسال به سرور را درنظر بگیرید. در وب فرم‌ها می‌توان این‌کار را با یک http module که روی کل برنامه تاثیر گذار است انجام داد. اما در MVC این فیلتر را تنها می‌توان بر روی یک فرم خاص عمومی برای مثال اعمال کرد و نه کل برنامه.
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 21 - بررسی تغییرات Bundling و Minification
زیرساخت یکی کردن و فشرده سازی اسکریپت‌ها و فایل‌های CSS نگارش پیشین ASP.NET MVC، به طور کامل از ASP.NET Core حذف شده‌است. در ابتدا (تا نگارش RC2)، روش استفاده‌ی از Gulp را توصیه کردند و در زمان ارائه‌ی نگارش RTM، توصیه‌ی رسمی آن‌ها به Bundler Minifier تغییر کرد (و دیگر Gulp را توصیه نمی‌کنند).


یکی کردن و فشرده سازی فایل‌های استاتیک در ASP.NET Core

هدف از یکی کردن و فشرده سازی فایل‌های استاتیک مانند اسکریپت‌ها و فایل‌های CSS، بهبود کارآیی برنامه با کاهش حجم نهایی ارائه‌ی آن و همچنین کاهش تعداد رفت و برگشت‌های به سرور برای دریافت فایل‌های متعدد مرتبط به آن است. در عملیات Bundling، چندین فایل، به یک تک فایل تبدیل می‌شوند تا اتصالات مرورگر به وب سرور، جهت دریافت آن‌ها به نحو چشمگیری کاهش پیدا کند و در عملیات Minification، مراحل متعددی بر روی کدهای نوشته شده صورت می‌گیرد تا حجم نهایی آن‌ها کاهش پیدا کنند. مایکروسافت در ASP.NET Core RTM، ابزاری را به نام BundlerMinifier.Core جهت برآورده کردن این اهداف ارائه کرده‌است. بنابراین اولین قدم، نصب وابستگی‌های آن است.
برای اینکار یک سطر ذیل  را به فایل project.json اضافه کنید. این بسته باید به قسمت tools اضافه شود تا قابلیت فراخوانی از طریق خط فرمان را نیز پیدا کند:
"tools": {
    "BundlerMinifier.Core": "2.1.258"
},
در غیر اینصورت (ذکر آن در قسمت dependencies) خطاهای ذیل را دریافت خواهید کرد:
No executable found matching command "dotnet-bundle"
Version for package `BundlerMinifier.Core` could not be resolved.


اسکریپت نویسی برای کار با BundlerMinifier.Core

روش‌های زیادی برای کار با ابزار BundlerMinifier.Core وجود دارند؛ منجمله انتخاب فایل‌ها در solution explorer و سپس کلیک راست بر روی فایل‌های انتخاب شده و انتخاب گزینه‌ی bundler & minifier برای یکی کردن و فشرده سازی خودکار این فایل‌ها. برای این منظور افزونه‌ی Bundler & Minifier را نیاز است نصب کنید.
اما روشی که قابلیت خودکارسازی را دارد، استفاده از فایل ویژه‌ی bundleconfig.json این ابزار است. برای این منظور فایل جدید bundleconfig.json را به ریشه‌ی پروژه اضافه کرده و سپس محتوای ذیل را به آن اضافه کنید:
[
    {
        "outputFileName": "wwwroot/css/site.min.css",
        "inputFiles": [
            "wwwroot/css/site.css"
        ]
    },
    {
        "outputFileName": "wwwroot/js/site.min.js",
        "inputFiles": [
            "bower_components/jquery/dist/jquery.min.js",
            "bower_components/jquery-validation/dist/jquery.validate.min.js",
            "bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
        ],
        "minify": {
            "enabled": true,
            "renameLocals": true
        },
        "sourceMap": false
    }
]
فرمت این فایل بسیار خوانا است. برای مثال در یک مدخل آن، در ذیل خاصیت inputFiles، لیست فایل‌های css ذکر می‌شوند و سپس در outputFileName، محل نهایی فایل تولیدی باید ذکر شود. این محل نیز باید از پیش وجود داشته باشد. یعنی باید پوشه‌های js و css را در پوشه‌ی عمومی wwwroot پیشتر ایجاد کرده باشید.
با ذخیره سازی این فایل، کار یکی سازی و فشرده کردن مداخل آن به صورت خودکار صورت خواهد گرفت.


خودکار سازی فرآیند یکی کردن و فشرده سازی فایل‌های استاتیک

برای خودکار سازی این فرآیند، می‌توان به صورت زیر عمل کرد. فایل project.json را گشوده و قسمت scripts آن‌را به نحو ذیل تغییر دهید:
"scripts": {
    "precompile": [
        "dotnet bundle"
    ],
    "prepublish": [
        "bower install"
    ],
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
روش دستی کار با ابزار BundlerMinifier، مراجعه به خط فرمان و صدور دستور dotnet bundle است (ابتدا از طریق خط فرمان به ریشه‌ی پروژه وارد شده و سپس این دستور را صادر کنید). برای خودکار سازی آن می‌توان این دستور را در قسمت scripts فایل project.json نیز ذکر کرد تا پیش از کامپایل برنامه، کار یکی کردن، فشرده سازی و همچنین کپی فایل نهایی به پوشه‌ی wwwroot برنامه به صورت خودکار انجام شود.

یک نکته: به منوی Build گزینه‌ی Update all bundles نیز با نصب افزونه‌ی Bundler & Minifier اضافه می‌شود. همچنین اگر از منوی Tools گزینه‌ی Task runner explorer را انتخاب کنید، فایل bundleconfig.json توسط آن شناسایی شده و گزینه‌ی update all files را نیز در اینجا مشاهده خواهید کرد.



ساده سازی تعاریف فایل Layout برنامه

در یک چنین حالتی دیگر نباید در فایل layout شما، ارجاعات مستقیمی به پوشه‌ی مثلا bower_components وجود داشته باشند و یا در کلاس آغازین برنامه، نیازی نیست تا این پوشه را عمومی کنید. لیست مداخلی را که نیاز دارید، به ترتیب از پوشه‌های مختلفی تهیه و در فایل bundleconfig.json ذکر کنید تا یکی شده و خروجی js/site.min.js را تشکیل دهند. این مورد تنها مدخلی است که نیاز است در فایل layout برنامه ذکر شود (بجای چندین و چند مدخل مورد نیاز):
 <script src="~/js/site.min.js" asp-append-version="true" type="text/javascript"></script>
در مورد ویژگی asp-append-version نیز پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 12 - معرفی Tag Helpers» بحث شد و به آن مکانیزم cache busting می‌گویند. این ویژگی سبب خواهد شد تا یک کوئری استرینگ v=xyz? مانند، به انتهای آدرس اسکریپت یا فایل css یا هر فایل استاتیک دیگری اضافه شود. با تغییر محتوای این فایل، قسمت xyz به صورت خودکار تغییر خواهد کرد و به این ترتیب مرورگر همواره آخرین نگارش این فایل را دریافت می‌کند.
مطالب
نمایش ای‌جکسی یک partial view در popover بوت استرپ 3
فرض کنید بخواهیم نمایش رای دهنده‌های یک مطلب را با popover بوت استرپ 3 نمایش دهیم:

Popover بوت استرپ برای کار با منابع remote طراحی نشده‌است و نیاز است توابع API آن‌را به همراه jQuery Ajax ترکیب کرد تا به تصویر فوق رسید.


مرحله‌ی اول: اکشن متدی که یک partial view را باز می‌گرداند

فرض کنید اکشن متدی که لیست کاربران رای داده‌ی به یک مطلب را باز می‌گرداند، چنین شکلی را دارد:
        public ActionResult RenderResults(string param1)
        {
            var users = new[]
            {
                new User{ Id = 1, Name = "Test 1", Rating = 3},
                new User{ Id = 2, Name = "Test 2", Rating = 4},
                new User{ Id = 3, Name = "Test 3", Rating = 5}
            };
            return PartialView("_RenderResults", model: users);
        }
در اینجا لیستی دریافت و سپس به partial view ارسال می‌شود. در ادامه این Partial view نیز به صورت ذیل اطلاعات را رندر می‌کند:
@using RemotePopOver.Models
@model IList<User>

<ul id="ratings1" data-title="Ratings" class="list-unstyled">
    @foreach (var user in Model)
    {
        <li>
            @user.Name
            <span class="badge pull-right">@user.Rating</span>
        </li>
    }
</ul>
در اینجا از یک ویژگی سفارشی data-* به نام data-title نیز استفاده کرده‌ایم. متنی که در آن قرار می‌گیرد، به صورت عنوان popover ظاهر خواهد شد.


مرحله‌ی دوم: دریافت اطلاعات partial view با استفاده از jQuery Ajax و سپس درج آن در یک popover

می‌خواهیم با حرکت ماوس بر روی دکمه‌ی سفارشی ذیل، یک popover ظاهر شده و محتوای خودش را از اکشن متد فوق تامین کند.
<span id="remotePopover1"
      aria-hidden="true"
      data-param1="test"
      data-popover-content-url="@Url.Action("RenderResults", "Home")"
      class="glyphicon glyphicon-info-sign btn btn-info"></span>
در این مثال، چند ویژگی سفارشی data-* دیگر را نیز تعریف کرده‌ایم تا نیازی به تعریف سراسری متغیرهای جاوا اسکریپتی نباشد. برای مثال data-param1 یک پارامتر دلخواه است و data-popover-content-url به آدرس اکشن متدی اشاره می‌کند که قرار است partial view مدنظر را رندر کند.
در ادامه نحوه‌ی استفاده‌ی از این ویژگی‌ها را در jQuery Ajax مشاهده می‌کنید:
@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            $('body').on('mouseenter', 'span[data-popover-content-url]', function () {
                var el = $(this);
                $.ajax({
                    type: "POST",
                    url: $(this).data("popover-content-url"),
                    data: JSON.stringify({ param1: $(this).data("param1") }),
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    // controller is returning a simple text, not json
                    complete: function (xhr, status) {
                        var data = xhr.responseText;
                        if (status === 'error' || !data) {
                            el.popover({
                                content: 'Error connecting server!',
                                trigger: 'focus',
                                html: true,
                                container: 'body',
                                placement: 'auto',
                                title: 'Error!'
                            }).popover('show');
                        }
                        else {
                            el.popover({
                                content: data,
                                trigger: 'focus',
                                html: true,
                                container: 'body',
                                placement: 'auto',
                                title: $('<html />').html(data).find('#ratings1:first').data('title')
                            }).popover('show');
                        }
                    }
                });
            }).on('mouseleave', 'span[data-popover-content-url]', function () {
                $(this).popover('hide');
            });
        });
    </script>
}
در این مثال تمام المان‌هایی که دارای data-popover-content-url هستند، تحت نظر قرار می‌گیرند. سپس اگر ماوس به محدوده‌ی آن‌ها وارد شد، مقدار ("this).data("popover-content-url)$ مساوی آدرسی است که قرار است اطلاعات را از سرور دریافت کند. به همین جهت از آن برای استفاده‌ی در متد ajax کمک گرفته شده‌است.
خروجی partial view به صورت json نیست. بنابراین باید اطلاعات نهایی آن‌را در callback ویژه‌ی complete دریافت کرد. مقدار data دریافتی، معادل اطلاعات رندر شده‌ی partial view است. به همین جهت آن‌را به خاصیت content متد popver ارسال می‌کنیم. همچنین چون خروجی patrtial view به همراه html است، نیاز است خاصیت html متد popover نیز به true تنظیم شود. در خاصیت title، نحوه‌ی دسترسی به مقدار data-title تنظیم شده‌ی در partial view را مشاهده می‌کنید.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
RemotePopOver.zip
مطالب
معرفی چند پروژه‌ی مهم Typescript
فلسفه‌ی بوجود آمدن زبان Typescript یکی از شنیدنی‌ترین‌ها در دنیای برنامه‌نویسی است. به یاد دارم روزهای اولی که با این زبان آشنا شدم (زمانی که حدوداً ورژن 0.6 منتشر شده بود)، افراد زیادی در مورد این زبان و اینکه آیا اصلاً به این زبان احتیاج داریم یا نه نظرات زیادی دادند. مثلاً Douglas Crockford در مورد این زبان بعد از تعریف و تمجیدهایی که از Anders Hejlsberg کرده گفته :
I think that JavaScript's loose typing is one of its best features and that type checking is way overrated. TypeScript adds sweetness, but at a price. It is not a price I am willing to pay. 

اما به مرور زمان این زبان توفیق بیشتری پیدا کرد تا اینکه امروز پروژه‌های بسیار جالبی با این زبان در حال توسعه هستند.

چرا باید در مورد Typescript بدانیم؟

زبان Typescript نقاط قوت بسیاری دارد، از جمله‌ی آنها می‌توان به موارد زیر اشاره کرد:

  1. این زبان یکی از مشکلات اصلی JavaScript را که نبودن Type Safety می‌باشد حل کرده‌است. اگر چه زبانی که type safe نباشد بسیاری اوقات مزیت است! زبان typescript در حقیقت یک زبان gradual typing است.
  2. از آنجایی که typescript یک super set از زبان JavaScript است، برنامه‌نویس در لحظه از مزایای زبان JavaScript هم بهره‌مند است. مهم‌تر از آن این است که در زبان typescript به اقیانوس کتابخانه‌های JavaScript دسترسی دارید. این امکان در بسیاری زبان‌های دیگر جایگزین JavaScript وجود ندارد. حتی بهتر از آن، می‌تواند با این کتابخانه‌ها به‌صورت type safe برنامه بنویسید. تصور کنید که وقتی با $ در JQuery کار می‌کنید بتوانید از امکان intellisense استفاده کنید.
  3. بازهم از آنجا که typescript یک super set از JavaScript است، typescript قرار نیست به اسمبلی کامپایل شود؛ بلکه به زبان شناخته شده‌ای به نام JavaScript تبدیل می‌شود. بنابراین حتی می‌توان از آن JavaScript نیز یاد گرفت.
  4. کار با زبان typescript برای کسانی که با java یا سی شارپ آشنا هستند، راحت است. امکاناتی مانند genericها نیز در typescript وجود دارد.
  5. نقشه‌ی راه typescript با EcmaScript هماهنگ است. بنابراین از یادگرفتن این زبان ضرر نمی‌کنید چون قابلیت‌های این زبان را به احتمال زیاد در نسخه‌ی بعدی EcmaScript خواهید دید.
  6. این زبان توسط شرکت مایکروسافت پشتیبانی می‌شود، اوپن سورس است و تجربه‌ی Anders Hejlsberg در زمینه‌ی طراحی زبان‌های برنامه‌نویسی پشتیبان آن!
  7. پروژه‌های جالبی که در ادامه به معرفی آنها می‌پردازیم، با این زبان در حال توسعه هستند.

در این مطلب تعدادی از این پروژه‌ها را که برای خودم جذاب هستند، به شما معرفی می‌کنم.

AngularJS 2

طبیعتاً مهم‌ترین اتفاقی که برای typescript در این روزهای اخیر افتاد این بود که تیم Angular اعلام کرد که نسخه‌ی ۲ این فریم‌ورک (که این روزها در حد JQuery در وب معروف شده و استفاده می‌شود) را با زبان Typescript توسعه می‌دهد و امکاناتی که قرار بود توسط زبان AtScript پیاده‌سازی شوند، به کمک Typescript توسعه پیدا می‌کنند. تیم Typescript هم بلافاصله اعلام کرد که در نسخه‌ی 1.5 که به‌زودی منتشر می‌شود بسیاری از امکانات AtScript قرار خواهد داشت. بنابراین می‌توانید منتظر قابلیتی شبیه به Attributeهای سی‌شارپ در typescript 1.5 باشید.
همانطور که می‌دانید AngularJS مهم‌ترین فریم‌ورک حال حاضر است که برای توسعه‌ی نرم‌افزارهای SPA وجود دارد. اعلام توسعه‌ی Angular 2 به‌وسیله‌ی Typescript مطمئناً خبر خوبی برای برنامه‌نویسان typescript خواهد بود، چون این اتفاق باعث بهبود سریع‌تر این زبان می‌شود.

Definitely Typed

اگرچه نمی‌توان این پروژه را در سطح دیگر پروژه‌هایی که در این مقاله معرفی می‌شود قرار داد، ولی اهمیت آن من را مجبور کرد که در این مقاله در موردش صحبت کنم. پروژه‌ی Definitely Typed در حقیقت استفاده از کتابخانه‌های دیگر JavaScript را در typescript ممکن می‌سازد. این پروژه برای پروژه‌های دیگری مانند JQuery، AngularJS، HighCharts، Underscore و هر چیزی که فکرش را بکنید Type Definition تعریف کرده. اگر هم کتابخانه‌ای که شما می‌خواستید در این پروژه نبود، دلیلش این است که اضافه کردن آن را به شما واگذار کرده‌اند! Type Definitionها در Typescript یکی از قابلیت‌های این زبان هستند برای اینکه بتوان با کتابخانه‌های JavaScript به‌صورت Type safe کار کرد.

shumway

حتماً از شنیدن اینکه این پروژه قرار است چه کاری انجام دهد شوکه خواهید شد! shumway که توسط موزیلا توسعه می‌یابد قرار است همان flash player باشد! البته این پروژه هنوز در مراحل اولیه‌ی توسعه است ولی اگر بخواهید می‌توانید دموی این پروژه را اینجا  ببینید.

Fayde

پروژه‌ی Fayde هم Silverlight را هدف گرفته است. البته مانند shumway موسسه‌ی معروفی از آن حمایت نمی‌کند.

Doppio

پروژه‌ی Doppio در حقیقت یک Java Virtual Machine است که روی Browser هم می‌تواند اجرا شود. از جمله کارهای جالبی که با این پروژه می‌توان کرد، کامپایل کردن کد جاوا، Disassemble کردن یک فایل class، اجرای یک فایل JAR و حتی ارتباط با JavaScript هستند.

TypeFramework

این پروژه برای افرادی خوب است که هم به NodeJS علاقمند هستند و هم به ASP.NET MVC. پروژه‌ی TypeFramework در حقیقت پیاده‌سازی مدل ASP.NET MVC در NodeJS است. Controllerها، Actionها، ActionResultها و حتی ActionFilterها با همان تعریف موجود در ASP.NET MVC در این فریم‌ورک وجود دارند.

MAYHEM

این پروژه یک فریم‌ورک کاملی برای طراحی و توسعه‌ی نرم‌افزارهای Enterprise است. در شرح این پروژه آمده است که بر خلاف اینکه همه‌ی فریم‌ورک‌ها روی حجم فایل، سرعت و... تمرکز دارند این پروژه بر درستی معماری تأکید دارد. احتمالاً استفاده از این فریم‌ورک برای پروژه‌های طولانی مدت و بزرگ مناسب است. اگرچه از طرف دیگر احتمالاً یاد گرفتن این فریم‌ورک هم کار سختی خواهد بود.

حرف آخر

حرف آخر اینکه به نظر می‌رسد Typescript زبانی است که ارزش وقت گذاشتن دارد و اگر خواستید Typescript را یاد بگیرید نگاه کردن به کدهای این پروژه‌ها حتماً کلاس درس پرباری خواهد بود. چه کسی می‌داند، شاید شما بخواهید در توسعه‌ی یکی از این پروژه‌ها مشارکت کنید!
نکته‌ی بعد از آخر هم اینکه اگر خواستید به‌طور جدی با این زبان برنامه‌نویسی کنید نگاهی به tslint و typedoc هم بیاندازید.
مطالب
آشنایی بیشتر با AngularJS Directive

در مطلب آشنایی با Directive‌ها در AngularJS با نحوه‌ی ایجاد Directive آشنا شدیم. هدف از این مطلب، آشنایی بیشتر با Directive در AngularJS است؛ یکی از بهترین فریم ورک‌های جاوااسکریپتی، با قابلیت ایجاد کتابخانه‌هایی از کامپوننت‌ها که می‌توانند به HTML اضافه شوند .

کتابخانه‌های جاوااسکریپتی زیادی وجود دارند. به عنوان مثال Bootstrap یکی از محبوب‌ترین "front-end framework" ها است که امکان تغییر در ظاهر المنت‌ها را فراهم می‌کند و شامل تعدادی کامپوننت جاوااسکریپتی نیز می‌باشد. مشکل کار، در هنگام استفاده از کامپوننت ها است. شخصی که در حال توسعه‌ی HTML است باید در کد جاوااسکریپتی خود از  jQuery استفاده کند و بعنوان مثال یک Popover  را فعال یا غیر فعال کند و این، یک فرآیند خسته کننده و مستعد خطا است. 


یک مثال ساده از Directives AngularJS و بررسی آن

var m = angular.module("myApp");
 
myApp.directive("myDir", function() {
  return {
  restrict: "E",   
  scope: {     
   name: "@",   
   amount: "=",  
   save: "&"    
  },
  template:    
   "<div>" +
   "  {{name}}: <input ng-model='amount' />" +
   "  <button ng-click='save()'>Save</button>" +
   "</div>",
  replace: true,   
  transclude: false, 
  controller: [ "$scope", function ($scope) { …  }],
  link: function (scope, element, attrs, controller) {…}
  }
});

به الگوی نامگذاری directive دقت کنید. پیشوند my شبیه به یک namespace است. بنابراین اگر یک Application از دایرکتیوهای قرار گرفته در Module ‌های متفاوت استفاده کند، به راحتی می‌توان محل تعریف یک directive را تشخیص داد. این نام می‌تواند نشان دهنده‌ی این باشد که این directive را خودتان توسعه داده‌اید یا از یک directive توسعه داده شده توسط شخص دیگری در حال استفاده هستید. به هر حال این نحوه‌ی نام گذاری یک اجبار نیست و به عنوان یک پیشنهاد است.

سازنده directive یک شیء را با تعدادی خاصیت باز می‌گرداند که تمامی آنها در سایت AngularJS توضیح داده شده‌اند. در اینجا قصد داریم تا توضیحی مختصر در مورد کاری که این خصوصیات انجام می‌دهند داشته باشیم.

· restrict : تشخیص می‌دهد که آیا directive در HTML استفاده خواهد شد. گزینه‌های قابل استفاده ‘A’ ،  ‘E’ ، ‘C’ برای attribute ، element ، class و یا comment است . پیش فرض ‘A’ برای attribute است. اما ما بیشتر علاقه به استفاده از ویژگی element برای ایجاد المنت‌های UI داریم.

· scope : ایجاد یک scope ایزوله که متعلق به directive است و موجب ایزوله شدن آن از scope صدا زننده directive می‌شود. متغیرهای scope پدر از طریق خصوصیات تگ directive ارسال می‌شوند. این ایزوله کردن زمانی کاربردی است که در حال ایجاد کامپوننت هایی با قابلیت استفاده مجدد هستیم، که نباید متکی به scope پدر باشند. شیء scope در directive نام و نوع متغیرهای scope را تعیین می‌کنند. در مثال بالا سه متغیر برای scope تعریف شده است:

-   name: "@" (by value, one-way) : علامت @ مشخص می‌کند که مقدار متغیر ارسال می‌شود. Directive یک رشته را دریافت می‌کند که شامل مقدار ارسال شده از scope پدر می‌باشد. Directive می‌تواند از آن استفاده کند، اما نمی‌تواند مقدار آن را در scope پدر تغییر دهد.

-   amount: "=" (by reference, two-way) : علامت = مشخص می‌کند این متغیر با ارجاع ارسال می‌شود. Directive یک ارجاع به مقدار متغیر در scope اصلی دریافت می‌کند. مقدار می‌تواند هر نوع داده ای، شامل یک شیء complex یا یک آرایه باشد. Directive می‌تواند مقدار را در scope پدر تغییر دهد. این نوع متغیر، زمانیکه نیاز باشد directive مقدار را در scope پدر تغییر دهد، استفاده می‌شود.

-   save: "&" (expression) : علامت & مشخص می‌کند این متغیر یک expression را که در scope پدر اجرا می‌شود، نگهداری می‌کند. اکنون directive قابلیت انجام کارهایی فراتر از تغییر یک مقدار را دارد. به عنوان مثال می‌توان یک تابع را از scope پدر فراخوانی و نتیجه‌ی اجرا را دریافت کرد.

· template :  الگوی رشته ای که جایگزین المنت تعریف شده می‌شود. فرآیند جایگزینی تمامی خصوصیات را از المنت قدیمی به المنت جدید انتقال می‌دهد. به نحوه استفاده از متغیر‌های تعریف شده در scope ایزوله دقت کنید. این مورد به شما امکان تعریف directive های macro-style را می‌دهد که نیاز به کد اضافه‌ای، ندارند. اگرچه در بیشتر موارد الگو  یک تگ ساده <div> است که از کد‌های link که در زیر توضیح داده شده است استفاده می‌کند.

· replace : تعیین می‌کند که آیا الگوی directive باید جایگزین المنت شود. مقدار پیش فرض false است.

· transclude : تعیین کننده این است که محتوای directive باید در المنت کپی شود یا خیر. در مثال زیر المنت tab شامل المنت‌های HTML دیگر است پس transclude برابر true است.  

<body ng-app="components">
  <h3>BootStrap Tab Component</h3>
    <tabs>
       <pane title="First Tab">
          <div>This is the content of the first tab.</div>
       </pane>
       <pane title="Second Tab">
          <div>This is the content of the second tab.</div>
       </pane>
    </tabs>
</body>
 

· link : این تابع بیشتر منطق directive را شامل می‌شود. Link وظیفه دستکاری DOM ، ایجاد event listener ‌ها و... را دارد. تابع Link پارامترهای زیر را دریافت می‌کند:

-   scope : ارجاع به scope ایزوله شده directive دارد.

-   element : ارجاع به المنت‌های DOM که directive را تعریف کرده اند. تابع link معمولا برای دستکاری المنت از jQuery استفاده می‌کند. (یا از Angular's jqLite در صورتی که jQuery بارگذاری نشده باشد)

-   controller : در مواقعی که از دایرکتیو‌های تو در تو استفاده می‌شود کاربرد دارد. این پارامتر یک directive فرزند با ارجاعی به پدر را فراهم می‌کند، بنابراین موجب ارتباط  directive ‌ها می‌شود.

به عنوان مثال،  این directive  که پیاده سازی bootstrap tab را انجام داده است، می‌توانید مشاهده نمایید.

موفق باشید

مطالب
بهینه سازی فایلهای js و css در برنامه‌های ASP.NET با استفاده از Combres - قسمت اول
 یکی از مواردی که در توسعه وب نقش مهمی دارد، بهینه سازی فایلهای js  و css است که با فشرده سازی و کش کردن آنها می‌توان سرعت بارگذاری را تا حد چشمگیری افزایش داد. برای درک بهتر، به مثال زیر توجه کنید.
یک پروژه ساده را ایجاد می‌کنیم و فایل‌های CSS و js  را مانند شکل زیر، به آن اضافه می‌کنیم:


طبق تصویر فایل‌ها را به صفحه‌ای که ساختیم اضافه می‌کنیم:


پروژه را اجرا کرده و توسط افزونه‌ی firebug درخواست‌هایی را که از سرور شده‌است، بررسی می‌کنیم. مشاهده خواهید کرد که به ازای هر فایل، یک درخواست به سرور ارسال شده و هیچکدام از فایل‌ها توسط  وب سرور فشرده سازی نشده‌اند و اطلاعاتی در مورد کش، به هدر آنها اضافه نشده است.

برای رفع این موارد، روشهای گوناگونی وجود دارد که امروز قصد داریم این کار را توسط  کتابخانه Combress انجام دهیم !
نصب کتابخانه Combres
شما می‌توانید با استفاده از nuget این کتابخانه را به پروژه خود اضافه کنید. 
ایجاد فایل تنظیمات
پس از نصب کتابخانه، فایلی با نام combres.xml در فولدر app_data ساخته می‌شود که تمامی فعالیت‌های کتابخانه براساس آن انجام می‌شود و ساختار آن بصورت زیر است:
<?xml version="1.0" encoding="utf-8" ?>
<combres xmlns='urn:combres'>
  <filters>
    <filter type="Combres.Filters.FixUrlsInCssFilter, Combres" />
  </filters>
  <resourceSets url="~/combres.axd"
                defaultDuration="30"
                defaultVersion="auto"
                defaultDebugEnabled="false"
                defaultIgnorePipelineWhenDebug="true"
                localChangeMonitorInterval="30"
                remoteChangeMonitorInterval="60">
    <resourceSet name="siteCss" type="css">
      <resource path="~/content/Site.css" />
      <resource path="~/content/anotherCss.css" />
      <resource path="~/scripts/yetAnotherCss.css" />
    </resourceSet>
    <resourceSet name="siteJs" type="js">
      <resource path="~/scripts/jquery-1.4.4.js" />
      <resource path="~/scripts/anotherJs.js" />
      <resource path="~/scripts/yetAnotherJs.js" />
    </resourceSet>
  </resourceSets>
</combres>
ResourceSet: با استفاده از هر ResourceSet می‌توانید آن مجموعه فایل را در یک درخواست از سرور دریافت کنید.
پارامتر url : برای تولید لینک فایل‌ها از آن استفاده میکند.
پارامتر defaultDuration : این عدد به تعداد روزهای پیشفرض برای کش کردن فایلها اشاره میکند.
پارامتر defaultVersion :در صورتی که مقدار آن auto باشد به ازای هر تغییر، آدرس جدیدی برای فایل موردنظر ایجاد میشود.
پارامتر defaultDebugEnabled :با استفاده از آن میتوانید debug mode را مشخص کنید. در صورتیکه مقدار آن auto باشد، این مقدار مستقیما از وب‌کانفیگ خوانده میشود.
مقادیر پیش فرض برای تمامی ResourceSet‌ها استفاده می‌شود و در صورت نیاز میتوان این مقادیر را برای هر ResourceSet بازنویسی کرد. فیلترها برای اعمال تغییراتی در فایل js و CSS استفاده می‌شوند که باید به این شکل معرفی شوند. در قسمت بعد با فیلترها بیشتر آشنا میشویم.
فایل cobmres.xml  را به منظور استفاده در پروژه به صورت زیر تغییر می‌دهیم.
<?xml version="1.0" encoding="utf-8" ?>

<combres xmlns='urn:combres'>
  <filters>
    <filter type="Combres.Filters.FixUrlsInCssFilter, Combres" />
  </filters>
  <resourceSets url="~/combres.axd"
                defaultDuration="30"
                defaultVersion="auto"
                defaultDebugEnabled="false"
                defaultIgnorePipelineWhenDebug="true"
                localChangeMonitorInterval="30"
                remoteChangeMonitorInterval="60">
    <resourceSet name="siteCss" type="css">
      <resource path="~/Styles/Site.css" />
    </resourceSet>
    <resourceSet name="siteJs" type="js">
      <resource path="~/Scripts/jquery-1.10.2.js" />
      <resource path="~/Scripts/jquery-1.10.2.min.js" />
    </resourceSet>
  </resourceSets>
</combres>
اگر از نیوگت برای نصب کتابخانه استفاده کرده باشید تغییرات مورد نیاز در فایل وب کانفیگ به صورت خودکار اعمال می‌شود؛ در غیر اینصورت باید قسمتهای زیر را به آن اضافه کنید.
<configuration>
  <configSections>
    <section name="combres" type="Combres.ConfigSectionSetting, Combres, Version=2.2, Culture=neutral, PublicKeyToken=1ca6b37997dd7536" />
  </configSections>
  <system.web>
    <pages>
      <namespaces>
        <add namespace="Combres" />
      </namespaces>
    </pages>
  </system.web>
  <combres definitionUrl="~/App_Data/combres.xml" />
  <appSettings>
    <add key="CombresSectionName" value="combres" />
  </appSettings>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="AjaxMin" publicKeyToken="21ef50ce11b5d80f" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.84.4790.14405" newVersion="4.84.4790.14405" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
حال باید Route  مربوط به Combres را به RouteTable اضافه کنیم. در صورتیکه از نیوگت استفاده کرده باشید، کلاسی به فولدر app_start  اضافه شده است که با استفاده از WebActivator اینکار را در Application_Start انجام میدهد؛ در غیر اینصورت باید به صورت زیر این کار را انجام دهیم.
protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.AddCombresRoute("Combres Route");
        }
بعد از انجام مراحل قبل، نوبت به آن رسیده است که از لینکهای combres در صفحات خود استفاده کنیم. شیوه استفاده از آن در وب فرم به این صورت است:
<%@ Import Namespace="Combres" %>
<head runat="server">
    <%= WebExtensions.CombresLink("siteCss") %>
    <%= WebExtensions.CombresLink("siteJs") %>
</head>
و در MVC به صورت زیر می‌باشد:
<%= Url.CombresLink("siteCss") %>
<%= Url.CombresLink("siteJs") %>
در هر دو مورد نام ResourceSet برای تولید لینک به متد CombresLink ارسال میشود. پس از اجرای مجدد برنامه و با استفاده از firebug خواهیم دید که به ازای هر ResourceSet، یک درخواست به سرور ارسال شده است و حجم فایلها به صورت چشمگیری کاهش یافته است و اطلاعات مربوط به کش هم به آن اضافه شده است.

در ادامه می‌توانید فایل site.css قبلی و فعلی را مقایسه کنید!

در قسمت بعد با سازوکار combres و روش استفاده از فیلترها، بیشتر آشنا میشویم.

CombresProject-67bd3b7299f24c5484edc35537fa990b.zip 

نظرات مطالب
یکپارچه سازی سیستم اعتبارسنجی ASP.NET MVC با Kendo UI validator
کتابخانه‌ی ذکر شده را حذف و سپس به روش زیر برای فعال سازی remote validation عمل کنید:
            $.validator.methods.remote = function () { /* disabled */ };
            $("form").kendoValidator({
                onfocusout: true,
                onkeyup: true,
                rules: {
                    remote: function (input) {
                        var remoteAttr = input.attr("data-val-remote-url");
                        if (typeof remoteAttr === typeof undefined || remoteAttr === false) {
                            return true;
                        }

                        var isInvalid = true;
                        var data = {};
                        data[input.attr('name')] = input.val();

                        $.ajax({
                            url: remoteAttr,
                            mode: "abort",
                            port: "validate" + input.attr('name'),
                            dataType: "json",
                            type: input.attr("data-val-remote-type"),
                            data: data,
                            async: false,
                            success: function (response) {
                                isInvalid = response;
                            }
                        });
                        return !isInvalid;
                    }
                },
                messages: {
                    remote: function (input) {
                        return input.data('val-remote');
                    }
                }
            });
- در اینجا در ابتدا متد remote کتابخانه‌ی jQuery Validator غیرفعال می‌شود. سپس یک rule جدید، به kendoValidator به نام دلخواه remote اضافه شده‌است. چون ruleهای kendoValidator اعمال async را پشتیبانی نمی‌کنند، در درخواست ajax آن async: false تنظیم شده‌است. به این ترتیب سطر پس از ajax، پس از پایان کار عملیات ajax فراخوانی می‌شود و در این حالت kendoValidator بدون مشکل کار خواهد کرد.
- سمت سرور آن هم مانند قبل به همراه استفاده از ویژگی Remote است که از آن صرفا برای مقدار دهی data-val-remote-url و val-remote که در rule جدید استفاده می‌شوند، کمک گرفته خواهد شد.
نظرات مطالب
صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
columnChooser رویداد done هم دارد که از آن می‌شود برای آنالیز ستون‌ها استفاده کرد:
            .jqGrid('navButtonAdd', '#pager',
            {
                caption: "تنظیم نمایش ستون‌ها", title: "Reorder Columns",
                onClickButton: function () {
                    jQuery("#list").jqGrid('columnChooser', {
                        done: function (perm) {
                            if (perm) {
                                $("#list").jqGrid("remapColumns", perm, true, false);
                            }

                            var colModel = $("#list").jqGrid('getGridParam', 'colModel');
                            var hiddenColumns = new Array();
                            for (var i = 0; i < colModel.length; i++) {
                                if (colModel[i].hidden) {
                                    hiddenColumns.push(colModel[i].name);
                                }
                            }

                            $.ajax({
                                type: "POST",
                                url: "@Url.Action("HiddenColumns","Home")",
                                dataType: "json",
                                traditional: true,
                                data: { hiddenColumns: hiddenColumns }
                            });
                        }
                    });
                }
            })
متد getGridParam با پارامتر colModel، آرایه‌ای از خواص ستون‌ها را بر می‌گرداند.
var colModel = $("#list").jqGrid('getGridParam', 'colModel');
در این خواص اگر hidden مساوی true بود، یعنی مخفی شده‌است. در نهایت از این‌ها می‌شود یک آرایه را تشکیل داد و به سرور ارسال کرد:
        public ActionResult HiddenColumns(string[] hiddenColumns)
        {
            //todo: save it in the DB or cookies or session ....

            return Content("OK");
        }
امضای اکشن متدی است که لیست نام ستون‌های مخفی را دریافت می‌کند.