پیاده سازی امنیت بر اساس Role در MVC

Role در MVC

پیاده سازی امنیت بر اساس Role در MVC ، انتخاب یک موضوع برای چنین مقالاتی ( آموزش MVC در هفت روز ) با توجه به اینکه چندین مبحث را مورد برسی قرار میدهد کمی دشوار است.برای درک بهتر مقاله میتوانید فهرست را مطالعه کنید.چون سعی میشود مهم ترین مبحث در فهرست را برای مقالات انتخاب کرد.

به روز پنجم از “MVC 5 را در ۷ روز یاد بگیرید” خوش آمدید. امیدوارم که اوقات خوشی را از روز اول تا روز چهارم در یادگیری MVC سپری کرده باشید و از آن لذت برده باشید. حتما به خاطر دارید که اولویت اول شروع روز پنجم، یادگیری کامل ۴ روز قبل می باشد.

فهرست مطالب:

  • تمرین بیست و دوم – اضافه کردن Footer
    • پرسش و پاسخ در تمرین بیست و دومم
  • تمرین بیست و سوم – پیاده سازی امنیت براساس Role
    • بخش اول
    • بخش دوم
    • پرسش و پاسخ در تمرین بیست و سوم
  • تمرین بیست و چهارم – پیاده سازی
    • پرسش و پاسخ در تمرین بیست و چهارم
  • تمرین بیست و پنجم – بهبود کارآیی کدهای Header و Footer با Action Filter
  • جمع بندی

تمرین بیست و دوم – اضافه کردن Footer

در این تمرین می خواهیم یک Footer به صفحه Employee اضافه کنیم. هدف اصلی این تمرین، معرفی Partial View می باشد.

Partial Views چیست؟

درواقع، Partial View یک View قابل استفاده مجدد است که هرگز به طور مستقیم نمایش داده نمی شود. در Viewهای دیگر Include می شود و به عنوان بخشی از آن ها نمایش داده می شود. درواقع، مانند user control در ASP.Net WebForms می باشد، اما بدون هیچ code behindای نوشته می شود.

گام ۱: ایجاد ViewModel برای Partial View

روی فولدر ViewModel راست کلیک کرده و کلاسی با نام FooterViewModel به صورت زیر ایجاد می کنیم:

 

public class FooterViewModel

{

   public string CompanyName { get; set; }

   public string Year { get; set; }

}

گام ۲: ایجاد Partial View

روی فولدر “~/Views/Shared” راست کلیک کرده و Add>>View را انتخاب می کنیم.

نام View را Footer می گذاریم و “Create as a partial view” را تیک زده و روی Add کلیک می کنیم.

توجه داشته باشید که در اینجا درباره همان shared folder که روز اول معرفی کردیم، صحبت می کنیم. Shared folder شامل Viewهایی است که متعلق به Controller خاصی نیستند. Viewهای این فولدر قابل دسترسی برای همه Controller ها هستند.

گام ۳: نمایش داده ها در Partial View

فایل Footer.cshtml را باز کرده و تگ های HTML زیر را در آن قرار می دهیم.

@using WebApplication1.ViewModels

@model FooterViewModel

<div style="text-align:right;background-color: silver;color: darkcyan;border: 1px solid gray;margin-top:2px;padding-right:10px;">

   @Model.CompanyName &copy; @Model.Year

</div>

 

گام ۳: Include کردن داده های Footer را در ViewModel اصلی

کلاس EmployeeListViewModel را باز کرده و یک Property جدید برای نگه داشتن داده های Footer مانند زیر ایجاد می کنیم:

public class EmployeeListViewModel
{
    public List<EmployeeViewModel> Employees { get; set; }

    public string UserName { get; set; }

    public FooterViewModel FooterData { get; set; }//New Property
}

 

در مثال ما، Footer(Partial View) به عنوان بخشی از Index View نمایش داده خواهد شد و داده های موردنیاز Footer را از طریق index به آن ارسال می کنیم.

Index View یک Strongly type view از نوع EmployeeListViewModel است، بنابراین تمام داده های موردنیاز Footer View باید در EmployeeListViewModel مخفی سازی (encapsulate) شود.

گام ۴: مقداردهی به Footer

EmployeeController را باز کرده و مانند زیر، در Index مقادیر موردنیاز را به FooterData می دهیم:

public ActionResult Index()
{
   ...
   ...
    employeeListViewModel.FooterData = new FooterViewModel();
    employeeListViewModel.FooterData.CompanyName = "MVC آموزش گام به گام ";//Can be set to dynamic value
    employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString();
    return View("Index", employeeListViewModel);
} 

 

