ASP.NET Core Razor Page

作者 Zhendong Ho 日期 2019-12-12
ASP.NET Core Razor Page

Razor Page

ASP.NET Core支持多种框架,除了MVC,还有Razor Page

Razor Page和MVC区别

MVC

  • Model:数据
  • View:Html、Razor、TagHelpers
  • Controller:逻辑

Razor Page

  • 数据
  • Html、Razor、TagHelpers
  • 逻辑

MVC的实现是把Model、View、Controller分开,有一定的好处,但是增加了复杂性。而对于一些相对简单的项目Razor Page是把MVC的部分合并到一起。

Razor Page路由

目录 请求路径1 请求路径2
/Pages/Index.cshtml / /Index
/Pages/Department.cshtml /Department
/Pages/Department/EmployeeList.cshtml /Department/EmployeeList
/Pages/Department/Index.cshtml /Department /Department/Index

注意

  • Razor Page的页面默认放在Pages目录下面。
  • 如果文件名为Index.cshtml,可以省略Index,用斜杠代替。

创建项目

打开VS2019,创建新项目,选择ASP.NET Core Web 应用程序。项目名称为ThreePage,选择ASP.NET Core 3.0版本,然后选择空模板,点击创建。

Three项目中的Models文件夹Services文件夹复制到本项目的根目录下。

打开Startup.cs,在构造函数注入IConfiguration

public class Startup
{
private readonly IConfiguration _configuration;

public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
}

然后把Three项目中的ThreeOptions.cs复制到本项目的根目录下。

Startup.csConfigureServices方法中,进行依赖注入。注意有一点不同的是,MVC是AddControllersWithViews,而这里是AddRazorPages

public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(); // Razor Page注入

services.AddSingleton<IDepartmentService, DepartmentService>();
services.AddSingleton<IEmployeeService, EmployeeService>();
}
}

修改Startup.cs中的Configure方法,注意RazorPage的UseEndpoints需要修改MapRazorPages

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseStaticFiles();

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthentication();

app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); // Razor Page配置
}

建立Razor Page

部门页面

项目右键,新建Pages文件夹,用于存放页面。在Pages下新建Razor视图,命名为Index.cshtml

@page
@using Three.Models
@using Three.Services

@inject IDepartmentService departmentService

<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>Name</th>
<th>Location</th>
<th>EmployeeCount</th>
<th>操作</th>
</tr>
<tr>
<th>@Html.DisplayFor(expression: x => x.Departments)</th>
</tr>
</table>
</div>
</div>

@functions
{

public IEnumerable<Department> Departments { get; set; }

public async Task OnGetAsync()
{
Departments = await departmentService.GetAll();
}

}

注意

  1. Razor Page页面最前面写上@page
  2. @functions代码块中,可以写处理请求的C#代码
  3. 处理GET请求的方法命名为OnGet,或者是OnGetAsync。可以是异步方法同步方法。同理,处理POST请求的方法名称是OnPostOnPostAsync
  4. 通过 @Html.DisplayFor使用Departments属性。

运行项目,可以看到正常显示。

image-20191212222229409

添加布局和样式

和Three项目类似,依次进行以下操作:

  1. 在Pages下新建Shared文件夹,添加布局页_Layout.cshtml
  2. 使用Libman安装Bootstrap
  3. 添加bundleconfig.json,使用bundle合并。
  4. 通过Nuget安装BuildBundlerMinifier包
  5. 复制Three项目的images文件夹
  6. 添加_ViewStart.cshtml,指定母页面。
  7. 添加_ViewImports.cshtml,引入Tag Helper
  8. Pages下添加展示模板,Pages/DisplayTemplates/Department.cshtml。

_ViewImports.cshtml文件的内容如下。

@using ThreePage
@namespace ThreePage.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Department.cshtml代码如下。

@model Three.Models.Department

<tr>
<td>@Model.Name</td>
<td>@Model.Location</td>
<td>@Model.EmployeeCount</td>

