ASP.NET Core 中的 Razor 页面介绍

Razor 页面是 ASP.NET Core MVC 的一个新特性,它可以使基于页面的编码方式更简单高效。
若要查找使用模型视图控制器方法的教程,请参阅  ASP.NET Core MVC 入门
本文档介绍 Razor 页面。 它并不是分步教程。  如果认为某些部分过于复杂,请参阅  Razor 页面入门   有关 ASP.NET Core 的概述,请参阅  ASP.NET Core 简介

系统必备

Install  one  of the following:

创建 Razor 页面项目

请参阅  Razor 页面入门 ,获取关于如何使用 Visual Studio 创建 Razor 页面项目的详细说明。

Razor 页面

Startup.cs 中已启用 Razor 页面:
C# 复制
public class Startup
{
    public void ConfigureServices (IServiceCollection services)
    {
        // Includes support for Razor Pages and controllers.
        services.AddMvc();
    }

    public void Configure (IApplicationBuilder app)
    {
        app.UseMvc();
    }
}
请考虑一个基本页面:
CSHTML 复制
@page

< h1 > Hello, world! </ h1 >
< h2 > The time on the server is @DateTime.Now </ h2 >
上述代码看上去类似于一个 Razor 视图文件。  不同之处在于  @page  指令。   @page  使文件转换为一个 MVC 操作 ,这意味着它将直接处理请求,而无需通过控制器处理。   @page 必须是页面上的第一个 Razor 指令。   @page  将影响其他 Razor 构造的行为。
将在以下两个文件中显示使用  PageModel  类的类似页面。  Pages/Index2.cshtml 文件:
CSHTML 复制
@page
@using RazorPagesIntro.Pages
@model IndexModel2

< h2 > Separate page model </ h2 >
< p >
    @Model.Message
</ p >
Pages/Index2.cshtml.cs 页面模型:
C# 复制
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;

namespace RazorPagesIntro.Pages
{
    public class IndexModel2 : PageModel
    {
        public string Message { get ; private set ; } = "PageModel in C#" ;

        public void OnGet ()
        {
            Message += $" Server time is { DateTime.Now }" ;
        }
    }
}
按照惯例, PageModel  类文件的名称与追加 .cs 的 Razor 页面文件名称相同。  例如,前面的 Razor 页面的名称为 Pages/Index2.cshtml。  包含  PageModel  类的文件的名称为 Pages/Index2.cshtml.cs。
页面的 URL 路径的关联由页面在文件系统中的位置决定。 下表显示了 Razor 页面路径及匹配的 URL:

文件名和路径 匹配的 URL
/Pages/Index.cshtml / 或 /Index
/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml /Store 或 /Store/Index

注意:
  • 默认情况下,运行时在“Pages”文件夹中查找 Razor 页面文件。
  • URL 未包含页面时,Index 为默认页面。

编写基本窗体

由于 Razor 页面的设计,在构建应用时可轻松实施用于 Web 浏览器的常用模式。  模型绑定 标记帮助程序 和 HTML 帮助程序均只可用于 Razor 页面类中定义的属性。   请参考为  Contact  模型实现基本的“联系我们”窗体的页面:
在本文档中的示例中, DbContext  在  Startup.cs  文件中进行初始化。
C# 复制
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RazorPagesContacts.Data;

namespace RazorPagesContacts
{
    public class Startup
    {
        public IHostingEnvironment HostingEnvironment { get ; }

        public void ConfigureServices (IServiceCollection services)
        {
            services.AddDbContext<AppDbContext>(options =>
                              options.UseInMemoryDatabase( "name" ));
            services.AddMvc();
        }

        public void Configure (IApplicationBuilder app)
        {
            app.UseMvc();
        }
    }
}
数据模型:
C# 复制

namespace RazorPagesContacts.Data
{
    public class Customer
    {
        public int Id { get ; set ; }

