ASP.NET Core应用程序6:Razor Pages
Razor Pages是生成Html响应的一种简化方式。它的简单性,意味着能够比MVC框架更快获得结果。它将单个视图与一个类关联起来,由该类为视图提供功能,并使用基于文件的路由系统来匹配URL。它的灵活性不如MVC,所以不适合复杂的应用程序。
1 准备工作
2 理解Razor Pages
Razor Pages和MVC并不是二选一的情况,是可以共存的,Razor Pages以牺牲灵活性来换取专注度,将标记与C#代码捆绑在一起。
2.1 配置Razor Pages
在Startup中添加配置。
services.AddRazorPages().AddRazorRuntimeCompilation();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
AddRazorPages
用于设置Razor Pages必须用到的服务,而可选的AddRazorRuntimeCompilation
则启用运行时重新编译。MapRazorPages
创建路由配置,用于将URL匹配到页面。
2.2 创建Razor Pages
Razor Pages是在Pages文件夹中定义的,添加Pages文件夹并创建Index.cshtml的Razor Pages文件。
@page
@model IndexModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using MyWebApp.Models
<!DOCTYPE html>
<html>
<head>
<link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="bg-primary text-white text-center m-2 p-2">@Model.Product.Name</div>
</body>
</html>
@functions
{
public class IndexModel : PageModel
{
private DataContext context;
public Product Product { get; set; }
public IndexModel(DataContext ctx)
{
context = ctx;
}
public async Task OnGetAsync(long id = 1)
{
Product = await context.Products.FindAsync(id);
}
}
}
在Razor Pages中,@page
指令必须第一个出现,这确保不会将该文件误认为与控制器关联在一起的视图。最重要的区别是使用@functions
指令在同一个文件中定义支持Razor内容的C#代码。启动程序浏览器请求http://localhost:5000/index。
3 理解Razor Pages的路由
Razor Pages依赖于cshtml文件位置来进行路由,要添加复杂URL结构,可以添加文件夹。Pages文件夹下添加Suppliers文件夹,再添加List.cshtml的Razor Pages。访问http://localhost:5000/suppliers/list。
@page
@model ListModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using MyWebApp.Models;
<!DOCTYPE html>
<html>
<head>
<link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<h5 class="bg-primary text-white text-center m-2 p-2">Suppliers</h5>
<ul class="list-group m-2">
@foreach (string s in Model.Suppliers)
{
<li class="list-group-item">@s</li>
}
</ul>
</body>
</html>
@functions {
public class ListModel : PageModel
{
private DataContext context;
public IEnumerable<string> Suppliers { get; set; }
public ListModel(DataContext ctx)
{
context = ctx;
}
public void OnGet()
{
Suppliers = context.Suppliers.Select(s => s.Name);
}
}
}
注意:当混和了Razor Pages和MVC时框架时,可以在配置中修改端点路由语句顺序,endpoints.MapRazorPages()放到endpoints.MapControllers()前面,先配置谁就由谁来设置默认路由。
3.1 在Razor Pages中指定路由模式
使用文件结构来执行路由,可以从URL查询中获取请求处理程序方法的值。请求http://localhost:5000/index?id=2,可以对应上OnGetAsync(long id = 1)
方法中定义的参数。
可以在@page指令中使用路由模式。@page "{id:long?}"
,请求http://localhost:5000/index/4。
@page指令可用于覆盖Razor Pages的基于文件的路由约定。@page "/lists/suppliers
,请求http://localhost:5000/lists/suppliers。
3.2 为Razor Pages添加路由
如果想为页面定义多个路由,可在Startup类配置。
services.Configure<RazorPagesOptions>(options =>
{
options.Conventions.AddPageRoute("/Index", "/extra/page/{id:long?}");
});
这里RazorPagesOptions类为Razor Pages添加额外路由。AddPageRoute方法的第一个实参相对于Pages文件夹页面路径,第二个实参是要添加路由配置的URL模式。请求http://localhost:5000/extra/page/2。
4 理解页面模型类
页面模型派生自PageModel类,它提供一些方法处理请求,并使用一些属性提供上下文数据。但在Razor Pages开发中常常不需要使用他们,因为开发更关注选择必要数据来渲染页面的视图部分。
4.1 使用代码隐藏类文件
把@functions指令中的类移到Index.cshtml.cs中,并修修改Index.cshtml。
@page "{id:long?}"
@model MyWebApp.Pages.IndexModel
<!DOCTYPE html>
<html>
<head>
<link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="bg-primary text-white text-center m-2 p-2">@Model.Product.Name</div>
</body>
</html>
添加视图导入文件
视图导入文件可用于避免在视图文件中使用模型的全类名,添加_ViewImports.cshtml文件。这样Index.cshtml中就这样写@model IndexModel
。
@namespace MyWebApp.Pages
@using MyWebApp.Models
4.2 理解Razor Pages的操作结果
Razor Pages处理程序方法使用IActionResult接口来控制生成的响应。
public async Task<IActionResult> OnGetAsync(long id = 1)
{
Product = await context.Products.FindAsync(id);
return Page();
}
使用操作结果
在没请求到数据时,相比使用NotFound方法,更好的方法是将客户端重定向到另一个URL。添加NotFound.cshtml的Razor Pages。
@page "/noid"
@model NotFoundModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using WebApp.Models;
<!DOCTYPE html>
<html>
<head>
<link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<title>Not Found</title>
</head>
<body>
<div class="bg-primary text-white text-center m-2 p-2">No Matching ID</div>
<ul class="list-group m-2">
@foreach (Product p in Model.Products) {
<li class="list-group-item">@p.Name (ID: @p.ProductId)</li>
}
</ul>
</body>
</html>
@functions {
public class NotFoundModel: PageModel {
private DataContext context;
public IEnumerable<Product> Products { get; set; }
public NotFoundModel(DataContext ctx) {
context = ctx;
}
public void OnGetAsync(long id = 1) {
Products = context.Products;
}
}
}
更新了IndexModel类的处理如下,当请求http://localhost:5000/index/500时,Product没有查询到数据,则将用户重定向到NotFound页面。
public async Task<IActionResult> OnGetAsync(long id = 1)
{
Product = await context.Products.FindAsync(id);
if(Product == null)
{
return RedirectToPage("NotFound");
}
return Page();
}
4.3 处理多个HTTP方法
Razor Pages可以定义多个处理程序方法来响应HTTP方法。常见的GET、POST方法。下面添加Editor.cshtml的Razor Pages。
@page "{id:long}"
@model EditorModel
<!DOCTYPE html>
<html>
<head>
<link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="bg-primary text-white text-center m-2 p-2">Editor</div>
<div class="m-2">
<table class="table table-sm table-striped table-bordered">
<tbody>
<tr><th>Name</th><td>@Model.Product.Name</td></tr>
<tr><th>Price</th><td>@Model.Product.Price</td></tr>
</tbody>
</table>
<form method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<label>Price</label>
<input name="price" class="form-control"
value="@Model.Product.Price" />
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>
</div>
</body>
</html>
此视图定义的form元素不包含action特性,这意味着当单击Submit按钮时发送一个Post请求。
在Editor.cshtml.cs中添加如下。OnGetAsync方法用于处理Get请求,OnPostAsync处理Post请求。
public class EditorModel : PageModel
{
private DataContext context;
public Product Product { get; set; }
public EditorModel(DataContext ctx)
{
context = ctx;
}
public async Task OnGetAsync(long id)
{
Product = await context.Products.FindAsync(id);
}
public async Task<IActionResult> OnPostAsync(long id, decimal price)
{
Product p = await context.Products.FindAsync(id);
p.Price = price;
await context.SaveChangesAsync();
return RedirectToPage();
}
}
请求http://localhost:5000/editor/1,编辑字段修改价格并提交,数据将被更新。
4.4 选择处理程序方法
页面模型类可定义多个处理程序方法,允许请求使用handler查询字符串参数或者路由片段来选择方法。添加HandlerSelector.cshtml的Razor Pages如下。
@page
@model HandlerSelectorModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using Microsoft.EntityFrameworkCore
<!DOCTYPE html>
<html>
<head>
<link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="bg-primary text-white text-center m-2 p-2">Selector</div>
<div class="m-2">
<table class="table table-sm table-striped table-bordered">
<tbody>
<tr><th>Name</th><td>@Model.Product.Name</td></tr>
<tr><th>Price</th><td>@Model.Product.Name</td></tr>
<tr><th>Category</th><td>@Model.Product.Category?.Name</td></tr>
<tr><th>Supplier</th><td>@Model.Product.Supplier?.Name</td></tr>
</tbody>
</table>
<a href="/handlerselector" class="btn btn-primary">Standard</a>
<a href="/handlerselector?handler=related" class="btn btn-primary">
Related
</a>
</div>
</body>
</html>
@functions{
public class HandlerSelectorModel: PageModel {
private DataContext context;
public Product Product { get; set; }
public HandlerSelectorModel(DataContext ctx) {
context = ctx;
}
public async Task OnGetAsync(long id = 1) {
Product = await context.Products.FindAsync(id);
}
public async Task OnGetRelatedAsync(long id = 1) {
Product = await context.Products
.Include(p => p.Supplier)
.Include(p => p.Category)
.FirstOrDefaultAsync(p => p.ProductId == id);
Product.Supplier.Products = null;
Product.Category.Products = null;
}
}
}
本页面模型定义了OnGetAsync和OnGetRelatedAsync两个方法,请求http://localhost:5000/handlerselector默认使用OnGetAsync,可以用参数来决定目标URL,/handlerselector?handler=related
,参数可以不用On前缀和Async后缀。
5 理解Razor Pages视图
Razor Pages视图语法等与控制器的基本相同。
5.1 为Razor Pages创建布局
创建Shared文件夹,添加_Layout.cshtml文件。
<!DOCTYPE html>
<html>
<head>
<link href="/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<title>@ViewBag.Title</title>
</head>
<body>
<h5 class="bg-secondary text-white text-center m-2 p-2">
Razor Page
</h5>
@RenderBody()
</body>
</html>
在Pages文件夹中添加_ViewStart.cshtml的文件。
@{
Layout = "_Layout";
}
修改Index.cshtml。
@page "{id:long?}"
@model IndexModel
<div class="bg-primary text-white text-center m-2 p-2">@Model.Product.Name</div>
_ViewStart文件将布局应用到所有覆盖Layout属性值的页面。
Editor.cshtml中禁用布局。
@{
Layout = null;
}
请求http://localhost:5000/index看到新布局,请求http://localhost:5000/editor/1将看到没有布局。
5.2 在Razor Pages中使用分部视图
使用分部视图拉避免重复公共内容。
在Pages/_ViewImports.cshtml中添加如下。
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
在Pages/Shared添加_ProductPartial.cshtml视图。
@model Product
<div class="m-2">
<table class="table table-sm table-striped table-bordered">
<tbody>
<tr><th>Name</th><td>@Model.Name</td></tr>
<tr><th>Price</th><td>@Model.Price</td></tr>
</tbody>
</table>
</div>
在Index.cshtml中使用分部视图。
@page "{id:long?}"
@model IndexModel
<div class="bg-primary text-white text-center m-2 p-2">@Model.Product.Name</div>
<partial name="_ProductPartial" model="Model.Product" />
5.3 创建没有页面模型的Pazor Pages
如果Pazor Pages只是简单地向用户展示数据,那么结果可以是一个页面模型类。
在Pages文件夹中添加Data.cshtml的Pazor Pages。当页面模型只用于访问服务,而不添加其他操作时,可使用@inject指令在视图中获取服务,并不需要使用页面模型。
@page
@inject DataContext context;
<h5 class="bg-primary text-white text-center m-2 p-2">Categories</h5>
<ul class="list-group m-2">
@foreach (Category c in context.Categories)
{
<li class="list-group-item">@c.Name</li>
}
</ul>
本文来自博客园,作者:一纸年华,转载请注明原文链接:https://www.cnblogs.com/nullcodeworld/p/18185810