<td>
</td>
</tr>

运行项目,可以看到和Three项目的效果差不多了。

image-20191212231327134

添加部门

Pages右键,新建Department文件夹,在Department下新建Razor页面,命名为AddDepartment.cshtml

可以看到,除了AddDepartment.cshtml,还多了.cs文件。这个类就是页面的类,相当于之前的/Pages/Index.cshtml@function代码块的内容。

编辑AddDepartment.cshtml.cs文件。添加Department属性,使用BindProperty特性修饰,可以让页面使用的属性绑定到Department属性中

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Threading.Tasks;
using Three.Services;

namespace ThreePage.Pages.Department
{
public class AddDepartmentModel : PageModel
{
private readonly IDepartmentService _departmentService;

[BindProperty]
public Three.Models.Department Department { get; set; }

public AddDepartmentModel(IDepartmentService departmentService)
{
_departmentService = departmentService;
}

public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
await _departmentService.Add(Department);
return RedirectToPage("/Index"); // 这里需要加上“/”,否则会找当前目录下的Index
}
}
}

编辑AddDepartment.cshtml的代码如下。

@page
@model ThreePage.Pages.Department.AddDepartmentModel

<form asp-action="Add">
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Department.Name"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Department.Name" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Department.Location"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Department.Location" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Department.EmployeeCount"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Department.EmployeeCount" />
</div>
</div>
<div class="row">
<div class="col-md-2 offset-md-2">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>

/Pages/Index.cshtml中怎加代码。通过TagHelper中的asp-page属性指定路由。

@page
@using Three.Services
@inject IDepartmentService departmentService

<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>Name</th>
<th>Location</th>
<th>EmployeeCount</th>
<th>操作</th>
</tr>
@Html.DisplayFor(expression: x => x.Departments)
</table>
</div>
</div>

<div class="row">
<div class="col-md-4">
<a asp-page="Department/AddDepartment">Add</a>
</div>
</div>

@functions
{

public IEnumerable<Three.Models.Department> Departments { get; set; }

public async Task OnGetAsync()
{
Departments = await departmentService.GetAll();
}

}

员工列表

Pages右键,新建Employee文件夹,在Employee下新建Razor视图,命名为EmployeeList.cshtml@page的作用是获取departmentId参数并在C#代码中使用

@page "{departmentId:int}"

@using Microsoft.AspNetCore.Mvc.RazorPages
@using Three.Models
@using Three.Services

@model EmployeeListModel

<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Gender</th>
<th>Is Fired</th>
<th>操作</th>
</tr>
@Html.DisplayFor(expression: x => x.Employees)
</table>
</div>
</div>

@functions {
public class EmployeeListModel : PageModel
{
private readonly IEmployeeService _employeeService;

public EmployeeListModel(IEmployeeService employeeService)
{
_employeeService = employeeService;
}

public IEnumerable<Employee> Employees { get; set; }

public async Task OnGet(int departmentId)
{
Employees = await _employeeService.GetByDepartmentId(departmentId);

ViewData["DepartmentId"] = departmentId;
}
}
}

添加员工列表模板。在DisplayTemplates下,添加Razor视图,命名为Employee.cshtml

@model Three.Models.Employee

<tr>
<td>@Model.FirstName</td>
<td>@Model.LastName</td>
<td>@Model.Gender</td>
<td>@(Model.Fired ? "是" : "")</td>

<td>
@if (!Model.Fired)
{

}
</td>
</tr>

然后在模板页面Department.cshtml下,增加Employees操作

@model Three.Models.Department

<tr>
<td>@Model.Name</td>
<td>@Model.Location</td>
<td>@Model.EmployeeCount</td>

<td>
<a asp-page="Employee/EmployeeList" asp-route-departmentId="@Model.Id">Employees</a>
</td>
</tr>

添加员工

在员工列表页面EmployeeList.cshtml中,增加一个添加员工操作,跳转到添加员工页面。

