ASP.NET Core Razor Pages入门

11 篇文章 0 订阅
9 篇文章 0 订阅

引入

Razor Pages 是 ASP.NET Core 2.0 中引入的 ASP.NET Core MVC 的一个新方面。它提供了一种**“基于页面”的方法**,用于在 ASP.NET Core 中构建服务器端呈现的应用程序,并且可以与“传统”MVC 或 Web API 控制器共存。在这篇文章中,我将介绍 Razor Pages、入门基础知识以及 Razor Pages 与 MVC 的不同之处。

在 MVC 中,控制器用于将相似的操作组合在一起(其实就是MVC默认的路由规则要求,在一个控制器controller 类下有多个方法,每个方法对应一个razor页面)。收到请求时,路由会将请求定向到单个操作方法。此方法通常执行一些处理,并返回一个IActionResult,通常是 aViewResult或 a RedirectResult。如果 a返回,则使用提供的视图模型呈现ViewResultRazor视图。

​ MVC 提供了很大的灵活性,因此将操作分组到控制器中可以是高度自由的,但您通常会**以某种方式对相关的操作进行分组**,例如通过 URL 路由或按功能。例如,您可以按域组件进行分组,以便在电子商务应用程序中,与“产品”相关的操作将位于 中ProductController,而“购物车”操作将位于CartController. 或者,可以根据技术方面对动作进行分组;例如,控制器上的所有操作共享一组通用的授权要求。

​ 您会发现**一个常见的模式**是在控制器中包含成对的相关操作。在您使用 HTML 表单时尤其如此,您通常需要一个操作来处理初始GET请求,而另一个操作来处POST理请求。这两个操作都使用相同的 URL 路由和相同的 Razor 视图。从用户(或开发人员)的角度来看,它们在逻辑上是同一“页面”的两个方面。

​ 在某些情况下,您可能会发现您的控制器充满了这些操作方法对。例如,AccountControllerMVC 应用程序的默认 ASP.NET Core 标识包含许多这样的对:

img

GET 和 POST 对动作高度耦合,因为它们都返回相同的视图模型,可能需要类似的初始化逻辑,并使用相同的 Razor 视图。这对操作也与它们所在的整体控制器相关(它们都与身份和帐户相关),但它们之间的关系更密切。

Razor Pages 提供与传统 MVC 大致相同的功能,但通过利用这种配对使用稍微不同的模型。每条路由(每动作)都成为一个单独的 Razor 页面,而不是将许多相似的动作组合在一个控制器下。该页面可以有多个处理程序,每个处理程序都响应不同的 HTTP 动词,但使用相同的视图。因此,上面的相同身份AccountController可以在 Razor 页面中重写,如下所示。事实上,从 ASP.NET Core 2.1 开始,新的项目模板使用 Razor Pages for Identity,即使在 MVC 应用程序中也是如此。

img

​ Razor Pages 具有高度凝聚力的优势。与应用程序中给定页面相关的所有内容都在一个地方(一个动作都在一个页面中得到解决)。与 MVC 控制器相比,其中一些动作高度相关,但控制器作为一个整体的凝聚力较低。

​ 使用 Razor 页面的另一个好指标是,当您的 MVC 控制器仅返回 Razor 视图而无需进行大量处理时。一个经典的例子是HomeController来自 ASP.NET Core 2.0 模板,其中包括四个操作:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
 
    public IActionResult About()
    {
        ViewData["Message"] = "Your application description page.";
 
        return View();
    }
 
    public IActionResult Contact()
    {
        ViewData["Message"] = "Your contact page.";
 
        return View();
    }
 
    public IActionResult Error()
    {
        return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
    }
} 

​ 这些动作并不是真正相关的,但是每个动作都需要一个控制器,并且HomeController放置它们是一个比较方便的位置。Razor Pages 等效项将Index(Home) About 、Contact和Error页面放在根目录中,删除它们之间的隐式链接。作为额外的奖励,与About页面相关的所有内容(例如)都可以在文件About.cshtml和中找到About.cshtml.cs,它们一起位于磁盘和解决方案资源管理器中。这与控制器、视图模型和视图文件通常位于完全不同的文件夹中的 MVC 方法形成对比。

