博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
七天学会ASP.NET MVC (五)——Layout页面使用和用户角色管理
阅读量:6799 次
发布时间:2019-06-26

本文共 14599 字,大约阅读时间需要 48 分钟。

hot3.png

系列文章

 

目录

实验22——添加页脚

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

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

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

总结

 

实验22——添加页脚

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

什么是“分部视图”?

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

1. 创建分部视图的 ViewModel

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

1: public class FooterViewModel
2: {
3:  public string CompanyName { get; set; }
4:  public string Year { get; set; }
5: }

2. 创建分部视图

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

输入View 名称”Footer”,选择复选框“Create as a partial view”,点击添加按钮。

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

3. 在分部View 中显示数据

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

1: @using WebApplication1.ViewModels
2:  
3: @model FooterViewModel
4:  
5: 
6:  
7:  @Model.CompanyName © @Model.Year
8:  
9: 

4.  在Main ViewModel 中包含Footer 数据

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

1: public class EmployeeListViewModel
2: {
3:  public List
Employees { get; set; }
4:  
5:  public string UserName { get; set; }
6:  
7:  public FooterViewModel FooterData { get; set; }//New Property
8: }

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

5. 设置Footer 数据

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

1: public ActionResult Index()
2: {
3:  ...
4:  ...
5:  employeeListViewModel.FooterData = new FooterViewModel();
6:  employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
7:  employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString();
8:  return View("Index", employeeListViewModel);
9: }

6. 显示Footer

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

1: 
2:  @{
3:  Html.RenderPartial("Footer", Model.FooterData);
4:  }
5:  
6: 
7: 

7. 运行,打开Index View

关于实验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:  string MyString = "My Simple String";
3: }
4: @MyString

以上代码会转换为:

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

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

如:

1: @{
2:  string MyString = "My Simple String";
3: }
4: @MvcHtmlString.Create(MyString)

输出:

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

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

 

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

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

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

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

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

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

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

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

创建UserStatus枚举类型,如下:

1: namespace WebApplication1.Models
2: {
3:  public enum UserStatus
4:  {
5:  AuthenticatedAdmin,
6:  AuthentucatedUser,
7:  NonAuthenticatedUser
8:  }
9: }

 2. 修改业务层功能

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

1: public UserStatus GetUserValidity(UserDetails u)
2: {
3:  if (u.UserName == "Admin" && u.Password == "Admin")
4:  {
5:  return UserStatus.AuthenticatedAdmin;
6:  }
7:  else if (u.UserName == "Sukesh" && u.Password == "Sukesh")
8:  {
9:  return UserStatus.AuthentucatedUser;
10:  }
11:  else
12:  {
13:  return UserStatus.NonAuthenticatedUser;
14:  }
15: }

3. 修改DoLogin action方法

打开 AuthenticationController, 修改DoLogin action:

1: [HttpPost]
2: public ActionResult DoLogin(UserDetails u)
3: {
4:  if (ModelState.IsValid)
5:  {
6:  EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
7:  //New Code Start
8:  UserStatus status = bal.GetUserValidity(u);
9:  bool IsAdmin = false;
10:  if (status==UserStatus.AuthenticatedAdmin)
11:  {
12:  IsAdmin = true;
13:  }
14:  else if (status == UserStatus.AuthentucatedUser)
15:  {
16:  IsAdmin = false;
17:  }
18:  else
19:  {
20:  ModelState.AddModelError("CredentialError", "Invalid Username or Password");
21:  return View("Login");
22:  }
23:  FormsAuthentication.SetAuthCookie(u.UserName, false);
24:  Session["IsAdmin"] = IsAdmin;
25:  return RedirectToAction("Index", "Employee");
26:  //New Code End
27:  }
28:  else
29:  {
30:  return View("Login");
31:  }
32: }

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

什么是Session?

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

4. 移除存在的 AddNew 链接

打开“~/Views/Employee”文件夹下 Index.cshtml View,移除”Add New“超链接。

Add New

5. 创建分部View

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

6. 输入分部View的内容

在新创建的分部视图中输入以下内容:

Add New

7.  新建 Action 方法

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

1: public ActionResult GetAddNewLink()
2: {
3:  if (Convert.ToBoolean(Session["IsAdmin"]))
4:  {
5:  return Partial View("AddNewLink");
6:  }
7:  else
8:  {
9:  return new EmptyResult();
10:  }
11: }

8.  显示  AddNew 链接

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

1: Logout
2: 
3: 

4: @{
5:  Html.RenderAction("GetAddNewLink");
6: }
7: 
8: 
9: 

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

9. 运行

测试1

测试2

第二部分: 直接URL 安全

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

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

1. 安装过滤器

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

2. 创建过滤器

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

1: public class AdminFilter:ActionFilterAttribute
2: {
3:  
4: }

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

3. 添加安全验证逻辑