گام ۵: نمایش Footer

Index.cshtml را باز کرده و Footer Partial View را بعد از تگ table قرار می دهیم.

       </table>
        @{
            Html.RenderPartial("Footer", Model.FooterData);
        }
    </div>
</body>
</html>

 

گام ۶: تست و اجرا

کلید F5 را فشار داده و به Index view می رویم.

 

پرسش و پاسخ در تمرین بیست و دوم:

Html.Partial چه کاری انجام می دهد؟

دقیقا مانند Html.RenderPartial برای نمایش Partial View در View استفاده می شود.

نحوه نوشتن آن به شکل زیر است:

@Html.Partial("Footer", Model.FooterData);

 

تفاوت این دو چیست؟

Html.RenderPartial نتیجه Partial View را به طور مستقیم روی جریان HTTP Response می نویسد، در حالی که Html.Partial نتیجه را به عنوان MvcHtmlString برمی گرداند.

MvcHtmlString چیست و چرا Html.Partial به جای String، MvcHtmlString برمی گرداند؟

ابتدا اجازه بدهید که بفهمیم، MvcHtmlString چیست؟

بنابرآنچه در MSDN ذکر شده: “MvcHtmlString یک رشته HTML-encoded را نشان می دهد که نباید دوباره کدگذاری شود.”

حالا اجازه بدهید که این تعریف را ساده تر کنیم:

به کد زیر توجه کنید:

@{
   string MyString = "My Simple String";
}
@MyString

خروجی زیر را تولید می کند:

همان طور که مشاهده می کنید، razor همه محتوا را همان طور که هست نمایش می دهد. بسیاری از افراد فکر می کنند باید یک رشته Bold شده ببینند، اما Razor HTML محتوا را قبل از نمایش رمزنگاری می کند و به همین دلیل به جای رشته bold، محتوای اصلی را دریافت می کنیم.

ما زمانی از MvcHtmlString استفاده می کنیم که نخواهیم Razor رمزنگاری انجام دهد. MvcHtmlString به Razor می فهماند که “این رشته در حال حاضر، رمزنگاری شده است و نیازی به رمزنگاری مجدد نیست.”

برای مثال به کد زیر توجه نمایید:

@{
   string MyString = "My Simple String";
}
@MvcHtmlString.Create(MyString)

کد زیر را تولید می کند:

چرا Html.Partial به جای string، MvcHtmlString برمی گرداند؟

در چند سطر بالا متوجه شدیم که “Razor همیشه رشته ها را رمزنگاری می کند اما هرگز MvcHtmlString را رمزنگاری نمی کند.” این باعث نمی شود که اگر محتوای Partial View رشته خالص محسوب شود، به همان صورت نمایش داده شود. ما می خواهیم آن را به عنوان محتوای HTML به حساب بیاورد و برای این کار باید Razor را از رمزنگاری متوقف کنیم، بنابراین Partial View طوری طراحی شده است که MvcHtmlString برمی گرداند.

کدام یک ارجحیت دارد، Html.RenderPartial یا Html.Partial؟

Html.RenderPartial پیشنهاد می شود چرا که سریعتر است.

Html.Partial چه زمانی پیشنهاد می شود؟

زمانی که بخواهیم نتیجه را قبل از نمایش با استفاده از Partial View تغییر دهیم،

Index.chtml را باز کرده و کد Footer را به شکل زیر تغییر می دهیم و دوباره تست می کنیم.

@{        
    MvcHtmlString result = Html.Partial ("Footer", Model.FooterData);
    string finalResult = result.ToHtmlString().Replace("2015", "20000");            
}
@MvcHtmlString.Create(finalResult)

حال Footer به شکل زیر در می آید:

حال Footer به شکل زیر در می آید:

چرا Partial View در Shared folder قرار می گیرد؟

Partial View ها برای استفاده مجدد ساخته می شوند، بنابراین بهترین محل برای آنها Shared folder می باشد.

آیا می توانیم Partial View را در فولدر Controller خاصی مانند Employee یا Authentication قرار دهیم؟

بله می توانیم اما در این مورد، این Partial View فقط برای یک Controller در دسترس است.

مثال: اگر ما Partial View را در فولدر Employee نگه داریم، برای AuthenticationController یا Viewهای مرتبط با آن قابل دسترسی نخواهد بود.

چرا به جای اینکه محتوای Footer را به طور مستقیم در View قرار دهیم، Partial View ایجاد کردیم؟

