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>
posted @   一纸年华  阅读(276)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示

目录导航

1 准备工作
2 理解Razor Pages
2.1 配置Razor Pages
2.2 创建Razor Pages
3 理解Razor Pages的路由
3.1 在Razor Pages中指定路由模式
3.2 为Razor Pages添加路由
4 理解页面模型类
4.1 使用代码隐藏类文件
4.2 理解Razor Pages的操作结果
4.3 处理多个HTTP方法
4.4 选择处理程序方法
5 理解Razor Pages视图
5.1 为Razor Pages创建布局
5.2 在Razor Pages中使用分部视图
5.3 创建没有页面模型的Pazor Pages