image-20221102191457678

创建 Razor 页面,它与传统的 Razor 视图有何不同?

接下来,如何创建 Razor 页面,它与传统的 Razor 视图有何不同?

Razor 页面模型

​ Razor Pages 构建在 MVC 之上,但它们使用的范式与 MVC 模式略有不同使用 MVC,控制器通常为操作提供逻辑和行为,最终生成包含用于呈现视图的数据的视图模型。Razor Pages 采用了稍微不同的方法,即使用Page Model

​ 与 MVC 相比,页面模型**既充当微型控制器,又充当视图的视图模型。它负责页面的行为和公开用于生成视图的数据**。这种模式更接近于某些桌面和移动框架中使用的模型-视图-视图模型 (MVVM)模式,尤其是当业务逻辑被推出页面模型并进入您的“业务”模型时。

​ 从技术上讲,Razor 页面与 Razor 视图非常相似,只是它@page在文件顶部有一个指令:

@page
<div>The time is @DateTime.Now</div>
img

​ 与 Razor 视图一样,Razor 页面中的任何 HTML 都会呈现给客户端,您可以使用该@符号呈现 C# 值或使用 C# 控制结构。

​ 添加@page是公开页面所需的全部内容,但此页面尚未使用页面模型。更典型的是,您创建一个派生自文件PageModel并将其与cshtml文件关联的类。如果您愿意,您可以将您的PageModel视图和 Razor 视图包含在同一个cshtml文件中,但最佳做法是将其保存PageModel在“代码隐藏”文件中,并且仅在cshtml文件中包含演示数据。按照惯例,如果您的Razor页面被调用MyPage.cshtml,则代码隐藏文件应命名为MyPage.cshtml.cs:

img

​ 该类PageModel是 Razor 视图的页面模型。呈现 Razor 页面时,在视图上公开的属性在视图PageModel中可用。.cshtml例如,您可以在Index.cshtml.cs文件中公开当前时间的属性:

using System;
using Microsoft.AspNetCore.Mvc.RazorPages;
 
public class IndexModel: PageModel
{
    public DateTime CurrentTime => DateTime.UtcNow;
} 

Index.cshtml并使用标准 Razor 语法将其呈现在您的文件中:

@page
@model IndexModel
 
<div>The current time is @Model.CurrentTime.ToShortTimeString()</div>

​ 如果你熟悉 Razor 视图,这应该很熟悉。您使用在属性@model上公开的指令声明模型的类型。Model不同之处在于,您的 MVC 控制器不是传入类型为 View Model 的 View Model,而是将IndexModel其PageModel本身作为Model属性公开。

Razor 页面中的路由

​ Razor Pages 与 MVC 类似,混合使用约定、配置和声明性指令来控制应用程序的行为方式。它们在底层使用与 MVC 相同的路由基础设施;不同之处在于路由的配置方式

​ 对于 MVC 和 Web API,您可以使用属性或基于约定的路由将传入 URL 与控制器和操作相匹配。
​ 对于 Razor 页面,**磁盘上文件的路径**用于计算可以访问页面的 URL。按照惯例,所有 Razor 页面都嵌套在Pages目录中
​ 例如,如果您在应用程序中创建一个 Razor 页面/Pages/MyFolder/Test.cshtml,它将在 URL 处公开/MyFolder/Test。这对于使用 Razor 页面来说绝对是一个积极的特性——导航和可视化应用程序公开的 URL 就像查看文件结构一样简单。

​ 话虽如此,Razor Page 路由是完全可定制的;如果您需要在与磁盘上的路径不对应的路由上公开您的页面,只需在指令中提供一个路由模板。@page这也可以包括其他路由参数,如下所示:

@page "/customroute/customized/{id?}" 

Razor Pages 中的模型绑定

​ 在 MVC 中,控制器中的操作的方法参数通过匹配 URL、查询字符串或请求正文中的值来绑定到传入请求(有关详细信息,请参阅文档)。在 Razor Pages 中,传入的请求被绑定到PageModel的属性