دو مزیت دارد:

  1. قابلیت استفاده مجدد – می توانیم همان Partial View را در View دیگری استفاده کنیم.
  2. نگه داری کد – قرار دادن آن در فایل جداگانه، نگه داری و تغییر آن را آسان تر می کند.

چرا Header به صورت Partial View تعریف نشده است؟

به عنوان بهترین راهکار، Header نیز باید به صورت Partial View تعریف شود اما برای ساده سازی تمرین های اولیه آن را به صورت inline تعریف کردیم.

تمرین بیست و سوم – پیاده سازی امنیت بر اساس Role

در این تمرین می خواهیم امکان ورود Admin و غیر Admin را پیاده سازی کنیم.

کاری که می خواهیم انجام دهیم، بسیار ساده است.

“کاربر غیر Admin نمی تواند کارمند جدید ایجاد کند.”

با این تمرین می خواهیم دو عضو دیگر MVC را معرفی کنیم.

  • Session
  • Action Fillter

برای ساده تر شدن مسئله، این تمرین را به دو بخش تقسیم می کنیم.

بخش ۱: پنهان کردن لینک “کارمند جدید” از دید کاربر غیر Admin

گام ۱: ایجاد Enum برای شناسایی UserStatus

روی فولدر Models راست کلیک کرده و روی “Add New Item” کلیک می کنیم.

و از پنجره باز شده “Code File” را انتخاب می کنیم.

 

نام آن را “UserStatus” گذاشته و روی Add کلیک می کنیم.

“Code File” یک فایل “.cs” خالی ایجاد می کند.

داخل این فایل یک enum به نام UserStatus به صورت زیر ایجاد می کنیم.

 

namespace WebApplication1.Models
{
    public enum UserStatus
    {
        AuthenticatedAdmin,
        AuthentucatedUser,
        NonAuthenticatedUser
    }
}

گام ۲: تغییر کاربرد business layer

تابع IsValidUser را حذف کرده و تابع جدیدی به نام GetUserValidity مانند زیر ایجاد می کنیم:

 

public UserStatus GetUserValidity(UserDetails u)
{
    if (u.UserName == "Admin" && u.Password == "Admin")
    {
        return UserStatus.AuthenticatedAdmin;
    }
    else if (u.UserName == "باران" && u.Password == "بزرگمهر")
    {
        return UserStatus.AuthentucatedUser;
    }
    else
    {
        return UserStatus.NonAuthenticatedUser;
    }
}

گام ۳: تغییر متد DoLogin

AuthenticationController را باز کرده و متد DoLogin را به صورت زیر تغییر می دهیم.

[HttpPost]
public ActionResult DoLogin(UserDetails u)
{
    if (ModelState.IsValid)
    {
        EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
        //New Code Start
        UserStatus status = bal.GetUserValidity(u);
        bool IsAdmin = false;
        if (status==UserStatus.AuthenticatedAdmin)
        {
            IsAdmin = true;
        }
        else if (status == UserStatus.AuthentucatedUser)
        {
            IsAdmin = false;
        }
        else
        {
            ModelState.AddModelError("CredentialError", "Invalid Username or Password");
            return View("Login");
        }
        FormsAuthentication.SetAuthCookie(u.UserName, false);
        Session["IsAdmin"] = IsAdmin;
        return RedirectToAction("Index", "Employee");
        //New Code End
    }
    else
    {
        return View("Login");
    }
}

همان طور که مشاهده می کنید، برای اینکه بدانیم کاربر Admin است یا خیر، از متغیر Session استفاده کردیم.

آیا می دانید Session چیست؟

Session یکی از امکانات ASP.Net است که در ASP.Net MVC نیز استفاده شده است.

ما از Session برای نگه داشتن داده های مرتبط با کاربر استفاده می کنیم. طول عمر یک Session Variable وابسته به طول عمر کاربر است، به محض اینکه Session به پایان برسد دیگر این متغیر در دسترس نخواهد بود.

گام ۴: حذف لینک کارمند جدید

Index.chtml را از فولدر “~/Views/Employee” باز کرده و لینک “کارمند جدید” را به طور کامل حذف می کنیم.

<!-- Remove following line from Index.cshtml -->

<a  href="/Employee/AddNew">Add New</a> 

گام ۵: ایجاد Partial View

روی فولدر “~/Views/Employee” راست کلیک کرده و Add>>View را انتخاب می کنیم. نام View را “َAddNewLink” گذاشته و یادمان هست که باید تیک “Create a partial View” را بزنیم.

گام ۶: قرار دادن محتوا در Partial View