在ActionFliter中重写 OnActionExecuting方法:

1: public override void OnActionExecuting(ActionExecutingContext filterContext)
2: {
3:  if (!Convert.ToBoolean(filterContext.HttpContext.Session["IsAdmin"]))
4:  {
5:  filterContext.Result = new ContentResult()
6:  {
7:  Content="Unauthorized to access specified resource."
8:  };
9:  }
10: }

4. 绑定过滤器

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

1: [AdminFilter]
2: public ActionResult AddNew()
3: {
4:  return View("CreateEmployee",new Employee());
5: }
6: ...
7: ...
8: [AdminFilter]
9: public ActionResult SaveEmployee(Employee e, string BtnSubmit)
10: {
11:  switch (BtnSubmit)
12:  {
13:  case "Save Employee":
14:  if (ModelState.IsValid)
15:  {
16:  EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
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.NET MVC过滤器中的一种,允许在action 方法中添加预处理和后处理逻辑。

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

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

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

1. 带有欢迎消息的页眉

2. 带有数据的页脚

最大的问题是什么?

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

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

解决方案——继承

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

1. 创建ViewModel基类

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

1: public class BaseViewModel
2: {
3:  public string UserName { get; set; }
4:  public FooterViewModel FooterData { get; set; }//New Property
5: }
 

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

2. 准备 EmployeeListViewModel

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

1: public class EmployeeListViewModel:BaseViewModel
2: {
3:  public List
Employees { get; set; }
4: }

3.  创建布局页面

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

1: 
2:  
3: 
4: 
5:  
6:  @ViewBag.Title
7: 
8: 
9:  
10:  @RenderBody()
11:  
12: 
13: 

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

1: @using WebApplication1.ViewModels
2: @model BaseViewModel

5. 设计布局页面

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

1: 
2: 
3:  
4:  @RenderSection("TitleSection")
5:  @RenderSection("HeaderSection",false)
6: 
7: 
8:  
9:  Hello, @Model.UserName
10:  Logout
11:  
12:  

13:  
14:  @RenderSection("ContentBody")
15:  
16:  @Html.Partial("Footer",Model.FooterData)
17: 
18: 

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

6. 在 Index View中绑定布局页面

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

1: @{
2:  Layout = null;
3: }

修改:

1: @{
2:  Layout = "~/Views/Shared/MyLayout.cshtml";
3: }

7.设计Index View

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

完整的View代码如下:

1: @using WebApplication1.ViewModels
2: @model EmployeeListViewModel
3: @{
4:  Layout = "~/Views/Shared/MyLayout.cshtml";
5: }
6:  
7: @section TitleSection{
8:  MyView
9: }
10: @section ContentBody{
11:  
12:  @{
13:  Html.RenderAction("GetAddNewLink");
14:  }
15:  
16:  
17:  Employee Name
18:  Salary
19:  
20:  @foreach (EmployeeViewModel item in Model.Employees)
21:  {
22:  
23:  @item.EmployeeName
24:  @item.Salary
25:  
26:  }
27:  
28:  
29: }

 

8. 运行

9. 在 CreateEmployee 中绑定布局页面

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

1: @{
2:  Layout = "~/Views/Shared/MyLayout.cshtml";
3: }

10. 设计 CreateEmployee  View

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

1: @using WebApplication1.Models
2: @model Employee
3: @{
4:  Layout = "~/Views/Shared/MyLayout.cshtml";
5: }
6:  
7: @section TitleSection{
8:  CreateEmployee
9: }
10:  
11: @section HeaderSection{
12: 
13: 
14:  function ResetForm() {
15:  document.getElementById('TxtFName').value = "";
16:  document.getElementById('TxtLName').value = "";
17:  document.getElementById('TxtSalary').value = "";
18:  }
19: 
20: }
21: @section ContentBody{
22:  
23:  
24:  
25:  
26:  
27:  First Name:
28:  
29:  
30:  
31:  
32:  
33:  
34:  
35:  @Html.ValidationMessage("FirstName")
36:  
37:  
38:  
39:  
40:  Last Name:
41:  
42:  
43:  
44:  
45:  
46:  
47:  
48:  @Html.ValidationMessage("LastName")
49:  
50:  
51:  
52:  
53:  
54:  Salary:
55:  
56:  
57:  
58:  
59:  
60:  
61:  
62:  @Html.ValidationMessage("Salary")
63:  
64:  
65:  
66:  
67:  
68:  
69:  
70:  
71:  
72:  
73:  
74:  
75:  
76: }

11. 运行

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

12. 准备 CreateEmployeeViewModel

使CreateEmployeeViewModel 继承 BaseViewModel,如下:

1: public class CreateEmployeeViewModel:BaseViewModel
2: {
3: ...

13. 运行

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

14. 初始化Header和Footer 数据

修改AddNew方法:

1: public ActionResult AddNew()
2: {
3:  CreateEmployeeViewModel employeeListViewModel = new CreateEmployeeViewModel();
4:  employeeListViewModel.FooterData = new FooterViewModel();
5:  employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
6:  employeeListViewModel.FooterData.Year = DateTime.Now.Year.ToString();
7:  employeeListViewModel.UserName = User.Identity.Name; //New Line
8:  return View("CreateEmployee", employeeListViewModel);
9: }

15. 初始化 SaveEmployee中的Header和 FooterData

1: public ActionResult SaveEmployee(Employee e, string BtnSubmit)
2: {
3:  switch (BtnSubmit)
4:  {
5:  case "Save Employee":
6:  if (ModelState.IsValid)
7:  {
8:  ...
9:  }
10:  else
11:  {
12:  CreateEmployeeViewModel vm = new CreateEmployeeViewModel();
13:  ...
14:  vm.FooterData = new FooterViewModel();
15:  vm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
16:  vm.FooterData.Year = DateTime.Now.Year.ToString();
17:  vm.UserName = User.Identity.Name; //New Line
18:  return View("CreateEmployee", vm); // Day 4 Change - Passing e here
19:  }
20:  case "Cancel":
21:  return RedirectToAction("Index");
22:  }
23:  return new EmptyResult();
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) // Not required
2: @RenderSection("HeaderSection",true) // required
3: @RenderSection("HeaderSection") // required

 

实验25——使用Action Fliter让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 = new FooterViewModel();
2: bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
3: bvm.FooterData.Year = DateTime.Now.Year.ToString();

2.创建HeaderFooter 过滤器

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

3. 升级ViewModel

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

1: public class HeaderFooterFilter : ActionFilterAttribute
2: {
3:  public override void OnActionExecuted(ActionExecutedContext filterContext)
4:  {
5:  ViewResult v = filterContext.Result as ViewResult;
6:  if(v!=null) // v will null when v is not a ViewResult
7:  {
8:  BaseViewModel bvm = v.Model as BaseViewModel;
9:  if(bvm!=null)//bvm will be null when we want a view without Header and footer
10:  {
11:  bvm.UserName = HttpContext.Current.User.Identity.Name;
12:  bvm.FooterData = new FooterViewModel();
13:  bvm.FooterData.CompanyName = "StepByStepSchools";//Can be set to dynamic value
14:  bvm.FooterData.Year = DateTime.Now.Year.ToString();
15:  }
16:  }
17:  }
18: }

4. 绑定过滤器

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

1: [HeaderFooterFilter]
2: public ActionResult Index()
3: {
4:  EmployeeListViewModel employeeListViewModel = new EmployeeListViewModel();
5: ...
6: }
7: ...
8: [AdminFilter]
9: [HeaderFooterFilter]
10: public ActionResult AddNew()
11: {
12:  CreateEmployeeViewModel employeeListViewModel = new CreateEmployeeViewModel();
13:  //employeeListViewModel.FooterData = new FooterViewModel();
14:  //employeeListViewModel.FooterData.CompanyName = "StepByStepSchools";
15: ...
16: }
17: ...
18: [AdminFilter]
19: [HeaderFooterFilter]
20: public ActionResult SaveEmployee(Employee e, string BtnSubmit)
21: {
22:  switch (BtnSubmit)
23:  {
24:  ...

5. 运行

总结

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

在学习了本节Layout页面及用户角色管理之后,你是否也跃跃欲试想要进行MVC开发?不妨试试  这款轻量级控件,它与Visual Studio无缝集成,完全与MVC6和ASP.NET 5.0兼容,将大幅提高工作效率。

原文链接:

转载于:https://my.oschina.net/powertoolsteam/blog/475664

你可能感兴趣的文章
Razor视图引擎 语法
查看>>
JAVA Map 和 List 排序方法
查看>>
快速构建Windows 8风格应用34-构建Toast通知
查看>>
GridView Print and Print Preview
查看>>
PL/SQL之--包
查看>>
SEOer怎样安排一天的工作
查看>>
深入学习golang(4)—new与make
查看>>
就近期面试所见,谈谈求职者的问题和面试官的问题
查看>>
markdown的语法说明
查看>>
高效的SQLSERVER分页查询(推荐)
查看>>
Android布局实现圆角边框
查看>>
linux 重启网卡的方法
查看>>
assert_param
查看>>
JVM参数配置
查看>>
【leetcode】Best Time to Buy and Sell 2(too easy)
查看>>
Centos下Apache使用Symlink访问外部目录出现403
查看>>
TortoiseSVN中Branching和Merging实践
查看>>
Substring with Concatenation of All Words
查看>>
SNF快速开发平台3.0之-界面个性化配置+10种皮肤+7种菜单-Asp.net+MVC4.0+WebAPI+EasyUI+Knockout...
查看>>
耗时两月,NHibernate系列出炉
查看>>