        [ Required, StringLength(100) ]
        public string Name { get ; set ; }
    }
}
数据库上下文:
C# 复制
using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext (DbContextOptions options)
            : base (options)
        {
        }

        public DbSet<Customer> Customers { get ; set ; }
    }
}
Pages/Create.cshtml 视图文件:
CSHTML 复制
@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

< html >
< body >
    < p >
        Enter your name.
    </ p >
    < div asp-validation-summary = "All" > </ div >
    < form method = "POST" >
        < div > Name: < input asp-for = " Customer.Name " /> </ div >
        < input type = "submit" />
    </ form >
</ body >
</ html >
Pages/Create.cshtml.cs 页面模型:
C# 复制
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages
{
    public class CreateModel : PageModel
    {
        private readonly AppDbContext _db;

        public CreateModel (AppDbContext db)
        {
            _db = db;
        }

        [ BindProperty ]
        public Customer Customer { get ; set ; }

        public async Task<IActionResult> OnPostAsync ()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage( "/Index" );
        }
    }
}
按照惯例, PageModel  类命名为  <PageName>Model 并且它与页面位于同一个命名空间中。
使用  PageModel  类,可以将页面的逻辑与其展示分离开来。  它定义了页面处理程序,用于处理发送到页面的请求和用于呈现页面的数据。  借助这种分离,可以通过 依赖关系注入 管理页面依赖关系,并对页面执行 单元测试
页面包含  OnPostAsync  处理程序方法,它在  POST  请求上运行(当用户发布窗体时)。 可以为任何 HTTP 谓词添加处理程序方法。 最常见的处理程序是:
  • OnGet,用于初始化页面所需的状态。 OnGet 示例。
  • OnPost,用于处理窗体提交。
Async  命名后缀为可选,但是按照惯例通常会将它用于异步函数。   前面示例中的  OnPostAsync  代码看上去与通常在控制器中编写的内容相似。  前面的代码通常用于 Razor 页面。  多数 MVC 基元(例如 模型绑定 验证 和操作结果)都是共享的。
之前的  OnPostAsync  方法:
C# 复制
public async Task<IActionResult> OnPostAsync ()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _db.Customers.Add(Customer);
    await _db.SaveChangesAsync();
    return RedirectToPage( "/Index" );
}
OnPostAsync  的基本流:
检查验证错误。
  • 如果没有错误,则保存数据并重定向。
  • 如果有错误,则再次显示页面并附带验证消息。 客户端验证与传统的 ASP.NET Core MVC 应用程序相同。 很多情况下,都会在客户端上检测到验证错误,并且从不将它们提交到服务器。
成功输入数据后, OnPostAsync  处理程序方法调用  RedirectToPage  帮助程序方法来返回  RedirectToPageResult  的实例。   RedirectToPage  是新的操作结果,类似于  RedirectToAction  或  RedirectToRoute ,但是已针对页面进行自定义。   在前面的示例中,它将重定向到根索引页 ( /Index )。   页面 URL 生成 部分中详细介绍了  RedirectToPage
提交的窗体存在(已传递到服务器的)验证错误时, OnPostAsync  处理程序方法调用  Page  帮助程序方法。   Page  返回  PageResult  的实例。   返回  Page  的过程与控制器中的操作返回  View  的过程相似。   PageResult  是处理程序方法的默认 返回类型。   返回  void  的处理程序方法将显示页面。
Customer  属性使用  [BindProperty]  特性来选择加入模型绑定。
C# 复制
public class CreateModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateModel (AppDbContext db)
    {
        _db = db;
    }

    [ BindProperty ]
    public Customer Customer { get ; set ; }

    public async Task<IActionResult> OnPostAsync ()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        return RedirectToPage( "/Index" );
    }
}
默认情况下,Razor 页面只绑定带有非 GET 谓词的属性。 绑定属性可以减少需要编写的代码量。  绑定通过使用相同的属性显示窗体字段 ( <input asp-for=" Customer.Name " /> ) 来减少代码,并接受输入。
备注
出于安全原因,必须选择绑定 GET 请求数据以对模型属性进行分页。 请在将用户输入映射到属性前对其进行验证。 当处理依赖查询字符串或路由值的方案时,选择加入此行为非常有用。
若要将属性绑定在 GET 请求上,请将  [BindProperty]  特性的  SupportsGet  属性设置为  true [BindProperty(SupportsGet = true)]
主页 (Index.cshtml):
CSHTML 复制
@page
@model RazorPagesContacts.Pages.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

