Mvc 视图
Mvc 视图
视图发现
- 视图发现:基于视图名称确定使用哪个视图文件
return View();
默认查找操作方法同名的视图return View("<ViewName>");
查找指定视图名
- 视图发现的顺序:
Views/[ControllerName]/[ViewName].cshtml
Views/Shared/[ViewName].cshtml
- 可以提供视图文件路径而不提供视图名称
- 使用应用根目录开始的绝对路径以
/
或~/
开头,须指定.cshtml
扩展名 - 使用相对路径在不同目录中指定视图以
../
开头,无需指定.cshtml
扩展名 - 当前的控制器特定目录以
./
开头,无需指定.cshtml
扩展名
- 使用应用根目录开始的绝对路径以
- 如果基础文件系统区分大小写,则视图名称也可能区分大小写
视图模型数据
强类型数据 ViewModel
@model WebApplication1.ViewModels.[YourViewModel]
- 可以将 Model 和 ViewMode 分离,Model 复制业务逻辑和数据访问,ViewModel 负责模型绑定和验证
弱类型数据(ViewData 和 ViewBag)
ViewData
属性是弱类型对象的字典。ViewBag
属性是ViewData
的包装器,为基础ViewData
集合提供动态属性。对于
ViewData
和ViewBag
,键查找都不区分大小写。提取
ViewData
对象值时必须将其强制转换为特定类型var address = ViewData["Address"] as Address;
控制器或 Razor 页面模型上使用
[ViewData]
特定的属性将其值存储在字典中并从中进行加载。[ViewData] public string Title { get; set; } public IActionResult About() { Title = "About Us"; ViewData["Message"] = "Your application description page."; return View(); }
ViewData 和 ViewBag 之间差异:
ViewBag
默认情况下,不可用于 Razor PagesPageModel
类。ViewData
派生自ViewDataDictionary
,因此它有可用的字典属性,如ContainsKey、Add、Remove 和 Clear
ViewBag
派生自Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.DynamicViewData
,因此它可使用点表示法@ViewBag.SomeKey = <value or object>
动态模型类型
不使用
@model
声明模型类型,可动态引用控制器传递过来的模型实例的属性// return View(Address); <address> @Model.Street<br> @Model.City, @Model.State @Model.PostalCode<br> <abbr title="Phone">P:</abbr> 425.555.0100 </address>
视图注入服务
- 视图使用
@inject
指令注入:@inject SomeService ServiceName
CSS 隔离
- 若要为页面或视图添加限定范围的 CSS 文件,请将 CSS 样式置于与
.cshtml
文件的名称匹配的配套.cshtml.css
文件 - CSS 隔离在生成时发生,框架会重写 CSS 选择器,以匹配应用页面或视图所呈现的标记
- 为了保证发生捆绑时的 CSS 样式隔离,不支持在 Razor 代码块中导入 CSS。
- CSS 隔离仅适用于 HTML 元素。 标记帮助程序不支持 CSS 隔离。
- 如何实现 CSS 预处理器支持?
- CSS 隔离并不原生支持 CSS 预处理器(如 Sass 或 Less)
- 需要在生成过程中框架重写 CSS 选择器的步骤之前进行预处理器编译(项目配置设置生成前事件)
- 第三方 NuGet 包(如
AspNetCore.SassCompiler
)完成预处理
布局 Layout
按照约定,ASP.NET Core 应用的默认布局名为
_Layout.cshtml
布局定义应用中的视图的最高级别模板
Razor 视图具有
Layout
属性,用来指定当前页面布局@{ Layout = "_Layout"; }
章节:
通过调用
RenderSection
来选择引用一个或多个节@RenderSection("Scripts", required: false)
@section
定义章节@section Scripts { <script type="text/javascript" src="~/scripts/main.js"></script> }
需要在每个视图或页面之前运行的代码应放在
_ViewStart.cshtml
文件中。如果在文件层次结构中找到多个
_ViewImports.cshtml
文件,则指令的组合行为为:@addTagHelper``@removeTagHelper
:按顺序全部运行@tagHelperPrefix
:最接近视图的文件会替代任何其他文件@model
:最接近视图的文件会替代任何其他文件@inherits
:最接近视图的文件会替代任何其他文件@using
:全部包括在内;忽略重复项@inject
:针对每个属性,最接近视图的属性会替代具有相同属性名的任何其他属性
分部视图
请勿使用需要复杂呈现逻辑或代码执行来呈现标记的分部视图。 使用视图组件而不是分部视图。
分部视图的文件名通常以下划线 (
_
) 开头处理程序方法也可以调用 Partial 方法来生成
PartialViewResult
对象:public IActionResult OnGetPartial() => Partial("_AuthorPartialRP");
视图中引入分部视图的方法:
分部标记帮助程序:
<partial name="../Account/_PartialName.cshtml" />
异步 HTML 帮助程序:
@await Html.PartialAsync("_PartialName.cshtml") @{ // 将呈现的输出直接流式传输到响应 // 因为该方法不返回结果 // 性能更好 await Html.RenderPartialAsync("_AuthorPartial"); }
如果按名称(无文件扩展名)引用分部视图,则按所述顺序搜索以下位置:
- Razor Pages
- 当前正在执行页面的文件夹
- 该页面文件夹上方的目录图
/Shared
/Pages/Shared
/Views/Shared
- MVC
/Areas/<Area-Name>/Views/<Controller-Name>
/Areas/<Area-Name>/Views/Shared
/Views/Shared
/Pages/Shared
- Razor Pages
分部视图可以调用另一个分部视图,相对路径始终相对于当前文件,而不是相对于文件的根视图或父视图
实例化分部视图时,它会获得父视图的
ViewData
字典的副本。 在分部视图中的ViewData
更改会在分部视图返回时丢失。还可将模型传入分部视图。@await Html.PartialAsync("_PartialName", customViewData) @await Html.PartialAsync("_PartialName", model)
视图组件
概念
- 视图组件不使用模型绑定,它们依赖于调用视图组件时传递的数据。
- 呈现一个区块而不是整个响应。
- 可以有参数和业务逻辑。
- 通常从布局页调用。
视图组件类
通常派生自 ViewComponent
使用
[ViewComponent]
特性修饰或者继承自视图组件类,类名通常为[视图组件名]ViewComponent
推荐做法:继承一个视图组件,使用视图组件名作为类名,使用 nameof 引用类名
public class PriorityList : ViewComponent { /* code */ }
@await Component.InvokeAsync(nameof(PriorityList),new{name:"..."})
视图组件必须是公共、非嵌套和非抽象的类
支持构造函数依赖项注入
不参与控制器生命周期,因此无法在视图组件中使用筛选器
定义返回
Task<IViewComponentResult>
的InvokeAsync
方法,或是返回IViewComponentResult
的同步Invoke
方法。通常通过调用 ViewComponent.View 方法初始化模型并将其传递给视图。
对视图组件方法的访问不是来自 Http 请求, 它们通常在视图中调用,因此:
- 参数来自调用方法,没有模型绑定。
- 不能作为 HTTP 终结点直接访问。
- 视图组件从不处理请求。
视图搜索路径
运行时搜索顺序
/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}
视图组件的默认视图名称为
Default
建议使用
Views/Shared/Components/{View Component Name}/{View Name}
路径命名视图文件Default.cshtml
调用视图组件
@await Component.InvokeAsync("Name of view component", {包含参数的匿名类})
以标记帮助程序的形式调用视图组件
<!-- 使用 @addTagHelper 指令注册包含视图组件的程序集后,可以将视图组件用作标记帮助程序 -->
@addTagHelper *, [程序集名称]
<!-- 采用 Pascal 大小写格式的类和方法参数将转换为各自相应的短横线格式 -->
<vc:[view-component-name] parameter1="parameter1 value" parameter2="parameter2 value"> </vc:[view-component-name]>
区域 Areas
概念
- 不同区域的 Controller 和 Action 名称可以相同
- 每个区域都有自己的一组 Razor Pages、控制器、视图和模型。
- 使用区域的场景:
- 应用由可以进行逻辑分隔的多个高级功能组件组成。
- 想对应用进行分区,以便可以独立处理每个功能区域。
- 区域实现步骤:
- 区域文件夹结构
- 使用
[Area("区域名称")]
属性修饰控制器 Program.cs
中添加区域路由
区域路由
区域路由通常使用传统路由,而不使用特性路由。
app.MapAreaControllerRoute( name: "MyAreaProducts", areaName: "Products", pattern: "Products/{controller=Home}/{action=Index}/{id?}");
一般情况下,具有区域的路由应放在路由表中靠前的位置,因为它们比没有区域的路由更具体。
区域链接
<a asp-area="Products" asp-controller="Home" asp-action="About"> Products/Home/About </a>
<a href='@Url.Action("About", "Manage", new { area = "Products" })'> Products/Manage/About </a>
@Html.ActionLink("Product/Manage/About", "About", "Manage", new { area = "Products" })
数据传递
Asp.Net Core 通用方法
- Cookie、Session、QueryString、HttpContext.Items、Application、Cache
TempData
在另一个请求读取数据之前,此属性将读取此数据。
Peek(string) 返回包含与指定键关联的元素的对象,不将该键标记为需删除。
Keep(String) 将字典中的指定键标记为需保留。
[TempData] public string Message { get; set; }
@{ <h3>Message: @TempData["Message"]</h3> TempData.Keep("Message"); } @{ <h3>Message: @TempData.Peek("Message")</h3> } @* 在请求结束时,不会删除 TempData["Message"] *@
默认情况下使用基于 cookie 的 TempData 提供程序将 TempData 存储于 cookie;因此,TempData 不因有大数据量
启用基于会话的 TempData 提供程序,避免在每次请求通过 cookie 传达大量数据
builder.Services.AddRazorPages().AddSessionStateTempDataProvider(); builder.Services.AddControllersWithViews() .AddSessionStateTempDataProvider();
隐藏字段
- 数据可以保存在隐藏的表单域中,并在下一个请求上回发。
- 由于客户端可能篡改数据,因此应用必须始终重新验证存储在隐藏字段中的数据。