بسیار ساده، محتوای زیر را در Partial View که به تازگی ایجاد کرده ایم، قرار می دهیم.

<a  href="/Employee/AddNew">Add New</a>

 

گام ۷: ایجاد Action Method

EmployeeController را باز کرده و یک Action Method جدید به نام “GetAddNewLink” به صورت زیر ایجاد می کنیم.

public ActionResult GetAddNewLink()
{
    if (Convert.ToBoolean(Session["IsAdmin"]))
    {
        return Partial View("AddNewLink");
    }
    else
    {
        return new EmptyResult();
    }
}

گام ۸: نمایش لینک AddNew

Index.cshtml را باز کرده و کد زیر را در آن می نویسیم.

<a href="/Authentication/Logout">Logout</a>
</div>
<hr />
@{
  Html.RenderAction("GetAddNewLink");
}
<div>
<table border="1">
<tr>

Html.RenderAction، Action Method را اجرا کرده و نتیجه را به طور مستقیم در جریان Response می نویسد.

 

گام ۹: تست و اجرا

کلید F5 را فشار داده و برنامه را اجرا می کنیم.

تست ۱:

تست ۲:

بخش ۲: امنیت آدرس Direct

با منطق بالا یک مورد تضمین می شود. حالا کاربر غیر Admin نمی تواند از طریق لینک به AddNew Action برود.

آیا این کافی است؟

خیر، کافی نیست. اگر کاربر غیر Admin سعی کند با URL به طور مستقیم به این Action Method برود، چه؟

همان طور که می دانید، کاربر غیر Admin به سادگی می تواند به AddNew Action دسترسی پیدا کند.

برای حل این مشکل، از MVC ActionFilters استفاده می کنیم. Action Filters امکان یک پردازش اولیه پیش از اجرا (pre-processing) و پردازش ثانویه بعد از اجرا (post-processing) روی action method را به ما می دهند. در این تمرین به امکان پردازش اولیه Action Filters می پردازیم و تمرین های بعدی درباره post-processing نیز صحبت خواهیم کرد.

گام ۱: تنظیم Filter

فولدر جدیدی به نام Filters ایجاد می کنیم و کلاس جدیدی به نام AdminFilter در آن ایجاد می کنیم.

گام ۲: ایجاد Filter

کلاس AdminFilter را با ارث بری از ActionFilterAttribute به ActionFilter ارتقا می دهیم.

public class AdminFilter:ActionFilterAttribute

{



}

 

توجه داشته باشید که برای استفاده از ActionFilterAttribute باید فضای نام System.Web.Mvc را در بالای کلاس قرار دهیم.

گام ۳: اضافه کردن اعتبارسنجی

متد OnActionExecuting را به صورت زیر در کلاس ActionFilter، Override می کنیم.

public override void OnActionExecuting(ActionExecutingContext filterContext)

{

    if (!Convert.ToBoolean(filterContext.HttpContext.Session["IsAdmin"]))

    {

        filterContext.Result = new ContentResult()

        {

            Content="Unauthorized to access specified resource."

        };

    }

}

گام ۴: اضافه کردن Filter

Filter را به هردو متد AddNew و SaveEmployee اضافه می کنیم:

[AdminFilter]

public ActionResult AddNew()

{

    return View("CreateEmployee",new Employee());

}

...

...

[AdminFilter]

public ActionResult SaveEmployee(Employee e, string BtnSubmit)