​ 出于安全原因,您必须通过装饰属性以与属性绑定来明确选择要绑定的[BindProperty]属性:

using Microsoft.AspNetCore.Mvc.RazorPages;
 
public class IndexModel : PageModel
{
    [BindProperty]
    public string Search { get;set; }
 
    public DateTime CurrentTime { get; set; };
}

​ 在此示例中,Search属性将绑定到请求,因为它用 装饰[BindProperty],但CurrentTime不会被绑定。对于 GET 请求,您必须更进一步并在SupportsGet属性上设置属性:

using Microsoft.AspNetCore.Mvc.RazorPages;
 
public class IndexModel : PageModel
{
    [BindProperty(SupportsGet = true)]
    public string Search { get;set; }
}

​ 如果您要绑定复杂的模型,例如回发表单,那么在[BindProperty]任何地方添加属性都会变得乏味。相反,我喜欢创建一个属性作为“输入模型”并用[BindProperty].(输入模型成为一个嵌套类) 这使您的PageModel公共表面区域保持明确和可控。这种方法的一个常见扩展是使您的输入模型成为一个嵌套类。这通常是有道理的,因为您通常不想在应用程序的其他地方使用您的 UI 层模型:

using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
 
public class IndexModel : PageModel
{
    public bool IsEmailConfirmed { get; set; }
 
    [BindProperty]
    public InputModel Input { get; set; }
 
    public class InputModel
    {
        [Required, EmailAddress]
        public string Email { get; set; }
 
        [Required, Phone, Display(Name = "Phone number")]
        public string PhoneNumber { get; set; }
    }
}

​ 在此示例中,只有属性Input绑定到传入请求。这使用嵌套InputModel类来定义要绑定的所有预期值。如果需要绑定其他值,可以添加其他属性到InputModel.

使用 Razor 页面处理程序处理多个 HTTP 动词

​ Razor Pages 的主要卖点之一是它们可以使用 MVC 控制器带来额外的凝聚力。通过使用页面处理程序响应请求,单个 Razor 页面包含与给定 Razor 视图关联的所有 UI 代码。

页面处理程序类似于 MVC 中的操作方法。当 Razor 页面收到请求时,会根据传入的请求和处理程序名称选择运行单个处理程序处理程序通过命名约定匹配On{Verb}[Async],其中{Verb}是 HTTP 方法,并且[async]是可选的。例如:

OnGet对于 HTML 表单,有一个显示初始空表单的处理程序和一个OnPost处理来自客户端的返回的处理程序是很常见POST的。例如,以下表单显示了更新用户显示名称的 Razor 页面的代码隐藏。

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
 
public class UpdateDisplayNameModel : PageModel
{
    private readonly IUserService _userService;
    public IndexModel(IUserService userService)
    {
        _userService = userService;
    }
 
    [BindProperty]
    public InputModel Input { get; set; }
 
    public void OnGet()
    {
        Input.DisplayName = _userService.GetDefaultDisplayName(); //显示初始空表单的处理程序
    }
 
    public async Task<IActionResult> OnPostAsync() //处理来自客户端的返回
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }
 
        await _userService.UpdateDisplayName(User, Input.DisplayName);
        return RedirectToPage("/Index");  //**在功能上等同于 MVC 的RedirectToAction()方法**。
    }
 
    public class InputModel
    {
        [Required, StringLength(50)]
        public string DisplayName { get; set; }
    }
}

​ 这个 Razor Page 使用了一个虚构IUserService的注入PageModel构造函数。页面处理程序在OnGet最初请求表单时运行,并将默认显示名称设置为从IUserService. 表格被发送给客户,客户填写详细信息并将其发回。

​ OnPostAsync处理程序响应 , 运行,POST并遵循与 MVC 操作方法类似的模式。您应该首先使用 来检查模型验证PageModel是否通过,如果没有则使用 重新显示表单。在语义上等价于 MVC 控制器中的方法;它用于渲染视图并返回响应。ModelState.IsValidPage()Page()View()