< h1 > Contacts </ h1 >
< form method = "post" >
    < table class = "table" >
        < thead >
            < tr >
                < th > ID </ th >
                < th > Name </ th >
            </ tr >
        </ thead >
        < tbody >
            @foreach (var contact in Model.Customers)
            {
                < tr >
                    < td > @ contact.Id </ td >
                    < td > @ contact.Name </ td >
                    < td >
                        < a asp-page = "./Edit" asp-route-id = "@ contact.Id " > edit </ a >
                        < button type = "submit" asp-page-handler = "delete"
                                asp-route-id = "@ contact.Id " > delete </ button >
                    </ td >
                </ tr >
            }
        </ tbody >
    </ table >

    < a asp-page = "./Create" > Create </ a >
</ form >
Index.cshtml.cs 隐藏文件:
C# 复制
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Pages
{
    public class IndexModel : PageModel
    {
        private readonly AppDbContext _db;

        public IndexModel (AppDbContext db)
        {
            _db = db;
        }

        public IList<Customer> Customers { get ; private set ; }

        public async Task OnGetAsync ()
        {
            Customers = await _db.Customers.AsNoTracking().ToListAsync();
        }

        public async Task<IActionResult> OnPostDeleteAsync ( int id )
        {
            var contact = await _db.Customers.FindAsync(id);

            if (contact != null )
            {
                _db.Customers.Remove(contact);
                await _db.SaveChangesAsync();
            }

            return RedirectToPage();
        }
    }
}
Index.cshtml 文件包含以下标记来创建每个联系人项的编辑链接:
CSHTML 复制
< a asp-page = "./Edit" asp-route-id = "@ contact.Id " > edit </ a >
定位点标记帮助程序  使用  asp-route-{value}  属性生成“编辑”页面的链接。  此链接包含路由数据及联系人 ID。  例如  http://localhost:5000/Edit/1
Pages/Edit.cshtml 文件:
CSHTML 复制
@page "{id:int}"
@model RazorPagesContacts.Pages.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@{
    ViewData["Title"] = "Edit Customer";
}

< h1 > Edit Customer - @Model. Customer.Id </ h1 >
< form method = "post" >
    < div asp-validation-summary = "All" > </ div >
    < input asp-for = " Customer.Id " type = "hidden" />
    < div >
        < label asp-for = " Customer.Name " > </ label >
        < div >
            < input asp-for = " Customer.Name " />
            < span asp-validation-for = " Customer.Name " > </ span >
        </ div >
    </ div >
    < div >
        < button type = "submit" > Save </ button >
    </ div >
</ form >
第一行包含  @page "{id:int}"  指令。   路由约束  "{id:int}"  告诉页面接受包含  int 路由数据的页面请求。   如果页面请求未包含可转换为  int  的路由数据,则运行时返回 HTTP 404(未找到)错误。   若要使 ID 可选,请将  ?  追加到路由约束:
CSHTML 复制
@page "{id:int?}"
Pages/Edit.cshtml.cs 文件:
C# 复制
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel (AppDbContext db)
        {
            _db = db;
        }

        [ BindProperty ]
        public Customer Customer { get ; set ; }

        public async Task<IActionResult> OnGetAsync ( int id )
        {
            Customer = await _db.Customers.FindAsync(id);

            if (Customer == null )
            {
                return RedirectToPage( "/Index" );
            }

            return Page();
        }

        public async Task<IActionResult> OnPostAsync ()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Attach(Customer).State = EntityState.Modified;

            try
            {
                await _db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                throw new Exception( $"Customer { Customer.Id } not found!" );
            }

            return RedirectToPage( "/Index" );
        }
    }
}
Index.cshtml 文件还包含用于为每个客户联系人创建删除按钮的标记:
CSHTML 复制
< button type = "submit" asp-page-handler = "delete"
        asp-route-id = "@ contact.Id " > delete </ button >