{

    switch (BtnSubmit)

    {

        case "Save Employee":

            if (ModelState.IsValid)

            {

                EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();

....

....

 

گام ۵: تست و اجرا

کلید F5 را فشار داده و برنامه را اجرا می کنیم. با استفاده از یک کاربر غیر Admin وارد شده و سعی می کنیم با قرار دادن آدرس AddNew Action به این صفحه برویم.

حال می بینیم که Action Method های ما کاملا امن هستند.

نکته: توجه داشته باشید، استراتژی و منطقی که در این تمرین برای پیاده سازی امنیت براساس Role استفاده شده، ممکن است بهترین راهکار نباشد. می توانید منطق بهتری را برای پیاده سازی این راه حل استفاده کنید، این تنها یکی از روش های پیاده سازی بود.

پرسش و پاسخ در تمرین بیست و سوم:

آیا می توانیم GetAddNewLink را به طور مستقیم از طریق آدرس بار مرورگر فراخوانی کنیم؟

بله، درباره چنین کاری در بخش “پرسش و پاسخ در تمرین بیست و دوم” صحبت کردیم.

آیا می توان اجرای مستقیم GetAddNewLink را متوقف کرد؟

برای این کار باید صفت ChildActionOnly را به GetAddNewLink اضافه می کنیم.

[ChildActionOnly]

public ActionResult GetAddNewLink()

{    if (Convert.ToBoolean(Session["IsAdmin"]))o

    {

 

Html.Action چه کاری انجام می دهد؟

دقیقا مانند Html.RenderAction، Action Method را اجرا می کند و نتیجه را در View نشان می دهد.

نحوه نگارش آن به شکل زیر است:

@Html.Action("GetAddNewLink");

 

تفاوت این دو چیست؟

Html.RenderAction نتیجه action method را به طور مستقیم در جریان HTTP Response می نویسد در حالی که Html.Action نتیجه را به صورت MvcHtmlString برمی گرداند.

کدام یک توصیه می شود Html.RenderAction یا Html.Action؟

Html.Render پیشنهاد می شود چرا که سریعتر است.

Html.Action چه زمانی پیشنهاد می شود؟

زمانی که بخواهیم نتیجه برگردانده شده action method را قبل از نمایش تغییر دهیم.

ActionFilter چیست؟

دقیقا شبیه AuthorizationFilter  یکی از انواع Filter در ASP.Net MVC می باشد. به ما امکان پردازش اولیه و پردازش ثانویه اجرای action method را می دهد.

تمرین بیست و چهارم – پیاده سازی یک ظاهر یکپارچه (Consistent look) در سراسر پروژه

در دنیای ASP.Net منظور از لایه consistent همان MasterPage می باشد. ASP.Net MVC نیز شبیه آن است. در Razor، Mater Pageها به نام Layout Page شناخته می شوند.

قبل از اینکه تمرینمان را شروع کنیم، اجازه بدهید درباره تمام چیزهایی که می خواهیم در Layout Page قرار دهیم، صحبت کنیم.

  1. Header با پیام خوشامد گویی
  2. Footer با داده های Footer

مشکل عمده:

داده های Footer و Header به عنوان بخشی از ViewModel از Controller به View ارسال می شود.

Role در MVC

اما سوالی که پیش می آید این است که بعد از انتقال Header و Footer به Layout Page چگونه داده ها از View به Layout Page ارسال می شود.

راه حل: وراثت

در اینجا به سادگی می توانیم از قانون وراثت در شی گرایی استفاده کنیم.

گام ۱: ایجاد کلاس پایه(Base) ViewModel

یک ViewModel جدید به نام BaseViewModel در فولدر ViewModel ایجاد می کنیم.

public class BaseViewModel

{

    public string UserName { get; set; }

    public FooterViewModel FooterData { get; set; }//New Property

}

 

همان طور که مشاهده می کنید، BaseViewModel هر چیزی را که برای Layout Page لازم است مخفی سازی می کند.

گام ۲: آماده کردن EmployeeListViewModel

Propertyهای UserName و FooterData را از کلاس EmployeeListViewModel حذف می کنیم و از کلاس BaseViewModel آن ها را ارث بری می کنیم.

public class EmployeeListViewModel:BaseViewModel

{

    public List<EmployeeViewModel> Employees { get; set; }

}

 

گام ۳: ایجاد Layout Page

روی Shared folder راست کلیک کرده و Add>>MVC 5 Layout page را انتخاب می کنیم. نام آن را MyLayout گذاشته و روی Ok کلیک می کنیم.

ساختاری مانند زیر ایجاد خواهد شد:

<!DOCTYPE html>



<html>

<head>

    <meta name="viewport" content="width=device-width" />

    <title>@ViewBag.Title</title>

</head>

<body>

    <div>

        @RenderBody()

    </div>

</body>

</html>

 

گام ۴: تبدیل Layout به Strongly typed Layout

دستورات زیر را در بالای Layout Page قرار داده و آن را به نوع Strongly type تبدیل می کنیم.

@using WebApplication1.ViewModels

@model BaseViewModel

 

گام ۵: طراحی Layout Page

در Layout Page، Header، Footer و سه بخش دیگر برای محتوا مانند زیر اضافه می کنیم.

<html>

<head>

    <meta name="viewport" content="width=device-width" />

    <title>@RenderSection("TitleSection")</title>

    @RenderSection("HeaderSection",false)

</head>

<body>

    <div style="text-align:right">

        Hello, @Model.UserName

        <a href="/Authentication/Logout">Logout</a>

    </div>

    <hr />

    <div>

    @RenderSection("ContentBody")

    </div>

    @Html.Partial("Footer",Model.FooterData)

</body>

</html>

 

همان طور که مشاهده می کنید، ما سه بخش در Layout Page ایجاد کردیم. TitleSection، HeaderSection و ContentBody.

نکته: پارامتر دوم HeaderSection نشان می دهد که این بخش، یک بخش اختیاری است یا حتما باید در همه صفحات وجود داشته باشد. False نشان دهنده اختیاری بودن این بخش است.

گام ۶: الصاق Layout Page به Index View

Index.cshtml را باز کرده و در بالای آن کد زیر را مشاهده خواهید کرد:

@{

    Layout = null;

}

 

آن را با کد زیر جایگزین می کنیم:

@{

    Layout = "~/Views/Shared/MyLayout.cshtml";

}

 

گام ۷: طراحی Index View

  • قسمت های Header و Footer را از Index View برمی داریم.
  • بقیه محتوا را در تگ body کپی می کنیم.
  • حالا محتوای تگ Title را کپی می کنیم.
  • تمام محتوای HTML را از View حذف می کنیم. توجه داشته باشید که فقط HTML حذف شود، @Model و دستورات layout نباید تغییر کنند.
  • TitleSection و ContentBody را با محتوایی که اول کار کپی کردیم، تعریف می کنیم.

View کامل شده به شکل زیر خواهد بود:

@using WebApplication1.ViewModels

@model EmployeeListViewModel

@{

    Layout = "~/Views/Shared/MyLayout.cshtml";

}



@section TitleSection{

    MyView

}

@section ContentBody{      

    <div>       

        @{

            Html.RenderAction("GetAddNewLink");

        }

        <table border="1">

            <tr>

                <th>Employee Name</th>

                <th>Salary</th>

            </tr>

            @foreach (EmployeeViewModel item in Model.Employees)

            {

                <tr>

                    <td>@item.EmployeeName</td>

                    <td style="background-color:@item.SalaryColor">@item.Salary</td>

                </tr>

            }

        </table>

    </div>

}

 

همان طور که مشاهده می کنید، هر چیزی در View در یکی از بخش ها تعریف شده است.

گام ۸: تست و اجرا

کلید F5 را فشار داده و برنامه را اجرا می کنیم و به Index Action می رویم.

Role در MVC

گام ۹: اضافه کردن Layout Page به CreateEmployee View

Index.cshtml را باز کرده و در بالای آن کد زیر را پیدا می کنیم.

@{

    Layout = null;

}

 

آن را با کد زیر تغییر می دهیم.

@{

    Layout = "~/Views/Shared/MyLayout.cshtml";

}

 

گام ۱۰: طراحی CreateEmployee View

همان ۷ گام قبلی را طی کرده و بخش های مختلف را در CreateEmployee ایجاد می کنیم. در اینجا یک کار اضافه هم باید انجام دهیم. در اینجا HeaderSection را نیز تعریف می کنیم.

HTML کامل شده آن به شکل زیر خواهد بود:

@using WebApplication1.Models

@model Employee

@{

    Layout = "~/Views/Shared/MyLayout.cshtml";

}



@section TitleSection{

    CreateEmployee

}



@section HeaderSection{

<script src="~/Scripts/Validations.js"></script>

<script>

    function ResetForm() {

        document.getElementById('TxtFName').value = "";

        document.getElementById('TxtLName').value = "";

        document.getElementById('TxtSalary').value = "";

    }

</script>

}

@section ContentBody{

    <div>

        <form action="/Employee/SaveEmployee" method="post" id="EmployeeForm">

            <table>

            <tr>

                <td>

                    First Name:

                </td>

                <td>

                    <input type="text" id="TxtFName" name="FirstName" value="@Model.FirstName" />

                </td>

            </tr>

            <tr>

                <td colspan="2" align="right">

                    @Html.ValidationMessage("FirstName")

                </td>

            </tr>

            <tr>

                <td>

                    Last Name:

                </td>

                <td>

                    <input type="text" id="TxtLName" name="LastName" value="@Model.LastName" />

                </td>

            </tr>

            <tr>

                <td colspan="2" align="right">

                    @Html.ValidationMessage("LastName")

                </td>

            </tr>



            <tr>

                <td>

                    Salary:

                </td>

                <td>

                    <input type="text" id="TxtSalary" name="Salary" value="@Model.Salary" />

                </td>

            </tr>

            <tr>

                <td colspan="2" align="right">

                    @Html.ValidationMessage("Salary")

                </td>

            </tr>



            <tr>

                <td colspan="2">



                    <input type="submit" name="BtnSubmit" value="Save Employee" onclick="return IsValid();" />

                    <input type="submit" name="BtnSubmit" value="Cancel" />

                    <input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />

                </td>

            </tr>

            </table>

    </div>

}

 

گام ۱۱: تست و اجرا

کلید F5 را فشار داده و برنامه را اجرا می کنیم و سعی می کنیم که از طریق لینک به AddNew Action برویم.

Role در MVC

 

Index View از نوع stongly type از کلاس EmployeeListViewModel می باشد که از BaseViewModel ارث بری می کند و به همین دلیل کار می کند. CreateEmployee View نیز از نوع Strongly type از کلاس CreateEmployeeViewModel است اما از BaseViewModel ارث بری نمی کند و از این رو خطای بالا رخ می دهد.

گام ۱۲: آماده کردن CreateEmployeeViewModel

CreateEmployeeViewModel به صورت زیر از BaseViewModel ارث بری می کند.

public class CreateEmployeeViewModel:BaseViewModel

{

...

 

گام ۱۳: تست و اجرا

تست برنامه را دوباره تکرار می کنیم.

خطای این بار کاملا متفاوت از خطای قبلی است. دلیل این خطا این است که ما Header و FooterData را در AddNew action مقداردهی نکرده ایم.

گام ۱۴: مقداردهی اولیه Header و Footer Data

متد AddNew action را به صورت زیر تغییر می دهیم.

public ActionResult AddNew()

{

    CreateEmployeeViewModel employeeListViewModel = new CreateEmployeeViewModel();

    employeeListViewModel.FooterData = new FooterViewModel();

    employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value

    employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString();

    employeeListViewModel.UserName = User.Identity.Name; //New Line

    return View("CreateEmployee", employeeListViewModel);

}

 

گام ۱۵: مقداردهی اولیه Header و Footer Data در SaveEmployee

همان کارها را در SaveEmployee نیز انجام می دهیم.

public ActionResult SaveEmployee(Employee e, string BtnSubmit)

{

    switch (BtnSubmit)

    {

        case "Save Employee":

            if (ModelState.IsValid)

            {

                ...

            }

            else

            {

                CreateEmployeeViewModel vm = new CreateEmployeeViewModel();

                ...

                vm.FooterData = new FooterViewModel();

                vm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value

                vm.FooterData.Year = DateTime.Now.Year.ToString();

                vm.UserName = User.Identity.Name; //New Line

                return View("CreateEmployee", vm); // Day 4 Change - Passing e here

            }

        case "Cancel":

            return RedirectToAction("Index");

    }

    return new EmptyResult();

}

 

گام ۱۶: تست و اجرا

کلید F5 را فشار داده و برنامه را اجرا می کنیم.

 

پرسش و پاسخ در تمرین بیست و چهارم:

RenderBody چگونه کار می کند؟

اول کار که Layout Page را ایجاد می کنیم دستور Razorای به شکل زیر است:

@Html.RenderBody()

 

حال ببینیم که چه کاری انجام می دهد؟

در صفحات محتوا، به طور معمول بخش هایی را تعریف کردیم که در Layout Page تعریف شده بودند. اما نکته عجیب این جاست که Razor این اجازه را به ما می دهد که محتوایی خارج از این بخش ها نیز تعریف کنیم. تمام محتواهای خارج از بخش های تعریف شده توسط RenderBody اجرا می شود.

شکل زیر کارکرد آن را بهتر توضیح می دهد.

آیا می توان Layout های تودرتو داشت؟

بله، ما می توانیم یک Layout Page ایجاد کنیم که از یک Layout Page دیگر استفاده می کند. نحوه نوشتن آن به همان شکل قبل است.

آیا لازم است که layout page را در همه Viewها مشخص کنیم؟

شما یک View خاص به نام ViewStart.cshtml در فولدر Views خواهید یافت. تنظیمات تعریف شده در آن، توسط همه Viewها دریافت خواهد شد.

مثال – بسیار ساده، کد زیر را در ViewStart.cshtml قرار دهید و این view، Layout Page را به بقیه Viewها اعمال می کند.

@{

    Layout = "~/Views/Shared/_Layout.cshtml";

}

 

آیا لازم است که کدهای Header و FooterData در همه action method ها قرار گیرد؟

خیر، لازم نیست. ما این تکرار را با استفاده از Action Filter از بین بردیم.

آیا باید همه بخش ها را در View فرزند تعریف کنیم؟

بله، اگر آن بخش به عنوان بخش اجباری(required) تعریف شده باشد. به طور پیش فرض true است.

@RenderSection("HeaderSection",false) // Not required

@RenderSection("HeaderSection",true) // required

@RenderSection("HeaderSection") // required

 

تمرین بیست و پنجم – بهبود کارایی کدهای Header و FooterData با استفاده از Action Filter

در تمرین بیست و سوم، یکی از مزایای ActionFilter را باهم دیدیم، حالا نوبت دومین مزیت آن است.

گام ۱: حذف کدهای اضافی از Action method ها

کدهای Header و FooterData را از index، AddNew و SaveEmployee حذف می کنیم (در EmployeeController)

کد Header به شکل زیر خواهد بود:

bvm.UserName = HttpContext.Current.User.Identity.Name;

 

و کد Footer نیز به شکل زیر:

bvm.FooterData = new FooterViewModel();

bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value

bvm.FooterData.Year = DateTime.Now.Year.ToString(); 

 

گام ۲: ایجاد HeaderFooterFilter

کلاس جدیدی با نام HeaderFooterFilter در فولدر Filters ایجاد می کنیم و با ارث بری از ActionFilterAttribute آن را به ActionFilter ارتقا می دهیم.

گام ۳: ارتقای ViewModel

متد OnActionExecuted را در کلاس HeaderFooterFilter، Override می کنیم. در این متد ViewModel جاری را دریافت کرده و Header و FooterData را به آن اضافه می کند.

public class HeaderFooterFilter : ActionFilterAttribute

{

    public override void OnActionExecuted(ActionExecutedContext filterContext)

    {

        ViewResult v = filterContext.Result as ViewResult;

        if(v!=null) // v will null when v is not a ViewResult

        {

                BaseViewModel bvm = v.Model as BaseViewModel;

                if(bvm!=null)//bvm will be null when we want a view without Header and footer

                {

                        bvm.UserName = HttpContext.Current.User.Identity.Name;

                        bvm.FooterData = new FooterViewModel();

                        bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value

                        bvm.FooterData.Year = DateTime.Now.Year.ToString();           

                }

        }

    }

}

 

OnActionExecuted برای پردازش ثانویه (post-processing) action method استفاده می شود.

گام ۴: اضافه کردن Filter

HeaderFooterFilter را به  action methodهای Index، AddNew و SaveEmployee اضافه می کنیم.

[HeaderFooterFilter]

public ActionResult Index()

{

    EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();

...

}

...

[AdminFilter]

[HeaderFooterFilter]

public ActionResult AddNew()

{

    CreateEmployeeViewModel employeeListViewModel = new CreateEmployeeViewModel();

    //employeeListViewModel.FooterData = new FooterViewModel();

    //employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";

...

}

...

[AdminFilter]

[HeaderFooterFilter]

public ActionResult SaveEmployee(Employee e, string BtnSubmit)

{

    switch (BtnSubmit)

    {

 

      …

گام ۵: تست و اجرا

کلید F5 را فشار داده و برنامه را اجرا می کنیم.

خب در این جا روز ۵ هم به پایان رسید، با ما در روز ۶ همراه باشید. یقین دارم که روز ششم برای شما جالب خواهد بود.

تمرین یادتون نره!

دانلود فایل PDF

  • پسورد: www.mspsoft.com
فاطمه زکایی

فاطمه زکایی هستم. فارغ التحصیل کارشناسی مهندسی نرم افزار، مدت سه سال هست که در زمینه توسعه اپلیکیشن های تحت وب و اندروید و همچنین تولید محتوای تخصصی برنامه نویسی تحت وب و اندروید در مجموعه mspsoft در خدمت شما هستم.

نوشته‌های مرتبط

دیدگاه‌ها

*
*

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.

    programmer پاسخ

    سلام
    جناب بزرگمر با تشکر فراوان از زحمات شما ما همچنان در انتظار ما بقی آموزشها می باشیم چرا سرعت آموزش کم شده است.
    لطفا دوره جدید را سریعتر بر روی سایت قرار دهید
    با تشکر فراوان

      باران بزرگمهر پاسخ

      سلام ممنون کمی ویراستاری زمان بر بود ولی خوب دوره را سعی کردیم بدون مشکل برای شما اماده کنیم.انشالله که استفاده کرده باشید.

    رامین بهنود پاسخ

    خسته نباشید

      باران بزرگمهر پاسخ

      ممنون :)

    صابری پاسخ

    بسیار عالی.
    خصوصا نگارش متن کاملا حرفه ای انجام شده

    سیاوش پاسخ

    خسته نباشید. چرا فایل پروژه رو برای دانلود نمی گذارید؟؟؟