​ 如果PageModel有效,则表单使用提供的DisplayName值来更新当前登录用户的名称,User。PageModel提供对许多与基类相同的属性的访问,Controller例如HttpContext, Request,在本例中为User. RedirectToPage()最后,处理程序使用该方法重定向到另一个 Razor 页面。这在功能上等同于 MVC 的RedirectToAction()方法

​ 当表单只有一个可能的角色时,OnGet和处理程序对很常见,但也可以有一个 Razor Page 和多个处理程序用于同一个动词。要创建命名处理程序,请使用命名约定。例如,也许我们想向Razor 页面添加一个处理程序,允许用户将其用户名重置为默认值。我们可以将以下处理程序添加到现有的 Razor 页面:On{Verb}{Handler}[Async]

public async Task<IActionResult> OnPostResetNameAsync()
{
    await _userService.ResetDisplayName(User);
    return RedirectToPage("/Index");
}

​ 要**调用处理程序,请在查询字符串中传递处理程序(方法)名称POST**,例如?handler=resetName。这确保调用命名处理程序而不是默认OnPostAsync处理程序

如果您不喜欢在此处使用查询字符串,则可以使用自定义路由并将处理程序名称包含在路径段中

​ 本节展示了 和 的处理程序GET,POST, 但也可以为其他 HTTP 动词(如DELETE、PUT和PATCH). HTML 表单通常不使用这些动词,因此在面向页面的 Razor Pages 应用程序中通常不需要。但是,如果您出于某种原因需要 API 调用它们,它们遵循与其他页面处理程序相同的命名约定和行为。

使用Handlers实现多个GET、POST Action

​ 默认情况下,Razor页面设计为具有单个OnGetAsyncOnPostAsync Action方法;如果您想在单个页面中具有不同的Action,则需要使用所谓的Handler。如您的页面有AJAX回调、多个表单提交或其它场景,则需要使用它。

例如,如果您使用Kendo Grid并希望通过AJAX调用加载Grid数据,则需要使用Handler来处理该AJAX调用。任何类型的单页面应用程序将使用大量Handler,或者您将所有这些AJAX调用指向MVC控制器。

我在页面中添加了一个名为OnGetHelloWorldAsync()的方法,我们该怎么调用它?

从我的研究来看, 调用Handler有三种不同的方式:

  1. Querystring – 示例:“/managepage/2177/?handler=helloworld”
  2. 为视图中的定义路由:@page"{handler?}",然后在Url中包括“/helloworld”
  3. 在视图中定义提交按钮 - 示例:

可以在这里了解更多有关单页面多个Handlers 的方式。

在 Razor 页面中使用标签助手TagHelper

​ 当您使用 MVC 操作和视图时,ASP.NET Core 提供了各种标记帮助asp-action器,例如asp-controller用于从 Razor 视图生成指向您的操作的链接。Razor 页面具有等效的标记帮助程序,您可以使用其中asp-page生成指向特定 Razor 页面的路径,并asp-page-handler设置特定的处理程序

​ 例如,您可以使用asp-page和asp-page-handler标签在表单中创建一个“重置名称”按钮:

<button asp-page="/Index" asp-page-handler="ResetName" type="submit">Reset Display Name</button>

概括

​ Razor 页面是 ASP.NET Core MVC 的一个新方面,在 ASP.NET Core 2.0 中引入。它们构建在现有 ASP.NET Core 基元之上,并提供与传统 MVC 相同的整体功能,但具有基于页面的模型。对于许多应用程序,使用 a 的基于页面的方法PageModel可以产生比传统 MVC 更具凝聚力的代码。Razor Pages 可以与传统的 MVC 或 Web API 控制器在同一应用程序中无缝使用,因此您只需要在合适的地方使用它。

​ 如果您使用 Razor 创建新应用程序,我强烈建议您将 Razor Pages 视为默认方法。一开始对于有经验的 MVC 开发人员可能会觉得奇怪,但我对改进的开发体验感到惊喜。对于现有的 MVC 应用程序,添加新的 Razor 页面很容易,但不太值得迁移整个 MVC 应用程序来使用它们。它们在功能上与 MVC 相同,因此主要优点是更方便常见开发任务的工作流。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值