删除按钮采用 HTML 呈现,其  formaction  包括参数:
  • asp-route-id 属性指定的客户联系人 ID。
  • asp-page-handler 属性指定的 handler
下面是呈现的删除按钮的示例,其中客户联系人 ID 为  1
HTML 复制
< button type = "submit" formaction = "/?id=1&amp;handler=delete" > delete </ button >
选中按钮时,向服务器发送窗体  POST  请求。   按照惯例,根据方案  OnPost[handler]Async  基于  handler  参数的值来选择处理程序方法的名称。
因为本示例中  handler  是  delete ,因此  OnPostDeleteAsync  处理程序方法用于处理  POST  请求。   如果  asp-page-handler  设置为不同值(如  remove ),则选择名称为  OnPostRemoveAsync  的页面处理程序方法。
C# 复制
public async Task<IActionResult> OnPostDeleteAsync ( int id )
{
    var contact = await _db.Customers.FindAsync(id);

    if (contact != null )
    {
        _db.Customers.Remove(contact);
        await _db.SaveChangesAsync();
    }

    return RedirectToPage();
}
OnPostDeleteAsync  方法:
  • 接受来自查询字符串的 id
  • 使用 FindAsync 查询客户联系人的数据库。
  • 如果找到客户联系人,则从客户联系人列表将其删除。 数据库将更新。
  • 调用 RedirectToPage,重定向到根索引页 (/Index)。

标记所需的页属性

PageModel  上的属性可通过  Required  特性进行修饰:
[!code-cs ]
有关详细信息,请参阅 模型验证

使用 OnGet 处理程序管理 HEAD 请求

通常,针对 HEAD 请求创建和调用 HEAD 处理程序:
C# 复制
public void OnHead ()
{
HttpContext.Response.Headers.Add( "HandledBy" , "Handled by OnHead!" );
}
如果未定义 HEAD 处理程序 ( OnHead ),Razor 页面会回退以调用 ASP.NET Core 2.1 或更高版本中的 GET 页处理程序 ( OnGet )。   使用 ASP.NET Core 2.1 到 2.x 版本  Startup.Configure  中的  SetCompatibilityVersion 方法 ,选择加入此行为:
C# 复制
services.AddMvc()
SetCompatibilityVersion  有效地将 Razor 页面选项  AllowMappingHeadRequestsToGetHandler  设置为  true
可以显式地选择使用特定行为,而不是通过  SetCompatibilityVersion  选择使用所有 2.1 行为。  以下代码选择使用将 HEAD 映射到 GET 处理程序这一行为。
C# 复制
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.AllowMappingHeadRequestsToGetHandler = true ;
});

XSRF/CSRF 和 Razor 页面

无需为 防伪验证 编写任何代码。  Razor 页面自动将防伪标记生成过程和验证过程包含在内。

将布局、分区、模板和标记帮助程序用于 Razor 页面

页面可使用 Razor 视图引擎的所有功能。 布局、分区、模板、标记帮助程序、_ViewStart.cshtml 和 _ViewImports.cshtml 的工作方式与它们在传统的 Razor 视图中的工作方式相同。
让我们使用其中的一些功能来整理此页面。
向 Pages/_Layout.cshtml 添加 布局页面
CSHTML 复制
<!DOCTYPE html>
< html >
< head >
    < title > Razor Pages Sample </ title >      
</ head >
< body >    
   < a asp-page = "/Index" > Home </ a >
    @RenderBody() 
    < a asp-page = "/Customers/Create" > Create </ a > < br />
</ body >
</ html >
布局
  • 控制每个页面的布局(页面选择退出布局时除外)。
  • 导入 HTML 结构,例如 JavaScript 和样式表。
