您的位置:首页 > 编程语言 > ASP

七天学会ASP.NET MVC (五)——Layout页面使用和用户角色管理

2016-09-28 10:58 369 查看




系列文章

七天学会ASP.NETMVC(一)——深入理解ASP.NETMVC

七天学会ASP.NETMVC(二)——ASP.NETMVC数据传递

七天学会ASP.NETMVC(三)——ASP.NetMVC数据处理

七天学会ASP.NETMVC(四)——用户授权认证问题

七天学会ASP.NET
MVC(五)——Layout页面使用和用户角色管理

七天学会ASP.NETMVC(六)——线程问题、异常处理、自定义URL

七天学会ASP.NET
MVC(七)——创建单页应用

目录

实验22——添加页脚

实验23——实现用户角色管理

实验24——实现项目外观一致性

实验25——使用Action过滤器让页眉和页脚代码更有效

总结


实验22——添加页脚

在本实验中,我们会在Employee页面添加页脚,通过本实验理解分部视图。

什么是“分部视图”?

从逻辑上看,分部视图是一种可重用的视图,不会直接显示,包含于其他视图中,作为其视图的一部分来显示。用法与用户控件类似,但不需要编写后台代码。

1.创建分部视图的ViewModel

右击ViewModel文件夹,新建FooterViewModel类,如下:

1:publicclassFooterViewModel

2:{

3:publicstringCompanyName{get;set;}

4:publicstringYear{get;set;}

5:}


2.创建分部视图

右击“~/Views/Shared”文件夹,选择添加->视图。

输入View名称”Footer”,选择复选框“Createasapartialview”,点击添加按钮。

注意:View中的Shared共享文件夹是为每个控制器都可用的文件夹,不是某个特定的控制器所属。

3.在分部View中显示数据

打开Footer.cshtml,输入以下HTML代码。

1:@usingWebApplication1.ViewModels

2:

3:@modelFooterViewModel

4:

5:<divstyle="text-align:right;background-color:silver;color:darkcyan;border:1pxsolidgray;margin-top:2px;padding-right:10px;">

6:

7:@Model.CompanyName©@Model.Year

8:

9:</div>


4.在MainViewModel中包含Footer数据

打开EmployeeListViewModel类,添加新属性,保存Footer数据,如下:

1:publicclassEmployeeListViewModel

2:{

3:publicList<EmployeeViewModel>Employees{get;set;}

4:

5:publicstringUserName{get;set;}

6:

7:publicFooterViewModelFooterData{get;set;}//NewProperty

8:}


在本实验中Footer会作为IndexView的一部分显示,因此需要将Footer的数据传到IndexView页面中。IndexView是EmployeeListViewModel的强类型View,因此Footer需要的所有数据都应该封装在EmployeeListViewModel中。

5.设置Footer数据

打开EmployeeController,在Indexaction方法中设置FooterData属性值,如下:

1:publicActionResultIndex()

2:{

3:...

4:...

5:employeeListViewModel.FooterData=newFooterViewModel();

6:employeeListViewModel.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue

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

8:returnView("Index",employeeListViewModel);

9:}


6.显示Footer

打开Index.cshtml文件,在Table标签后显示Footer分部View,如下:

1:</table>

2:@{

3:Html.RenderPartial("Footer",Model.FooterData);

4:}

5:</div>

6:</body>

7:</html>


7.运行,打开IndexView


关于实验22

Html.Partial的作用是什么?与Html.RenderPartial区别是什么?

与Html.RenderPartial作用相同,Html.Partial会在View中用来显示分部View。

Html.RenderPartial会将分部View的结果直接写入HTTP响应流中,而Html.Partial会返回MvcHtmlString值。

什么是MvcHtmlString,为什么Html.Partial返回的是MvcHtmlString而不是字符串?

根据MSDN规定,”MvcHtmlString”代表了一个HTML编码的字符串,不需要二次编码。代码如下:

1:@{

2:stringMyString="MySimpleString";

3:}

4:@MyString


以上代码会转换为:

Razor显示了全部的内容,许多人会认为已经看到加粗的字符串,是RazorHtml在显示内容之前将内容编码,这就是为什么使用纯内容来代替粗体。

当不适用razor编码时,使用MvcHtmlString,MvcHtmlString是razor的一种表示,即“字符串已经编码完毕,不需要其他编码”。

如:

1:@{

2:stringMyString="MySimpleString";

3:}

4:@MvcHtmlString.Create(MyString)



输出:

Html.RenderAction和Html.Action两者之间有什么不同?更推荐使用哪种方法?