<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Gender</th>
<th>Is Fired</th>
<th>操作</th>
</tr>
@Html.DisplayFor(expression: x => x.Employees)
</table>
<a asp-page="AddEmployee" asp-route-departmentId="@ViewData["DepartmentId"]">Add</a>
</div>
</div>

Pages/Employee下新建添加员工页面Razor页面,命名为AddEmployee.cshtml

AddEmployee.cshtml页面的代码如下。

@page "{departmentId:int}"
@using Three.Models
@model ThreePage.Pages.Employee.AddEmployeeModel

<form method="post">

<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Employee.FirstName"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Employee.FirstName" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Employee.LastName"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Employee.LastName" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Employee.Gender"></label>
</div>
<div class="col-md-6">
<select class="form-control"
asp-for="Employee.Gender"
asp-items="Html.GetEnumSelectList<Gender>()">
</select>
</div>
</div>
<div class="row">
<div class="col-md-2 offset-md-4">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>

AddEmployee.cshtml.cs的代码如下。

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Three.Services;

namespace ThreePage.Pages.Employee
{
public class AddEmployeeModel : PageModel
{
private readonly IEmployeeService _employeeService;

public AddEmployeeModel(IEmployeeService employeeService)
{
_employeeService = employeeService;
}

[BindProperty]
public Three.Models.Employee Employee { get; set; }

public async Task<IActionResult> OnPostAsync(int departmentId)
{
Employee.DepartmentId = departmentId;

if (!ModelState.IsValid)
{
return Page();
}

await _employeeService.Add(Employee);

return RedirectToPage("EmployeeList", routeValues: new { departmentId });
}
}
}

解雇操作

在员工列表的模板页面Pages/DisplayTemplates/Employee.cshtml中,修改代码,增加解雇操作入口

@model Three.Models.Employee

<tr>
<td>@Model.FirstName</td>
<td>@Model.LastName</td>
<td>@Model.Gender</td>
<td>@(Model.Fired ? "是" : "")</td>

<td>
@if (!Model.Fired)
{
<a asp-page="EmployeeList"
asp-page-handler="Fire"
asp-route-employeeId="@Model.Id"
asp-route-departmentId="@Model.DepartmentId">
Fire
</a>
}
</td>
</tr>

然后在EmployeeList.cshtml中,C#代码部分添加方法OnGetFireAsync,该方法接收两个参数。

public async Task<IActionResult> OnGetFireAsync(int employeeId, int departmentId)
{
await _employeeService.Fire(employeeId);
return RedirectToPage("EmployeeList", routeValues: new { departmentId });
}

使用View Component

复制Three项目中的,Components文件夹,到本项目的Pages目录下。

复制Three项目中的,ViewComponents文件夹到本项目的根目录下,并修改CompanySummaryViewComponent.cs的命名空间为ThreePage

using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Three.Services;

namespace ThreePage.ViewComponents
{
public class CompanySummaryViewComponent : ViewComponent
{
private readonly IDepartmentService _departmentService;

public CompanySummaryViewComponent(IDepartmentService departmentService)
{
_departmentService = departmentService;
}
public async Task<IViewComponentResult> InvokeAsync(string title)
{
ViewBag.Title = title;
var summary = await _departmentService.GetCompanySummary();
return View(summary);
}
}
}

_ViewImports.cshtml中引入。

@using ThreePage
@namespace ThreePage.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper "*, ThreePage"

Pages/Index.cshtml中使用。

<div class="row">
<div class="col-md-4 offset-md-2">
<a asp-page="Department/AddDepartment">Add</a>
</div>
<div class="col-md-4">
@await Component.InvokeAsync(name: "CompanySummary", arguments: new { title = "部门列表页的汇总" })
<vc:company-summary title="部门列表页的汇总2"></vc:company-summary>
</div>
</div>

运行项目,View Component正常显示。

image-20191228011652738