请参阅 布局页面 了解详细信息。
在 Pages/_ViewStart.cshtml 中设置  Layout  属性:
CSHTML 复制
@{
    Layout = "_Layout";
}
布局位于“页面”文件夹中。 页面按层次结构从当前页面的文件夹开始查找其他视图(布局、模板、分区)。 可以从“页面”文件夹下的任意 Razor 页面使用“页面”文件夹中的布局。
建议不要将布局文件放在“视图/共享”文件夹中。 视图/共享 是一种 MVC 视图模式。 Razor 页面旨在依赖文件夹层次结构,而非路径约定。
Razor 页面中的视图搜索包含“页面”文件夹。 用于 MVC 控制器和传统 Razor 视图的布局、模板和分区可直接工作。
添加 Pages/_ViewImports.cshtml 文件:
CSHTML 复制
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
本教程的后续部分中将介绍  @namespace   @addTagHelper  指令将 内置标记帮助程序 引入“页面”文件夹中的所有页面。
页面上显式使用  @namespace  指令后:
CSHTML 复制
@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

< h2 > Name space </ h2 >
< p >
    @Model.Message
</ p >
此指令将为页面设置命名空间。  @model  指令无需包含命名空间。
_ViewImports.cshtml 中包含  @namespace  指令后,指定的命名空间将为在导入  @namespace  指令的页面中生成的命名空间提供前缀。  生成的命名空间的剩余部分(后缀部分)是包含 _ViewImports.cshtml 的文件夹与包含页面的文件夹之间以点分隔的相对路径。
例如,代码隐藏文件 Pages/Customers/Edit.cshtml.cs 显式设置命名空间:
C# 复制
namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel (AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.
Pages/_ViewImports.cshtml 文件设置以下命名空间:
CSHTML 复制
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
为 Pages/Customers/Edit.cshtml Razor 页面生成的命名空间与代码隐藏文件相同.  已对  @namespace  指令进行设计,因此添加到项目的 C# 类和页面生成的代码可直接工作,而无需添加代码隐藏文件的  @using  指令。
@namespace  也可用于传统的 Razor 视图。
原始的 Pages/Create.cshtml 视图文件:
CSHTML 复制
@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

< html >
< body >
    < p >
        Enter your name.
    </ p >
    < div asp-validation-summary = "All" > </ div >
    < form method = "POST" >
        < div > Name: < input asp-for = " Customer.Name " /> </ div >
        < input type = "submit" />
    </ form >
</ body >
</ html >
更新后的 Pages/Create.cshtml 视图文件:
CSHTML 复制
@page
@model CreateModel

< html >
< body >
    < p >
        Enter your name.
    </ p >
    < div asp-validation-summary = "All" > </ div >
    < form method = "POST" >
        < div > Name: < input asp-for = " Customer.Name " /> </ div >
        < input type = "submit" />
    </ form >
</ body >
</ html >
Razor 页面初学者项目 包含 Pages/_ValidationScriptsPartial.cshtml,它与客户端验证联合。

页面的 URL 生成

之前显示的  Create  页面使用  RedirectToPage
C# 复制
public async Task<IActionResult> OnPostAsync ()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _db.Customers.Add(Customer);
    await _db.SaveChangesAsync();
    return RedirectToPage( "/Index" );
}
应用具有以下文件/文件夹结构:
  • /Pages
    • Index.cshtml
    • /Customers
      • Create.cshtml
      • Edit.cshtml
      • Index.cshtml
成功后,Pages/Customers/Create.cshtml 和 Pages/Customers/Edit.cshtml 页面将重定向到 Pages/Index.cshtml。  字符串  /Index  是用于访问上一页的 URI 的组成部分。   可以使用字符串  /Index  生成 Pages/Index.cshtml 页面的 URI。  例如:
  • Url.Page("/Index", ...)
  • <a asp-page="/Index">My Index Page</a>
  • RedirectToPage("/Index")
