第五单元 特殊视图
布局视图和我们在Asp.Net MVC一样,布局视图_Layout.cshtml使得所有视图保持一致的外观变得更加容易,因为我们只有一个要修改的布局视图文件,更改后将立即反映在整个应用程序的所有视图中。
在 ASP.NET Core MVC 中,有一些视图文件,如布局的视图,ViewStart.cshtml 和ViewImports.cshtml 等其他.cshtml 文件的文件名以下划线开头,这些文件名中的前下划线表示这些文件不是直接面向浏览器。
我们可以在单个应用程序中包含多个布局视图文件。比如一个布局视图文件服务为管理员用户,另外一个不同的布局视图文件服务于普通用户。
我们一般将布局视图建在Views/Shared文件夹下,以_Layout.cshtml命名。
-
@RenderBody()
是注入视图特定内容的位置。例如,如果使用此布局视图呈现 index.chtml 视图,则会在我们 调用@RenderBody()方法 的位置注入 index.cshtml 视图内容
-
IsSectionDefined("Scripts")
判断子视图中是否定义了Scripts Section 模块, 假设有子视图中index.cshtml 定义如下:
@{ ViewData["Title"] = "Home Page"; } <form method="get" action="/home/query"> <label for="username">姓名:</label><input name="username" id="username"/> <label for="studentNo">学号:</label><input name="studentno" id="studentNo"/> <input type="submit" value="查询"/> <a href="/home/download">下载文件</a> </form> <!--Scripts section--> @section Scripts{ <script> $('form').submit(function (){ $.get('/home/query',$(this).serialize(),function (data){ console.log(data); }); return false; }); </script> }
此时子视图中已经定义了
@section Scripts
,那么_Layout.cshtml 中的 IsSectionDefined("Scripts") 结果为true
-
@RenderSection("Scripts", false);
name: Scripts 表示 section 的名称
required: false 表示在子视图中,Scripts section 并不是必要的
它的功能与RenderBody() 类似,同样是将子视图中的
@section Scripts
中的内容注入到_Layout.cshtml 中的@RenderSection("Scripts", false)
位置
2. _ViewStart.cshtml 视图
一盘位于 ~/Views/_ViewStart.cshtml
位置 ,表示对Views
文件夹下的所有视图文件起作用。可以将所有子视图中公共的部分放在其中。
作用:所有的视图请求都会先执行_ViewStart.cshtml。
@{ Layout = "_Layout"; // 如果子视图不指定布局视图,则默认为"_Layout.cshtml" } @if (ViewData["Layout"]!=null && ViewData["Layout"].Equals("Admin")) { Layout = "_AdminLayout"; } else { Layout = "_Layout"; }
_AdminLayout.cshtml 布局页代码:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <title>title</title> </head> <body> <div> <h1>_AdminLayout.cshtml 视图布局页</h1> @RenderBody() </div> </body> </html>
子视图代码:
public ViewResult Index() { ViewData["Layout"] = "Admin"; return View(); } <div> <h1>大家好,欢迎来到任我行码农场,Asp.net Mvc</h1> @if (true) { <h1>hello</h1> } else { <p>world</p> } <h1>我的姓名:@Context.Session.GetString("user")</h1> </div>
运行结果:
3. _ViewImport.cshtml 命名导入视图
如果我们在很多页面都使用同一个命名空间,同一个model的话,我们可以在Views/_ViewImports.cshtml文件中添加共用的命名空间,model。
@using StudentManagement.Models; @using StudentManagement.ViewModels; @*还支持以下指令*@ @* @addTagHelper @removeTagHelper @tagHelperPrefix @model @inherits @inject *@
需要注意的是,ViewStart和ViewImports是支持分层的,除了将它放在 Views 文件夹中之外,我们还可以在 Views 文件夹的“Home”子文件夹中放置另一个_ViewImports,在文件 Home 的文件夹中的\_ViewImports
将覆盖在 Shared 文件夹中的\_ViewImports
文件指定的设置。
4. 标签助手
标签助手是服务端代码能够参与在 Razor 文件中创建和呈现HTML元素。例如,内置的 ImageTagHelper 可以将版本号追加到图像名称。无论何时更改图像,服务器都会为图像生成新的唯一版本,因此可以保证客户端获取当前图像(而不是过时的缓存图像)。内置的标签助手多用于常见任务,例如创建表单,链接和加载资源等。标签助手是在 C# 中定义的,它们基于元素名称,属性名称或父标签来定位HTML元素。例如,当应用 LabelTagHelper 特性时,内置的 LabelTagHelper 可以减少 Razor 视图中 HTML和 C# 之间的显示转换。
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
上面的代码使用通配符语法(“ * ”)来指定程序集中的所有标签助手将可用于Views目录或子目录中的每个视图文件。@addTagHelper 之后的第一个参数指定要装载的标签助手,第二个参数 “Microsoft.AspNetCore.Mvc.TagHelpers
” 指定包含标签助手的程序集。
1. 表单标签助手
Microsoft.AspNetCore.Mvc.TagHelpers 是内置的 ASP.NET Core 标签助手的程序集。
@model MvcDemo1.Student @{ ViewBag.Title = "title"; Layout = "_Layout"; } <h2>添加学生信息</h2> <form asp-controller="Student" asp-action="Submit" method="post"> <table class="table table-bordered" style="width: 500px;margin: auto"> <tr> <td colspan="3"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> </td> </tr> <tr> <td> <label asp-for="Account"></label> </td> <td> <input asp-for="Account" class="form-control"/> </td> <td> <span asp-validation-for="Account" class="text-danger"></span> </td> </tr> <tr> <td> <label asp-for="Password"></label> </td> <td> <input asp-for="Password" class="form-control"/> </td> <td> <span asp-validation-for="Password" class="text-danger"></span> </td> </tr> <tr> <td> <label asp-for="ConfirmPassword"></label> </td> <td> <input asp-for="ConfirmPassword" class="form-control"/> </td> <td> <span asp-validation-for="ConfirmPassword" class="text-danger"></span> </td> </tr> <tr> <td> <label asp-for="Hobby"></label> </td> <td colspan="2"> <input type="checkbox" name="Hobby" value="编程"/> 编程 <input type="checkbox" name="Hobby" value="学习"/> 学习 <input type="checkbox" name="Hobby" value="爬山"/> 爬山 </td> </tr> <tr> <td><label asp-for="Gender"></label></td> <td colspan="2"> <input type="radio" name="Gender" value="男"/> 男 <input type="radio" name="Gender" value="女"/> 女 </td> </tr> <tr> <td> <label asp-for="Province"></label> </td> <td colspan="2"> <select asp-for="Province" class="form-control" asp-items="ViewBag.ProvinceList"></select> </td> </tr> <tr> <td> <label asp-for="Birthday"></label> </td> <td > <input asp-for="Birthday" class="form-control" /> </td> <td><span asp-validation-for="Birthday"></span></td> </tr> <tr> <td> <label asp-for="Description" ></label> </td> <td colspan="2"> <textarea asp-for="Description" class="form-control"></textarea> </td> </tr> <tr> <td colspan="3"> <input type="submit" class="btn btn-primary" value="提交"/> </td> </tr> </table> </form> @section Scripts { <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script> } public class Student { [Display(Name = "账号")] [Required(ErrorMessage = "请输入账号")] public String? Account { get; set; } [Display(Name = "密码")] [DataType(DataType.Password)] [Required(ErrorMessage = "请输入密码")] public String? Password { get; set; } [Display(Name = "确认密码")] [DataType(DataType.Password)] [Compare("Password",ErrorMessage = "两次输入的密码不一致")] public String? ConfirmPassword { get; set; } [Display(Name = "爱好")] public String[]? Hobby { get; set; } [Display(Name = "性别")] public String? Gender { get; set; } [Display(Name = "祖籍")] [Required(ErrorMessage = "请选择祖籍")] [Range(1,int.MaxValue,ErrorMessage = "请选择祖籍")] public int Province { get; set; } [Display(Name = "生日")] [DataType(DataType.Date)] [Required(ErrorMessage = "请选择你的生日")] public DateTime Birthday { get; set; } [Display(Name = "简介")] public string? Description { get; set; } } public record Province(int Id, String Name); public IActionResult Create() { List<Province> provinces = new() { new(0, "请选择"), new(1, "江西"), new(2, "北京"), new(3, "台湾") }; // 创建一个下拉框数据源 ViewBag.ProvinceList = new SelectList( provinces, // 下拉框的数据源 "Id", // 选中值设置为Id 属性 "Name" // 显示项设置为Name属性 ); return View(); } [HttpPost] public IActionResult Submit(Student student) { if (ModelState.IsValid) { var entity = Students.FirstOrDefault(p => p.Account!.Equals(student.Account)); if (entity != null) { // 服务端用于逻辑处理产品的错误 key 需要为空 ModelState.AddModelError("", "账号已经存在"); return View("Create"); } Students.Add(student); return Ok("添加成功"); } return BadRequest("请求失败"); }
-
asp-validation-summary
只能用于div 标签中,来至于
ValidationSummaryTagHelper
,其中的ValidationSummary
中包含了三个枚举值:-
All : 显示所有的错误信息
-
ModelOnly : 只显示Model级别的错误信息(不包括所有的属性错误)
-
None: 不显示错误信息,一般用不上
-
-
asp-validation-for
用于显示属性的错误信息,只能用于span标签中
-
SelectList
专用于绑定下拉框数据源,也可以直接绑定一个枚举
enum OrderState{ WaitPay, WaitSend, WaitReceive, Finish, Cancel } <select asp-for="OrderState" asp-items="@Html.GetEnumSelectList<OrderState>()"></select>
2. LinkTagHelper
应用在link元素上,支持备用的样式文件。它具有以下属性:
-
href——指定样式资源的链接地址。
-
asp-href-include——指定所有需要被加载的样式文件路径,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。
-
asp-href-exclude——指定那些不需要被加载的样式文件路径,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。
-
asp-fallback-href——指定备用资源链接地址。
-
asp-fallback-href-include——指定所有需要被加载的备用样式文件路径,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。
-
asp-fallback-href-exclude——指定那些不需要被加载的备用样式文件路径,当有多个时,以逗号来分隔每一个;这里的路径是相对于应用程序中wwwroot的相对路径。
-
asp-fallback-test-class——用来检测加载失败的样式名。
-
asp-fallback-test-property——用来检测资源加载失败所用的测试属性。
-
asp-fallback-test-value——用来检测资源加载失败所用的测试值。
-
asp-file-version——bool值,用来指定是否需要将文件版本信息加入到url地址中。
例如,在下面例子中,当从网络上(http://ajax.aspnetcdn.com/ajax/bootstrap-touch-carousel/0.8.0/css/bootstrap-touch-carousel.css)加载样式文件失败时,加载本地相应的样式文件(~/lib/bootstrap-touch-carousel/css/bootstrap-touch-carousel.css)。通过检测样式类carousel-caption中display属性是否是none来判断网络上样式文件是否加载成功。
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.1/css/bootstrap.min.css" asp-fallback-href-include="~/lib/bootstrap/dist/css/bootstrap.css" asp-fallback-test-class="alert-warning" asp-fallback-test-property="color" asp-fallback-test-value="#664d03" />
3. EnvironmentTagHelper
应用在environment元素上,根据不同names的设置呈现不同的内容。它支持以下属性:
-
names——指定环境名,当有多个时候以逗号分隔。这里判断的依据是,读取IHostingEnvironment的EnvironmentName的值,与environment元素中的names匹配,当匹配上的时候就呈现出里面的内容,否则移除该environment元素。
在很多情况下,我们想在开发环境使用一套配置信息,在生产环境又是另外一套,这时候就需要使用条件判断语句了,不过在新版的MVC中,使用EnvironmentTagHelper提供的Environment元素标签就可以了,示例如下:
<environment names="Development"> <link rel="stylesheet" href="~/lib2/bootstrap/dist/css/bootstrap.css" /> <link rel="stylesheet" href="~/css/site.css" /> </environment> <environment names="Staging,Production"> <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css" asp-fallback-href="~/lib2/bootstrap/dist/css/bootstrap.min.css" asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> </environment>
在上述代码中,我们定于,如果是Development环境就使用本地的js文件,否则(Staging或Production环境)就使用网络上的css文件。asp-append-version="true"为静态文件提供唯一版本号。
4. ScriptTagHelper
应用在script元素上,和LinkTagHelper一样,它也具有fallback功能, 只不过这里判断的不是class样式,而是用来判断默认的js文件是否加载成功。
它支持以下属性:
src指定要加载的js源地址。
asp-src-include指定要加载的js文件,当有多个文件时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。
asp-src-exclude指定不需要加载的js文件,当有多个文件时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。
asp-fallback-src指定备用的js源地址。
asp-fallback-src-include指定需要加载的备用js文件,当有多个文件格式时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。
asp-fallback-src-exclude指定不需要加载的备用js文件,当有多个文件时以逗号分隔。这里文件路径是相对于程序webroot的相对路径。
asp-file-version——bool值,用来指定是否需要将文件版本信息加入到url地址中。
<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.10.2.min.js" asp-fallback-src="~/lib/jquery/jquery.min.js"> </script>
此外,还可以根据需要自定义TagHelper。
5. 局部视图
<partial name="局部视图名" for="局部视图数据源"/>
5. 自定义标签助手
-
所有的标签助手都继承于
TagHelper
类,类名都以TagHelper命名结尾,例如:TableListTagHelper -
重写Process 方法
-
using Microsoft.AspNetCore.Razor.TagHelpers; namespace MvcDemo.Util; [HtmlTargetElement("email")] public class EmailTagHelper:TagHelper { // 想要生成的目标html <a href="mailto:1310734881@qq.com">1310734881@qq.com</a> /// <summary> /// 邮箱地址属性 /// </summary> public string? EmailTo { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "a"; // 将email 标签替换成a标签 output.Attributes.SetAttribute("href",$"mailto:{EmailTo}"); // 设置a 标签属性 output.Content.SetContent(EmailTo); // 设置a 标签中间内容 } }
TagHelperContext: 包含了当前执行的TagName 标签元素相关的信息
-
TagHelperOutput:包含了即将生成的html相关信息。
-
[HtmlTargetElement("email")] 表示目标标签名叫"Email"
-
[HtmlTargetElement(Attributes = "email")] 表示此标签只能作为一个名叫email的属性标签
-
-
在_ViewImport.cshtml 中将 TableListTagHelper 类的程序集通过@addTagHelper 指令将自定义的标签助手导入进来。当前我的程序集名称是:
MvcDemo
, 我的tagHelper类是在MvcDemo.Utils
命名空间里。@addTagHelper *,MvcDemo // 导入自定义的标签助手的程序集
-
在Razor视图中使用
-
假设你的自定义标签助手名叫:
EmailTagHelper
,那么视图中写成如下:<email></email>
本例中,EmailTagHelper中有个EmailTo 属性,那么我们应该写成:
<email email-to="xxx@qq.com"></email>
注意属性名单词与单词间用“-”隔开,单词全部小写。
-
假设你的定义标签助手名叫:
TableListTagHelper
,那么视图中写成如下:<table-list></table-list>
-
6. TagHelper 前缀
随着现在前端框架越来越丰富多彩,如果自定义tagHelper 太多,或许你的tagHelper 会与某些前端控件冲突了。
此时我们可以利用TagHelper 前缀
来帮忙解决这个问题(只针对当前页面有效)
@tagHelperPrefix "renwoxing:" // 只针对当前页面有效 <div class="text-center"> <h1 class="display-4">Welcome</h1> <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> </div> <renwoxing:email email-to="131@qq.com"></renwoxing:email> <a> 没有用到自定义标签,不需要加前缀</a> <renwoxing:a asp-action="Privacy">Privacy</renwoxing:a>
我们通过会在前缀尾部加一个冒号,以免与其他标签连在一起了。
一旦加了标签前缀,则所有用到了标签助手的地方,都需要加上前缀。
5. ViewComponent 视图组件
视图组件与分部视图类似,但它们的功能更加强大。 视图组件不使用模型绑定,具体取决于调用视图组件时传递的数据。 本文是使用控制器和视图编写的,但视图组件使用
视图组件:
-
呈现一个区块而不是整个响应。
-
包括控制器和视图间发现的相同关注点分离和可测试性优势。
-
可以有参数和业务逻辑。
-
通常从布局页调用。
视图组件适用于任何对于部分视图来说过于复杂的可重用呈现逻辑,例如:
-
动态导航菜单
-
标记云,在其中查询数据库
-
登录面板
-
购物车
-
最近发布的文章
-
博客上的边栏内容
-
将在每个页面上呈现的登录面板,并显示注销或登录的链接,具体取决于用户的登录状态
视图组件由两个部分组成:
-
类,通常派生自
-
它返回的结果通常是视图。
视图组件类:
-
支持构造函数
-
不参与控制器生命周期,因此无法在视图组件中使用
若要防止具有不区分大小写 ViewComponent
的后缀的类被视为视图组件,请使用 [[NonViewComponent]
该属性修饰类:
using Microsoft.AspNetCore.Mvc; [NonViewComponent] public class ReviewComponent { public string Status(string name) => JobStatus.GetCurrentStatus(name); }
视图组件方法
视图组件在以下项中定义其逻辑:
-
InvokeAsync
返回Task
的方法 。 -
Invoke
返回 .
参数直接来自视图组件的调用,而不是来自模型绑定。 视图组件从不直接处理请求。 通常,视图组件通过调用 View
方法来初始化模型并将其传递到视图。 总之,视图组件方法:
-
定义返回
Task
的InvokeAsync
方法,或是返回IViewComponentResult
的同步Invoke
方法。 -
通常通过调用
-
参数来自调用方法,而不是 HTTP。 没有模型绑定。
-
无法直接作为 HTTP 终结点访问。 它们通常在视图中调用。 视图组件从不处理请求。
-
在签名上重载,而不是当前 HTTP 请求的任何详细信息。
// Student 是视图组件名称 public class StudentViewComponent:ViewComponent { public async Task<IViewComponentResult> InvokeAsync() { List<Student> list = new() { new() {Id = 1, Name = "张三"}, new() {Id = 2, Name = "李四"} }; // 如果不写Index 参数,则默认视图名称为Default.cshtml return View("Index",list); // 返回 一个ViewComponentResult } }
视图搜索路径
运行时在以下路径中搜索视图:
-
/Views/{Controller Name}/Components/{View Component Name}/{View Name}
-
/Views/Shared/Components/{View Component Name}/{View Name}
-
/Pages/Shared/Components/{View Component Name}/{View Name}
搜索路径适用于使用控制器 + 视图和 Razor Pages 的项目。
视图组件的默认视图名称是 Default
,这意味着视图文件通常命名 Default.cshtml
。 创建视图组件结果或调用 View
方法时,可以指定其他视图名称。
建议命名视图文件 Default.cshtml
,并使用 Views/Shared/Components/{View Component Name}/{View Name} 路径。 Student
此示例Views/Shared/Components/Student/Default.cshtml
中使用的视图组件用于视图组件视图。
调用视图组件
要使用视图组件,请在视图中调用以下内容:
@await Component.InvokeAsync("Name of view component", {Anonymous Type Containing Parameters}) @await Component.InvokeAsync("Student")
ViewComponent 传参:
public class StudentViewComponent:ViewComponent { public IViewComponentResult Invoke(String? studentName=null) { List<Student> list = new() { new() {Id = 1, Name = "张三"}, new() {Id = 2, Name = "李四"} }; if (!string.IsNullOrWhiteSpace(studentName)) { list = list.Where(p => p.Name.Contains(studentName)).ToList(); } return View("Index",list); // 返回 一个ViewComponentResult } } @await Component.InvokeAsync("Student",new { StudentName="张三"})
标签助手调用:
标记帮助程序采用 Pascal 大小写格式的类和方法参数将转换为各自相应的
<vc:[view-component-name] parameter1="parameter1 value" parameter2="parameter2 value"> </vc:[view-component-name]>
若要将视图组件用作标记帮助程序,请使用 @addTagHelper
指令注册包含视图组件的程序集。 如果视图组件位于名为 MyWebApp
的程序集中,请将以下指令添加到 _ViewImports.cshtml
文件中:
@addTagHelper *, MyWebApp
无参:
<vc:student></vc:student>
有参:
<vc:student student-name="张三"></vc:student>
6. 区域路由与视图
区域是一项 ASP.NET 功能,用于将相关功能整理到一个组中,单独作为:
-
用于路由的命名空间。
-
用于视图和 Razor Pages 的文件夹结构。
使用区域会通过为 controller
和 action
或 Razor 页面 page
添加另一个路由参数 area
,创建用于路由目的的层次结构。
区域提供了一种将 ASP.NET Core Web 应用划分为更小的功能组的方法,每个功能组都有自己的一组 Razor Pages、控制器、视图和模型。 区域实际上是应用内的结构。 在 ASP.NET Core Web 项目中,Pages、模型、控制器和视图等逻辑组件保存在不同的文件夹中。 ASP.NET Core 运行时使用命名约定来创建这些组件之间的关系。 对于大型应用,将应用分区为独立的高级功能区域可能更有利。 例如,具有多个业务单位(如结账、计费、搜索等)的电子商务应用。 每个单位都有自己的区域,用于包含视图、控制器、Razor Pages 和模型。
如果发生以下情况,请考虑在项目中使用区域:
-
应用由可以进行逻辑分隔的多个高级功能组件组成。
-
想对应用进行分区,以便可以独立处理每个功能区域。
如果使用的是 Razor Pages,请参阅本文档中的
带视图的控制器区域
使用区域、控制器和视图的典型 ASP.NET Core Web 应用包含以下内容:
-
-
使用
[Area("AreaName")]
属性将自己与区域关联的控制器:[Area("Products")] public class ManageController : Controller {
-
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "MyArea", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
区域文件夹结构
请考虑具有两个逻辑组(产品和服务)的应用。 使用区域,文件夹结构类似于以下内容:
-
项目名称
-
Areas
-
产品
-
Controllers
-
HomeController.cs
-
ManageController.cs
-
-
视图
-
Home
-
Index.cshtml
-
-
管理
-
Index.cshtml
-
About.cshtml
-
-
-
-
服务
-
Controllers
-
HomeController.cs
-
-
视图
-
Home
-
Index.cshtml
-
-
-
-
-
虽然前面的布局是使用区域时的典型布局,但只需要视图文件即可使用此文件夹结构。 视图发现按以下顺序搜索匹配的区域视图文件:
text复制
/Areas/<Area-Name>/Views/<Controller-Name>/<Action-Name>.cshtml /Areas/<Area-Name>/Views/Shared/<Action-Name>.cshtml /Views/Shared/<Action-Name>.cshtml /Pages/Shared/<Action-Name>.cshtml
将控制器与区域关联
区域控制器使用
C#复制
using Microsoft.AspNetCore.Mvc; using Microsoft.Docs.Samples; namespace MVCareas.Areas.Products.Controllers; [Area("Products")] public class ManageController : Controller { public IActionResult Index() { ViewData["routeInfo"] = ControllerContext.MyDisplayRouteInfo(); return View(); } public IActionResult About() { ViewData["routeInfo"] = ControllerContext.MyDisplayRouteInfo(); return View(); } }
添加区域路由
区域路由通常使用
如果所有区域的 url 空间一致,则 {area:...}
可用作路由模板中的令牌:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "MyArea", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
在前面的代码中,exists
应用了路由必须与区域匹配的约束。 将 {area:...}
与 MapControllerRoute
结合使用:
-
是将路由添加到区域的最简单的机制。
-
将所有控制器与
[Area("Area name")]
特性相匹配。
以下代码使用
C#复制
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapAreaControllerRoute( name: "MyAreaProducts", areaName: "Products", pattern: "Products/{controller=Home}/{action=Index}/{id?}"); app.MapAreaControllerRoute( name: "MyAreaServices", areaName: "Services", pattern: "Services/{controller=Home}/{action=Index}/{id?}"); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
有关详细信息,请参阅
MVC 区域的链接生成
CSHTML复制
<li>Anchor Tag Helper links</li> <ul> <li> <a asp-area="Products" asp-controller="Home" asp-action="About"> Products/Home/About </a> </li> <li> <a asp-area="Services" asp-controller="Home" asp-action="About"> Services About </a> </li> <li> <a asp-area="" asp-controller="Home" asp-action="About"> /Home/About </a> </li> </ul> <li>Html.ActionLink generated links</li> <ul> <li> @Html.ActionLink("Product/Manage/About", "About", "Manage", new { area = "Products" }) </li> </ul> <li>Url.Action generated links</li> <ul> <li> <a href='@Url.Action("About", "Manage", new { area = "Products" })'> Products/Manage/About </a> </li> </ul>
示例下载包括一个
-
上面代码中所示的链接。
-
与上述链接类似的链接,只是未指定
area
。
在
如果未指定区域或控制器,路由取决于环境值。 当前请求的当前路由值被视为链接生成的环境值。 在许多情况下,对于示例应用,使用环境值将生成带有未指定区域的标记的错误链接。
有关详细信息,请参阅
使用 _ViewStart.cshtml 文件的共享区域布局
若要共享整个应用的常用布局,请将 _ViewStart.cshtml 保留在
应用程序根文件夹
应用程序根文件夹是包含Program.cs
使用 ASP.NET Core模板创建的 Web 应用中的文件的文件夹。
_ViewImports.cshtml
适用于 MVC 的 /Views/ViewImports.cshtml 和适用于 Razor Pages 的 /Pages/ViewImports.cshtml 不会导入到区域中的视图。 使用以下任一方法将视图导入到所有视图中:
-
将 _ViewImports.cshtml 添加到
-
将 _ViewImports.cshtml 文件复制到区域下相应的视图文件夹中。 例如, Razor 使用单个用户帐户创建的 Pages 应用具有以下文件夹中
的 _ViewImports.cshtml
文件:
-
/Areas/Identity/Pages/_ViewImports.cshtml
-
/Pages/_ViewImports.cshtml
-
_ViewImports.cshtml 文件通常包含
更改存储视图的默认区域文件夹
以下代码将默认的区域文件夹从 "Areas"
改为"MyAreas"
:
C#复制
using Microsoft.AspNetCore.Mvc.Razor; var builder = WebApplication.CreateBuilder(args); builder.Services.Configure<RazorViewEngineOptions>(options => { options.AreaViewLocationFormats.Clear(); options.AreaViewLocationFormats.Add("/MyAreas/{2}/Views/{1}/{0}.cshtml"); options.AreaViewLocationFormats.Add("/MyAreas/{2}/Views/Shared/{0}.cshtml"); options.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml"); }); builder.Services.AddControllersWithViews(); var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "MyArea", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
包含 Razor Pages 的区域
包含 Razor Pages 的区域要求在应用根目录中有一个 Areas//Pages
文件夹。 以下文件夹结构用于
-
项目名称
-
Areas
-
产品
-
页数
-
_ViewImports
-
关于
-
索引
-
-
-
服务
-
页数
-
管理
-
关于
-
索引
-
-
-
-
-
Razor Pages 和区域的链接生成
CSHTML复制
<li>Anchor Tag Helper links</li> <ul> <li> <a asp-area="Products" asp-page="/About"> Products/About </a> </li> <li> <a asp-area="Services" asp-page="/Manage/About"> Services/Manage/About </a> </li> <li> <a asp-area="" asp-page="/About"> /About </a> </li> </ul> <li>Url.Page generated links</li> <ul> <li> <a href='@Url.Page("/Manage/About", new { area = "Services" })'> Services/Manage/About </a> </li> <li> <a href='@Url.Page("/About", new { area = "Products" })'> Products/About </a> </li> </ul>
示例下载包含
如果未指定区域,路由取决于环境值。 当前请求的当前路由值被视为链接生成的环境值。 在许多情况下,对于示例应用,使用环境值会生成错误的链接。 例如,考虑从下面的代码生成的链接:
CSHTML复制
<li> <a asp-page="/Manage/About"> Services/Manage/About </a> </li> <li> <a asp-page="/About"> /About </a> </li>
对于上述代码:
-
只有当最后一个请求是针对
Services
区域的页时,从` 生成的链接才是正确的。 例如,
/Services/Manage/、
/Services/Manage/Index或
/Services/Manage/About`。 -
只有当最后一个请求是针对
/Home
中的页时,从 `` 生成的链接才是正确的。 -
代码摘自
使用 _ViewImports 文件导入命名空间和标记帮助程序
可以向每个区域 Pages 文件夹添加一个 _ViewImports.cshtml 文件,以便将命名空间和标记帮助程序导入到该文件夹中的每个 Razor 页面中。
请考虑使用示例代码的“服务”区域,它不包含 _ViewImports.cshtml 文件。 以下标记显示 /Services/Manage/About Razor 页面:
CSHTML复制
@page @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @model RPareas.Areas.Services.Pages.Manage.AboutModel @{ ViewData["Title"] = "Srv Mng About"; } <div> ViewData["routeInfo"]: @ViewData["routeInfo"] </div> <a asp-area="Products" asp-page="/Index"> Products/Index </a>
在前面的标记中:
-
必须使用完全限定的类名来指定模型 (
@model RPareas.Areas.Services.Pages.Manage.AboutModel
)。 -
在示例下载中,“产品”区域包含下列 _ViewImports.cshtml 文件:
CSHTML复制
@namespace RPareas.Areas.Products.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
以下标记显示 /Products/About Razor 页面:
CSHTML复制
@page @model AboutModel @{ ViewData["Title"] = "Prod About"; }
在前面的文件中,命名空间和 @addTagHelper
指令由 Areas/Products/Pages/_ViewImports.cshtml 文件导入文件。
有关详细信息,请参阅
Razor Pages 区域的共享布局
要共享整个应用的常用布局,请将 _ViewStart.cshtml 移动到应用程序根文件夹。
视频配套链接:Asp.Net Mvc Core 6.0 详细教程 - 网易云课堂 (163.com)