七天学会ASP.NET MVC (五)——Layout页面使用和用户角色管理
2015-07-07 14:04
621 查看
系列文章
[b]
目录
实验22——添加页脚
实验23——实现用户角色管理
实验24——实现项目外观一致性
实验25——使用Action过滤器让页眉和页脚代码更有效
总结
实验22——添加页脚
在本实验中,我们会在Employee页面添加页脚,通过本实验理解分部视图。什么是“分部视图”?
从逻辑上看,分部视图是一种可重用的视图,不会直接显示,包含于其他视图中,作为其视图的一部分来显示。用法与用户控件类似,但不需要编写后台代码。
1.创建分部视图的ViewModel
右击ViewModel文件夹,新建FooterViewModel类,如下:
publicclassFooterViewModel
[code]{
publicstringCompanyName{get;set;}
publicstringYear{get;set;}
}
[/code]
2.创建分部视图
右击“~/Views/Shared”文件夹,选择添加->视图。
输入View名称”Footer”,选择复选框“Createasapartialview”,点击添加按钮。
注意:View中的Shared共享文件夹是为每个控制器都可用的文件夹,不是某个特定的控制器所属。
3.在分部View中显示数据
打开Footer.cshtml,输入以下HTML代码。
@usingWebApplication1.ViewModels
[code]
@modelFooterViewModel
<divstyle="text-align:right;background-color:silver;color:darkcyan;border:1pxsolidgray;margin-top:2px;padding-right:10px;">
@Model.CompanyName©@Model.Year
</div>
[/code]
4.在MainViewModel中包含Footer数据
打开EmployeeListViewModel类,添加新属性,保存Footer数据,如下:
publicclassEmployeeListViewModel
[code]{
publicList<EmployeeViewModel>Employees{get;set;}
publicstringUserName{get;set;}
publicFooterViewModelFooterData{get;set;}//NewProperty
}
[/code]
在本实验中Footer会作为IndexView的一部分显示,因此需要将Footer的数据传到IndexView页面中。IndexView是EmployeeListViewModel的强类型View,因此Footer需要的所有数据都应该封装在EmployeeListViewModel中。
5.设置Footer数据
打开EmployeeController,在Indexaction方法中设置FooterData属性值,如下:
publicActionResultIndex()
[code]{
...
...
employeeListViewModel.FooterData=newFooterViewModel();
employeeListViewModel.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue
employeeListViewModel.FooterData.Year=DateTime.Now.Year.ToString();
returnView("Index",employeeListViewModel);
}
[/code]
6.显示Footer
打开Index.cshtml文件,在Table标签后显示Footer分部View,如下:
</table>
[code]@{
Html.RenderPartial("Footer",Model.FooterData);
}
</div>
</body>
</html>
[/code]
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编码的字符串,不需要二次编码。代码如下:
@{
[code]stringMyString="MySimpleString";
}
@MyString
[/code]
以上代码会转换为:
Razor显示了全部的内容,许多人会认为已经看到加粗的字符串,是RazorHtml在显示内容之前将内容编码,这就是为什么使用纯内容来代替粗体。
当不适用razor编码时,使用MvcHtmlString,MvcHtmlString是razor的一种表示,即“字符串已经编码完毕,不需要其他编码”。
如:
@{
[code]stringMyString="MySimpleString";
}
@MvcHtmlString.Create(MyString)
[/code]
输出:
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枚举类型,如下:
namespaceWebApplication1.Models
[code]{
publicenumUserStatus
{
AuthenticatedAdmin,
AuthentucatedUser,
NonAuthenticatedUser
}
}
[/code]
2.修改业务层功能
删除IsValidUser函数,创建新函数“GetUserValidity“,如下:
publicUserStatusGetUserValidity(UserDetailsu)
[code]{
if(u.UserName=="Admin"&&u.Password=="Admin")
{
returnUserStatus.AuthenticatedAdmin;
}
elseif(u.UserName=="Sukesh"&&u.Password=="Sukesh")
{
returnUserStatus.AuthentucatedUser;
}
else
{
returnUserStatus.NonAuthenticatedUser;
}
}
[/code]
3.修改DoLoginaction方法
打开AuthenticationController,修改DoLoginaction:
[HttpPost]
[code]publicActionResultDoLogin(UserDetailsu)
{
if(ModelState.IsValid)
{
EmployeeBusinessLayerbal=newEmployeeBusinessLayer();
//NewCodeStart
UserStatusstatus=bal.GetUserValidity(u);
boolIsAdmin=false;
if(status==UserStatus.AuthenticatedAdmin)
{
IsAdmin=true;
}
elseif(status==UserStatus.AuthentucatedUser)
{
IsAdmin=false;
}
else
{
ModelState.AddModelError("CredentialError","InvalidUsernameorPassword");
returnView("Login");
}
FormsAuthentication.SetAuthCookie(u.UserName,false);
Session["IsAdmin"]=IsAdmin;
returnRedirectToAction("Index","Employee");
//NewCodeEnd
}
else
{
returnView("Login");
}
}
[/code]
在上述代码中,已经出现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“,如下:
publicActionResultGetAddNewLink()
[code]{
if(Convert.ToBoolean(Session["IsAdmin"]))
{
returnPartialView("AddNewLink");
}
else
{
returnnewEmptyResult();
}
}
[/code]
8.显示AddNew链接
打开Index.html,输入以下代码:
<ahref="/Authentication/Logout">Logout</a>
[code]</div>
<hr/>
@{
Html.RenderAction("GetAddNewLink");
}
<div>
<tableborder="1">
<tr>
[/code]
Html.RenderAction执行Action方法,并将结果直接写入响应流中。
9.运行
测试1
测试2
第二部分:直接URL安全
以上实验实现了非管理员用户无法导航到AddNew链接。这样还不够,如果非管理员用户直接输入AddNewURL,则会直接跳转到此页面。非管理员用户还是可以直接访问AddNew方法,为了解决这个问题,我们会引入MVCaction过滤器。Action过滤器使得在action方法中添加一些预处理和后处理的逻辑判断问题。在整个实验中,会注重ActionFilters预处理的支持和后处理的功能。
1.安装过滤器
新建文件夹Filters,新建类”AdminFilter“。
2.创建过滤器
通过继承ActionFilterAttribute,将AdminFilter类升级为”ActionFilter“,如下:
publicclassAdminFilter:ActionFilterAttribute
[code]{
}
[/code]
注意:使用”ActionFilterAttribute“需要在文件顶部输入”System.Web.Mvc“。
3.添加安全验证逻辑
在ActionFliter中重写OnActionExecuting方法:
publicoverridevoidOnActionExecuting(ActionExecutingContextfilterContext)
[code]{
if(!Convert.ToBoolean(filterContext.HttpContext.Session["IsAdmin"]))
{
filterContext.Result=newContentResult()
{
Content="Unauthorizedtoaccessspecifiedresource."
};
}
}
[/code]
4.绑定过滤器
在AddNew和SaveEmployee方法中绑定过滤器,如下:
[AdminFilter]
[code]publicActionResultAddNew()
{
returnView("CreateEmployee",newEmployee());
}
...
...
[AdminFilter]
publicActionResultSaveEmployee(Employeee,stringBtnSubmit)
{
switch(BtnSubmit)
{
case"SaveEmployee":
if(ModelState.IsValid)
{
EmployeeBusinessLayerempBal=newEmployeeBusinessLayer();
....
....
[/code]
5.运行
关于实验23
可以通过浏览器直接调用GetAddNewLink方法吗?可以直接调用,也可直接停止”GetAddNewLink“的运行。
Html.Action有什么作用?
与Html.RenderAction作用相同,Html.Action会执行action方法,并在View中显示结果。
语法:
@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“,如下:
publicclassBaseViewModel
[code]{
publicstringUserName{get;set;}
publicFooterViewModelFooterData{get;set;}//NewProperty
}
[/code]
BaseViewModel可封装布局页所需要的所有值。
2.准备EmployeeListViewModel
删除EmployeeListViewModel类的UserName和FooterData属性,并继承BaseViewModel:
publicclassEmployeeListViewModel:BaseViewModel
[code]{
publicList<EmployeeViewModel>Employees{get;set;}
}
[/code]
3.创建布局页面
右击shared文件夹,选择添加>>MVC5LayoutPage。输入名称”MyLayout“,点击确认
<!DOCTYPEhtml>
[code]
<html>
<head>
<metaname="viewport"content="width=device-width"/>
<title>@ViewBag.Title</title>
</head>
<body>
<div>
@RenderBody()
</div>
</body>
</html>
[/code]
4.将布局转换为强类型布局
@usingWebApplication1.ViewModels
[code]@modelBaseViewModel
[/code]
5.设计布局页面
在布局页面添加页眉,页脚和内容,内容,三部分,如下:
<html>
[code]<head>
<metaname="viewport"content="width=device-width"/>
<title>@RenderSection("TitleSection")</title>
@RenderSection("HeaderSection",false)
</head>
<body>
<divstyle="text-align:right">
Hello,@Model.UserName
<ahref="/Authentication/Logout">Logout</a>
</div>
<hr/>
<div>
@RenderSection("ContentBody")
</div>
@Html.Partial("Footer",Model.FooterData)
</body>
</html>
[/code]
如上所示,布局页面包含三部分,TitleSection,HeaderSection和ContentBody,内容页面将使用这些部分来定义合适的内容。
6.在IndexView中绑定布局页面
打开Index.cshtml,在文件顶部会发现以下代码:
@{
[code]Layout=null;
}
[/code]
修改:
@{
[code]Layout="~/Views/Shared/MyLayout.cshtml";
}
[/code]
7.设计IndexView
从IndexView中去除页眉和页脚
在Body标签中复制保留的内容,并存放在某个地方。
复制Title标签中的内容
移除View中所有的HTML内容,确保只移动了HTML,@model且没有移动layout语句
在复制的内容中定义TitleSection和Contentbody
完整的View代码如下:
@usingWebApplication1.ViewModels
[code]@modelEmployeeListViewModel
@{
Layout="~/Views/Shared/MyLayout.cshtml";
}
@sectionTitleSection{
MyView
}
@sectionContentBody{
<div>
@{
Html.RenderAction("GetAddNewLink");
}
<tableborder="1">
<tr>
<th>EmployeeName</th>
<th>Salary</th>
</tr>
@foreach(EmployeeViewModeliteminModel.Employees)
{
<tr>
<td>@item.EmployeeName</td>
<tdstyle="background-color:@item.SalaryColor">@item.Salary</td>
</tr>
}
</table>
</div>
}
[/code]
8.运行
9.在CreateEmployee中绑定布局页面
打开Index.cshtml,修改顶部代码:
@{
[code]Layout="~/Views/Shared/MyLayout.cshtml";
}
[/code]
10.设计CreateEmployeeView
与第7步中的程序类似,定义CreateEmployeeView中的Section,在本次定义中只添加一项,如下:
@usingWebApplication1.Models
[code]@modelEmployee
@{
Layout="~/Views/Shared/MyLayout.cshtml";
}
@sectionTitleSection{
CreateEmployee
}
@sectionHeaderSection{
<scriptsrc="~/Scripts/Validations.js"></script>
<script>
functionResetForm(){
document.getElementById('TxtFName').value="";
document.getElementById('TxtLName').value="";
document.getElementById('TxtSalary').value="";
}
</script>
}
@sectionContentBody{
<div>
<formaction="/Employee/SaveEmployee"method="post"id="EmployeeForm">
<table>
<tr>
<td>
FirstName:
</td>
<td>
<inputtype="text"id="TxtFName"name="FirstName"value="@Model.FirstName"/>
</td>
</tr>
<tr>
<tdcolspan="2"align="right">
@Html.ValidationMessage("FirstName")
</td>
</tr>
<tr>
<td>
LastName:
</td>
<td>
<inputtype="text"id="TxtLName"name="LastName"value="@Model.LastName"/>
</td>
</tr>
<tr>
<tdcolspan="2"align="right">
@Html.ValidationMessage("LastName")
</td>
</tr>
<tr>
<td>
Salary:
</td>
<td>
<inputtype="text"id="TxtSalary"name="Salary"value="@Model.Salary"/>
</td>
</tr>
<tr>
<tdcolspan="2"align="right">
@Html.ValidationMessage("Salary")
</td>
</tr>
<tr>
<tdcolspan="2">
<inputtype="submit"name="BtnSubmit"value="SaveEmployee"onclick="returnIsValid();"/>
<inputtype="submit"name="BtnSubmit"value="Cancel"/>
<inputtype="button"name="BtnReset"value="Reset"onclick="ResetForm();"/>
</td>
</tr>
</table>
</div>
}
[/code]
11.运行
IndexView是EmployeeListViewModel类型的强View类型,是BaseViewModel的子类,这就是为什么IndexView可一直发挥作用。CreateEmployeeView是CreateEmployeeViewModel的强类型,并不是BaseViewModel的子类,因此会出现以上错误。
12.准备CreateEmployeeViewModel
使CreateEmployeeViewModel继承BaseViewModel,如下:
publicclassCreateEmployeeViewModel:BaseViewModel
[code]{
...
[/code]
13.运行
报错,该错误好像与步骤11中的错误完全不同,出现这些错误的根本原因是未初始化AddNewaction方法中的Header和Footer数据。
14.初始化Header和Footer数据
修改AddNew方法:
publicActionResultAddNew()
[code]{
CreateEmployeeViewModelemployeeListViewModel=newCreateEmployeeViewModel();
employeeListViewModel.FooterData=newFooterViewModel();
employeeListViewModel.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue
employeeListViewModel.FooterData.Year=DateTime.Now.Year.ToString();
employeeListViewModel.UserName=User.Identity.Name;//NewLine
returnView("CreateEmployee",employeeListViewModel);
}
[/code]
15.初始化SaveEmployee中的Header和FooterData
publicActionResultSaveEmployee(Employeee,stringBtnSubmit)
[code]{
switch(BtnSubmit)
{
case"SaveEmployee":
if(ModelState.IsValid)
{
...
}
else
{
CreateEmployeeViewModelvm=newCreateEmployeeViewModel();
...
vm.FooterData=newFooterViewModel();
vm.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue
vm.FooterData.Year=DateTime.Now.Year.ToString();
vm.UserName=User.Identity.Name;//NewLine
returnView("CreateEmployee",vm);//Day4Change-Passingehere
}
case"Cancel":
returnRedirectToAction("Index");
}
returnnewEmptyResult();
}
[/code]
16.运行
关于实验24
RenderBody有什么作用?之前创建了Layout页面,包含一个Razor语句如:
@Html.RenderBody()
首先我们先来了RenderBody是用来做什么的?
在内容页面,通常会定义Section,声明Layout页面。但是奇怪的是,Razor允许定义在Section外部定义一些内容。所有的非section内容会使用RenderBody函数来渲染,下图能够更好的理解:
布局是否可嵌套?
可以嵌套,创建Layout页面,可使用其他存在的Layout页面,语法相同。
是否需要为每个View定义Layout页面?
会在View文件夹下发现特殊的文件“__ViewStart.cshtml”,在其内部的设置会应用所有的View。
例如:在__ViewStart.cshtml中输入以下代码,并给所有View设置Layout页面。
@{
[code]Layout="~/Views/Shared/_Layout.cshtml";
}
[/code]
是否在每个Action方法中需要加入Header和Footer数据代码?
不需要,可在Action过滤器的帮助下删除重复的代码。
是否强制定义了所有子View中的Section?
是的,如果Section定义为需要的section,默认的值会设置为true。如下
@RenderSection("HeaderSection",false)//Notrequired
[code]@RenderSection("HeaderSection",true)//required
@RenderSection("HeaderSection")//required
[/code]
实验25——使用ActionFliter让Header和Footer数据更有效
在实验23中,我们已经知道了使用ActionFilter的一个优点,现在来看看使用ActionFilter的其他好处1.删除Action方法中的冗余代码
删除Index,AddNew,SaveEmployee方法中的Header和Footer数据代码。
Header代码如:
bvm.UserName=HttpContext.Current.User.Identity.Name;
Footer代码如:
bvm.FooterData=newFooterViewModel();
[code]bvm.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue
bvm.FooterData.Year=DateTime.Now.Year.ToString();
[/code]
2.创建HeaderFooter过滤器
在Filter文件夹下新建类”HeaderFooterFilter“,并通过继承ActionFilterAttribute类升级为ActionFilter
3.升级ViewModel
重写HeaderFooterFilter类的OnActionExecuted方法,在该方法中获取当前ViewModel,并绑定Header和Footer数据。
publicclassHeaderFooterFilter:ActionFilterAttribute
[code]{
publicoverridevoidOnActionExecuted(ActionExecutedContextfilterContext)
{
ViewResultv=filterContext.ResultasViewResult;
if(v!=null)//vwillnullwhenvisnotaViewResult
{
BaseViewModelbvm=v.ModelasBaseViewModel;
if(bvm!=null)//bvmwillbenullwhenwewantaviewwithoutHeaderandfooter
{
bvm.UserName=HttpContext.Current.User.Identity.Name;
bvm.FooterData=newFooterViewModel();
bvm.FooterData.CompanyName="StepByStepSchools";//Canbesettodynamicvalue
bvm.FooterData.Year=DateTime.Now.Year.ToString();
}
}
}
}
[/code]
4.绑定过滤器
在Index中,AddNew,SaveEmployee的action方法中绑定HeaderFooterFilter
[HeaderFooterFilter]
[code]publicActionResultIndex()
{
EmployeeListViewModelemployeeListViewModel=newEmployeeListViewModel();
...
}
...
[AdminFilter]
[HeaderFooterFilter]
publicActionResultAddNew()
{
CreateEmployeeViewModelemployeeListViewModel=newCreateEmployeeViewModel();
//employeeListViewModel.FooterData=newFooterViewModel();
//employeeListViewModel.FooterData.CompanyName="StepByStepSchools";
...
}
...
[AdminFilter]
[HeaderFooterFilter]
publicActionResultSaveEmployee(Employeee,stringBtnSubmit)
{
switch(BtnSubmit)
{
...
[/code]
5.运行
总结
本文主要介绍了ASP.NETMVC中页眉页脚的添加和Layout页面的使用,并实现了用户角色分配及ActionFilter的使用,下一节中我们将是最难和最有趣的一篇,请持续关注吧!一起ComponentOneStudio!
原文链接:相关文章推荐
- ASP.NET MVC图片管理(删除)
- [漏洞分析] CTF:ASP源码审计认证绕过
- 如何设置ASP.NET页面的运行超时时间
- asp.net Ajax的应用
- OData v4 - Web API 轻量级应用(使用Entity Framwork)-Endpoint
- ASP.NET MVC图片管理(上传,预览与显示)
- asp.net mvc 如何在View中获取Url参数的值
- ASP.NET MVC显示HTML字符串
- ASP.NET中使用Razor语法(C#)怎样获取当前浏览器的cookie
- 让Windows Server 2008 + IIS 7+ ASP.NET 支持10万并发请求
- 用Jasper制作贷款分期报表
- Spring基于注解@AspectJ的AOP
- ASP.NET MVC随想录——创建自定义的Middleware中间件
- asp.net 学习之路之gridView控件之修改数据
- ASP.NET MVC 基于页面的权限管理
- ASP.Net Cache(缓存)
- ASP.Net MVC 使用MySQL 【转】
- ASP.NET MVC与Sql Server交互,把字典数据插入数据库
- Asp.Net Ajax4.5 未知的服務器標記 問題
- ASP.NET MVC与Sql Server交互, 插入数据