页面名称是从根“/Pages”文件夹到页面的路径(包含前导  / ,例如  /Index )。  与硬编码 URL 相比,前面的 URL 生成示例提供了改进的选项和功能。  URL 生成使用 路由 ,并且可以根据目标路径定义路由的方式生成参数并对参数编码。
页面的 URL 生成支持相对名称。  下表显示了 Pages/Customers/Create.cshtml 中不同的  RedirectToPage  参数选择的索引页:

RedirectToPage(x)
RedirectToPage("/Index") Pages/Index
RedirectToPage("./Index"); Pages/Customers/Index
RedirectToPage("../Index") Pages/Index
RedirectToPage("Index") Pages/Customers/Index

RedirectToPage("Index") RedirectToPage("./Index")  和  RedirectToPage("../Index")  是相对名称。   结合  RedirectToPage  参数与当前页的路径来计算目标页面的名称。
构建结构复杂的站点时,相对名称链接很有用。 如果使用相对名称链接文件夹中的页面,则可以重命名该文件夹。 所有链接仍然有效(因为这些链接未包含此文件夹名称)。

ViewData 特性

可以通过  ViewDataAttribute  将数据传递到页面。   控制器或 Razor 页面模型上使用  [ViewData]  修饰的属性将其值存储在  ViewDataDictionary  中并从此处进行加载。
在下面的示例中, AboutModel  包含使用  [ViewData]  修饰的  Title  属性。   Title  属性设置为“关于”页面的标题:
C# 复制
public class AboutModel : PageModel
{
[ ViewData ]
public string Title { get ; } = "About" ;

public void OnGet ()
{
}
}
在“关于”页面中,以模型属性的形式访问  Title  属性:
CSHTML 复制
< h1 > @Model.Title </ h1 >
在布局中,从 ViewData 字典读取标题:
CSHTML 复制
<!DOCTYPE html>
< html lang = "en" >
< head >
< title > @ViewData["Title"] - WebApplication </ title >
...

TempData

ASP.NET 控制器 上公开了  TempData  属性。  此属性存储未读取的数据。  Keep  和  Peek  方法可用于检查数据,而不执行删除。   多个请求需要数据时, TempData  有助于进行重定向。
[TempData]  是 ASP.NET Core 2.0 中的新属性,在控制器和页面上受支持。
下面的代码使用  TempData  设置  Message  的值:
C# 复制
public class CreateDotModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateDotModel (AppDbContext db)
    {
        _db = db;
    }

    [ TempData ]
    public string Message { get ; set ; }

    [ BindProperty ]
    public Customer Customer { get ; set ; }

    public async Task<IActionResult> OnPostAsync ()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        Message = $"Customer { Customer.Name } added" ;
        return RedirectToPage( "./Index" );
    }
}
Pages/Customers/Index.cshtml 文件中的以下标记使用  TempData  显示  Message  的值。
CSHTML 复制
< h3 > Msg: @Model.Message </ h3 >
Pages/Customers/Index.cshtml.cs 页面模型将  [TempData]  属性应用到  Message  属性。
C# 复制
[ TempData ]
public string Message { get ; set ; }
请参阅  TempData  了解详细信息。

针对一个页面的多个处理程序

以下页面使用  asp-page-handler  标记帮助程序为两个页面处理程序生成标记:
CSHTML 复制
@page
@model CreateFATHModel

< html >
< body >
    < p >
        Enter your name.
    </ p >
    < div asp-validation-summary = "All" > </ div >
    < form method = "POST" >
        < div > Name: < input asp-for = " Customer.Name " /> </ div >
        < input type = "submit" asp-page-handler = "JoinList" value = "Join" />
        < input type = "submit" asp-page-handler = "JoinListUC" value = "JOIN UC" />
    </ form >
