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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!