Html.RenderAction会将Action方法的执行结果直接写入HTTP响应请求流中,而Html.Action会返回MVCHTML字符串。更推荐使用Html.RenderAction,因为它更快。当我们想在显示前修改action执行的结果时,推荐使用Html.Action。


实验23——实现用户角色管理

在实验23中我们将实现管理员和非管理员登录的功能。需求很简单:非管理员用户没有创建新Employee的权限。实验23会帮助大家理解MVC提供的Session和Action过滤器。

因此我们将实验23分为两部分:


第一部分:非管理员用户登录时,隐藏AddNew链接

1.创建标识用户身份的枚举类型

右击Model文件夹,选择添加新项目。选择“CodeFile”选项。

输入“UserStatus”名,点击添加。

“CodeFile”选项会创建一个“.cs”文件.

创UserStatus枚举类型,如下:

1:namespaceWebApplication1.Models

2:{

3:publicenumUserStatus

4:{

5:AuthenticatedAdmin,

6:AuthentucatedUser,

7:NonAuthenticatedUser

8:}

9:}


2.修改业务层功能

删除IsValidUser函数,创建新函数“GetUserValidity“,如下:

1:publicUserStatusGetUserValidity(UserDetailsu)

2:{

3:if(u.UserName=="Admin"&&u.Password=="Admin")

4:{

5:returnUserStatus.AuthenticatedAdmin;

6:}

7:elseif(u.UserName=="Sukesh"&&u.Password=="Sukesh")

8:{

9:returnUserStatus.AuthentucatedUser;

10:}

11:else

12:{

13:returnUserStatus.NonAuthenticatedUser;

14:}

15:}


3.修改DoLoginaction方法

打开AuthenticationController,修改DoLoginaction:

1:[HttpPost]

2:publicActionResultDoLogin(UserDetailsu)

3:{

4:if(ModelState.IsValid)

5:{

6:EmployeeBusinessLayerbal=newEmployeeBusinessLayer();

7://NewCodeStart

8:UserStatusstatus=bal.GetUserValidity(u);

9:boolIsAdmin=false;

10:if(status==UserStatus.AuthenticatedAdmin)

11:{

12:IsAdmin=true;

13:}

14:elseif(status==UserStatus.AuthentucatedUser)

15:{

16:IsAdmin=false;

17:}

18:else

19:{

20:ModelState.AddModelError("CredentialError","InvalidUsernameorPassword");

21:returnView("Login");

22:}

23:FormsAuthentication.SetAuthCookie(u.UserName,false);

24:Session["IsAdmin"]=IsAdmin;

25:returnRedirectToAction("Index","Employee");

26://NewCodeEnd

27:}

28:else

29:{

30:returnView("Login");

31:}

32:}


在上述代码中,已经出现Session变量来识别用户身份。

什么是Session?

Session是Asp.Net的特性之一,可以在MVC中重用,可用于暂存用户相关数据,session变量周期是穿插于整个用户生命周期的。

4.移除存在的AddNew链接

打开“~/Views/Employee”文件夹下Index.cshtmlView,移除”AddNew“超链接。
<!--RemovefollowinglinefromIndex.cshtml-->

<ahref="/Employee/AddNew">AddNew</a>


5.创建分部View

右击“~/Views/Employee”文件夹,选择添加View,设置View名称”“AddNewLink”“,选中”CreateapartialView“复选框。

6.输入分部View的内容

在新创建的分部视图中输入以下内容:
<ahref="/Employee/AddNew">AddNew</a>


7.新建Action方法

打开EmployeeController,新建Action方法”GetAddNewLink“,如下:

1:publicActionResultGetAddNewLink()

2:{

3:if(Convert.ToBoolean(Session["IsAdmin"]))

4:{

5:returnPartialView("AddNewLink");

6:}

7:else

8:{

9:returnnewEmptyResult();

10:}

11:}


8.显示AddNew链接

打开Index.html,输入以下代码:

1:<ahref="/Authentication/Logout">Logout</a>

2:</div>

3:<hr/>

4:@{

5:Html.RenderAction("GetAddNewLink");

6:}

7:<div>

8:<tableborder="1">

9:<tr>


Html.RenderAction执行Action方法,并将结果直接写入响应流中。

9.运行

测试1

测试2


第二部分:直接URL安全

以上实验实现了非管理员用户无法导航到AddNew链接。这样还不够,如果非管理员用户直接输入AddNewURL,则会直接跳转到此页面。

非管理员用户还是可以直接访问AddNew方法,为了解决这个问题,我们会引入MVCaction过滤器。Action过滤器使得在action方法中添加一些预处理和后处理的逻辑判断问题。在整个实验中,会注重ActionFilters预处理的支持和后处理的功能。