</ body >
</ html >
前面示例中的窗体包含两个提交按钮,每个提交按钮均使用  FormActionTagHelper  提交到不同的 URL。   asp-page-handler  是  asp-page  的配套属性。   asp-page-handler  生成提交到页面定义的各个处理程序方法的 URL。   未指定  asp-page ,因为示例已链接到当前页面。
页面模型:
C# 复制
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages.Customers
{
    public class CreateFATHModel : PageModel
    {
        private readonly AppDbContext _db;

        public CreateFATHModel (AppDbContext db)
        {
            _db = db;
        }

        [ BindProperty ]
        public Customer Customer { get ; set ; }

        public async Task<IActionResult> OnPostJoinListAsync ()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage( "/Index" );
        }

        public async Task<IActionResult> OnPostJoinListUCAsync ()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            Customer.Name = Customer.Name?.ToUpper();
            return await OnPostJoinListAsync();
        }
    }
}
前面的代码使用已命名处理程序方法。  已命名处理程序方法通过采用名称中  On<HTTP Verb>  之后及  Async  之前的文本(如果有)创建。   在前面的示例中,页面方法是 OnPost JoinList Async 和 OnPost JoinListUC Async。   删除 OnPost 和 Async 后,处理程序名称为  JoinList  和  JoinListUC
CSHTML 复制
< input type = "submit" asp-page-handler = "JoinList" value = "Join" />
< input type = "submit" asp-page-handler = "JoinListUC" value = "JOIN UC" />
使用前面的代码时,提交到  OnPostJoinListAsync  的 URL 路径为  http://localhost:5000/Customers/CreateFATH?handler=JoinList   提交到  OnPostJoinListUCAsync  的 URL 路径为  http://localhost:5000/Customers/CreateFATH?handler=JoinListUC

自定义路由

如果你不喜欢 URL 中的查询字符串  ?handler=JoinList ,可以更改路由,将处理程序名称放在 URL 的路径部分。   可以通过在  @page  指令后面添加使用双引号括起来的路由模板来自定义路由。
CSHTML 复制
@page "{handler?}"
@model CreateRouteModel

< html >
< body >
    < p >
        Enter your name.
    </ p >
    < div asp-validation-summary = "All" > </ div >
    < form method = "POST" >
        < div > Name: < input asp-for = " Customer.Name " /> </ div >
        < input type = "submit" asp-page-handler = "JoinList" value = "Join" />
        < input type = "submit" asp-page-handler = "JoinListUC" value = "JOIN UC" />
    </ form >
</ body >
</ html >
前面的路由将处理程序放在了 URL 路径中,而不是查询字符串中。  handler  前面的  ? 表示路由参数为可选。
可以使用  @page  将其他段和参数添加到页面的路由中。  其中的任何内容均会被追加到页面的默认路由中。  不支持使用绝对路径或虚拟路径更改页面的路由(如  "~/Some/Other/Path" )。

配置和设置

若要配置高级选项,请在 MVC 生成器上使用  AddRazorPagesOptions  扩展方法:
C# 复制
public void ConfigureServices (IServiceCollection services)
{
    services.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            options.RootDirectory = "/MyPages" ;
            options.Conventions.AuthorizeFolder( "/MyPages/Admin" );
        });
}
目前,可以使用  RazorPagesOptions  设置页面的根目录,或者为页面添加应用程序模型约定。  通过这种方式,我们在将来会实现更多扩展功能。
若要预编译视图,请参阅  Razor 视图编译
请参阅  Razor 页面入门 ,这篇文章以本文为基础编写。

指定 Razor 页面位于内容根目录中

默认情况下,Razor 页面位于 /Pages 目录的根位置。  向  AddMvc  添加  WithRazorPagesAtContentRoot ,以指定 Razor 页面位于应用的内容根目录 ( ContentRootPath ) 中:
C# 复制
services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesAtContentRoot();

指定 Razor 页面位于自定义根目录中

向  AddMvc  添加  WithRazorPagesRoot ,以指定 Razor 页面位于应用中自定义根目录位置(提供相对路径):
C# 复制
services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesRoot( "/path/to/razor/pages" );

猜你喜欢

转载自blog.csdn.net/luzhangtong/article/details/80905089