1.安装过滤器

新建文件夹Filters,新建类”AdminFilter“。

2.创建过滤器

通过继承ActionFilterAttribute,将AdminFilter类升级为”ActionFilter“,如下:

1:publicclassAdminFilter:ActionFilterAttribute

2:{

3:

4:}


注意:使用”ActionFilterAttribute“需要在文件顶部输入”System.Web.Mvc“。

3.添加安全验证逻辑

在ActionFliter中重写OnActionExecuting方法:

1:publicoverridevoidOnActionExecuting(ActionExecutingContextfilterContext)

2:{

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

4:{

5:filterContext.Result=newContentResult()

6:{

7:Content="Unauthorizedtoaccessspecifiedresource."

8:};

9:}

10:}


4.绑定过滤器

在AddNew和SaveEmployee方法中绑定过滤器,如下:

1:[AdminFilter]

2:publicActionResultAddNew()

3:{

4:returnView("CreateEmployee",newEmployee());

5:}

6:...

7:...

8:[AdminFilter]

9:publicActionResultSaveEmployee(Employeee,stringBtnSubmit)

10:{

11:switch(BtnSubmit)

12:{

13:case"SaveEmployee":

14:if(ModelState.IsValid)

15:{

16:EmployeeBusinessLayerempBal=newEmployeeBusinessLayer();

17:....

18:....


5.运行


关于实验23

可以通过浏览器直接调用GetAddNewLink方法吗?

可以直接调用,也可直接停止”GetAddNewLink“的运行。

Html.Action有什么作用?

与Html.RenderAction作用相同,Html.Action会执行action方法,并在View中显示结果。

语法:

1:@Html.Action("GetAddNewLink");


Html.RenderAction和Html.Action两者之间有什么不同?更推荐使用哪种方法?

Html.RenderAction会将Action方法的执行结果直接写入HTTP响应请求流中,而Html.Action会返回MVCHTMLString。更推荐使用Html.RenderAction,因为它更快。当我们想在显示前修改action执行的结果时,推荐使用Html.Action。

什么是ActionFilter?

与AuthorizationFilter类似,ActionFilter是ASP.NETMVC过滤器中的一种,允许在action方法中添加预处理和后处理逻辑。


实验24——实现项目外观的一致性

在ASP.NET能够保证外观一致性的是母版页的使用。MVC却不同于ASP.NET,在RAZOR中,母版页称为布局页面。

在开始实验之前,首先来了解布局页面

1.带有欢迎消息的页眉

2.带有数据的页脚

最大的问题是什么?

带有数据的页脚和页眉作为ViewModel的一部分传从Controller传给View。

现在最大的问题是在页眉和页脚移动到布局页面后,如何将数据从View传给Layout页面。

解决方案——继承

可使用继承原则,通过实验来深入理解。

1.创建ViewModel基类

在ViewModel文件夹下新建ViewModel类”BaseViewModel“,如下:

1:publicclassBaseViewModel

2:{

3:publicstringUserName{get;set;}

4:publicFooterViewModelFooterData{get;set;}//NewProperty

5:}




BaseViewModel可封装布局页所需要的所有值。

2.准备EmployeeListViewModel

删除EmployeeListViewModel类的UserName和FooterData属性,并继承BaseViewModel:

1:publicclassEmployeeListViewModel:BaseViewModel

2:{

3:publicList<EmployeeViewModel>Employees{get;set;}

4:}


3.创建布局页面

右击shared文件夹,选择添加>>MVC5LayoutPage。输入名称”MyLayout“,点击确认

1:<!DOCTYPEhtml>

2:

3:<html>

4:<head>

5:<metaname="viewport"content="width=device-width"/>

6:<title>@ViewBag.Title</title>

7:</head>

8:<body>

9:<div>

10:@RenderBody()

11:</div>

12:</body>

13:</html>


4.将布局转换为强类型布局

1:@usingWebApplication1.ViewModels

2:@modelBaseViewModel


5.设计布局页面

在布局页面添加页眉,页脚和内容,内容,三部分,如下:

1:<html>

2:<head>

3:<metaname="viewport"content="width=device-width"/>

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

5:@RenderSection("HeaderSection",false)

6:</head>

7:<body>

8:<divstyle="text-align:right">

9:Hello,@Model.UserName

10:<ahref="/Authentication/Logout">Logout</a>

11:</div>

12:<hr/>

13:<div>

14:@RenderSection("ContentBody")

15:</div>

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

17:</body>

18:</html>


如上所示,布局页面包含三部分,TitleSection,HeaderSection和ContentBody,内容页面将使用这些部分来定义合适的内容。

6.在IndexView中绑定布局页面

打开Index.cshtml,在文件顶部会发现以下代码:

1:@{

2:Layout=null;

3:}


修改:

1:@{

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

3:}


7.设计IndexView

从IndexView中去除页眉和页脚
在Body标签中复制保留的内容,并存放在某个地方。
复制Title标签中的内容
移除View中所有的HTML内容,确保只移动了HTML,@model且没有移动layout语句
在复制的内容中定义TitleSection和Contentbody

完整的View代码如下:

1:@usingWebApplication1.ViewModels

2:@modelEmployeeListViewModel

3:@{

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

5:}

6:

7:@sectionTitleSection{

8:MyView

9:}

10:@sectionContentBody{

11:<div>

12:@{

13:Html.RenderAction("GetAddNewLink");

14:}

15:<tableborder="1">

16:<tr>

17:<th>EmployeeName</th>

18:<th>Salary</th>

19:</tr>

20:@foreach(EmployeeViewModeliteminModel.Employees)

21:{

22:<tr>

23:<td>@item.EmployeeName</td>

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

25:</tr>

26:}

27:</table>

28:</div>

29:}


8.运行

9.在CreateEmployee中绑定布局页面

打开Index.cshtml,修改顶部代码:

1:@{

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

3:}


10.设计CreateEmployeeView

与第7步中的程序类似,定义CreateEmployeeView中的Section,在本次定义中只添加一项,如下:

1:@usingWebApplication1.Models

2:@modelEmployee

3:@{

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

5:}

6:

7:@sectionTitleSection{

8:CreateEmployee

9:}

10:

11:@sectionHeaderSection{

12:<scriptsrc="~/Scripts/Validations.js"></script>

13:<script>

14:functionResetForm(){

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

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

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

18:}

19:</script>

20:}

21:@sectionContentBody{

22:<div>

23:<formaction="/Employee/SaveEmployee"method="post"id="EmployeeForm">

24:<table>

25:<tr>

26:<td>

27:FirstName:

28:</td>

29:<td>

30:<inputtype="text"id="TxtFName"name="FirstName"value="@Model.FirstName"/>

31:</td>

32:</tr>

33:<tr>

34:<tdcolspan="2"align="right">

35:@Html.ValidationMessage("FirstName")

36:</td>

37:</tr>

38:<tr>

39:<td>

40:LastName:

41:</td>

42:<td>

43:<inputtype="text"id="TxtLName"name="LastName"value="@Model.LastName"/>

44:</td>

45:</tr>

46:<tr>

47:<tdcolspan="2"align="right">

48:@Html.ValidationMessage("LastName")

49:</td>

50:</tr>

51:

52:<tr>

53:<td>

54:Salary:

55:</td>

56:<td>

57:<inputtype="text"id="TxtSalary"name="Salary"value="@Model.Salary"/>

58:</td>

59:</tr>

60:<tr>

61:<tdcolspan="2"align="right">

62:@Html.ValidationMessage("Salary")

63:</td>

64:</tr>

65:

66:<tr>

67:<tdcolspan="2">

68:

69:<inputtype="submit"name="BtnSubmit"value="SaveEmployee"onclick="returnIsValid();"/>

70:<inputtype="submit"name="BtnSubmit"value="Cancel"/>

71:<inputtype="button"name="BtnReset"value="Reset"onclick="ResetForm();"/>

72:</td>

73:</tr>

74:</table>

75:</div>

76:}


11.运行

IndexView是EmployeeListViewModel类型的强View类型,是BaseViewModel的子类,这就是为什么IndexView可一直发挥作用。CreateEmployeeView是CreateEmployeeViewModel的强类型,并不是BaseViewModel的子类,因此会出现以上错误。

12.准备CreateEmployeeViewModel

使CreateEmployeeViewModel继承BaseViewModel,如下:

1:publicclassCreateEmployeeViewModel:BaseViewModel

2:{

3:...


13.运行

报错,该错误好像与步骤11中的错误完全不同,出现这些错误的根本原因是未初始化AddNewaction方法中的Header和Footer数据。

14.初始化Header和Footer数据

修改AddNew方法:

1:publicActionResultAddNew()

2:{

3:CreateEmployeeViewModelemployeeListViewModel=newCreateEmployeeViewModel();

4:employeeListViewModel.FooterData=newFooterViewModel();

5:employeeListViewModel.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue

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

7:employeeListViewModel.UserName=User.Identity.Name;//NewLine

8:returnView("CreateEmployee",employeeListViewModel);

9:}


15.初始化SaveEmployee中的Header和FooterData

1:publicActionResultSaveEmployee(Employeee,stringBtnSubmit)

2:{

3:switch(BtnSubmit)

4:{

5:case"SaveEmployee":

6:if(ModelState.IsValid)

7:{

8:...

9:}

10:else

11:{

12:CreateEmployeeViewModelvm=newCreateEmployeeViewModel();

13:...

14:vm.FooterData=newFooterViewModel();

15:vm.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue

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

17:vm.UserName=User.Identity.Name;//NewLine

18:returnView("CreateEmployee",vm);//Day4Change-Passingehere

19:}

20:case"Cancel":

21:returnRedirectToAction("Index");

22:}

23:returnnewEmptyResult();

24:}


16.运行


关于实验24

RenderBody有什么作用?

之前创建了Layout页面,包含一个Razor语句如:

1:@Html.RenderBody()


首先我们先来了RenderBody是用来做什么的?

在内容页面,通常会定义Section,声明Layout页面。但是奇怪的是,Razor允许定义在Section外部定义一些内容。所有的非section内容会使用RenderBody函数来渲染,下图能够更好的理解:

布局是否可嵌套?

可以嵌套,创建Layout页面,可使用其他存在的Layout页面,语法相同。

是否需要为每个View定义Layout页面?

会在View文件夹下发现特殊的文件“__ViewStart.cshtml”,在其内部的设置会应用所有的View。

例如:在__ViewStart.cshtml中输入以下代码,并给所有View设置Layout页面。

1:@{

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

3:}


是否在每个Action方法中需要加入Header和Footer数据代码?

不需要,可在Action过滤器的帮助下删除重复的代码。

是否强制定义了所有子View中的Section?

是的,如果Section定义为需要的section,默认的值会设置为true。如下

1:@RenderSection("HeaderSection",false)//Notrequired

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

3:@RenderSection("HeaderSection")//required



实验25——使用ActionFliter让Header和Footer数据更有效

在实验23中,我们已经知道了使用ActionFilter的一个优点,现在来看看使用ActionFilter的其他好处

1.删除Action方法中的冗余代码

删除Index,AddNew,SaveEmployee方法中的Header和Footer数据代码。

Header代码如:

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


Footer代码如:

1:bvm.FooterData=newFooterViewModel();

2:bvm.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue

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


2.创建HeaderFooter过滤器

在Filter文件夹下新建类”HeaderFooterFilter“,并通过继承ActionFilterAttribute类升级为ActionFilter

3.升级ViewModel

重写HeaderFooterFilter类的OnActionExecuted方法,在该方法中获取当前ViewModel,并绑定Header和Footer数据。

1:publicclassHeaderFooterFilter:ActionFilterAttribute

2:{

3:publicoverridevoidOnActionExecuted(ActionExecutedContextfilterContext)

4:{

5:ViewResultv=filterContext.ResultasViewResult;

6:if(v!=null)//vwillnullwhenvisnotaViewResult

7:{

8:BaseViewModelbvm=v.ModelasBaseViewModel;

9:if(bvm!=null)//bvmwillbenullwhenwewantaviewwithoutHeaderandfooter

10:{

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

12:bvm.FooterData=newFooterViewModel();

13:bvm.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue

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

15:}

16:}

17:}

18:}


4.绑定过滤器

在Index中,AddNew,SaveEmployee的action方法中绑定HeaderFooterFilter

1:[HeaderFooterFilter]

2:publicActionResultIndex()

3:{

4:EmployeeListViewModelemployeeListViewModel=newEmployeeListViewModel();

5:...

6:}

7:...

8:[AdminFilter]

9:[HeaderFooterFilter]

10:publicActionResultAddNew()

11:{

12:CreateEmployeeViewModelemployeeListViewModel=newCreateEmployeeViewModel();

13://employeeListViewModel.FooterData=newFooterViewModel();

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

15:...

16:}

17:...

18:[AdminFilter]

19:[HeaderFooterFilter]

20:publicActionResultSaveEmployee(Employeee,stringBtnSubmit)

21:{

22:switch(BtnSubmit)

23:{

24:...


5.运行


总结

本文主要介绍了ASP.NETMVC中页眉页脚的添加和Layout页面的使用,并实现了用户角色分配及ActionFilter的使用,下一节中我们将是最难和最有趣的一篇,请持续关注吧!

在学习了本节Layout页面及用户角色管理之后,你是否也跃跃欲试想要进行MVC开发?不妨试试ComponentOneStudioASP.NETMVC这款轻量级控件,它与Visual
Studio无缝集成,完全与MVC6和ASP.NET5.0兼容,将大幅提高工作效率.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