问题
如何在ASP.NET Core 2.0中使用Razor页面
解
创建一个空的项目并修改Startup.cs文件以为MVC添加服务和中间件。
-
publicvoid ConfigureServices(
-
IServiceCollection services)
-
{
-
services.AddSingleton<IMovieService, MovieService>();
-
services.AddMvc();
-
}
-
publicvoid Configure(
-
IApplicationBuilder app,
-
IHostingEnvironment env)
-
{
-
app.UseMvc();
-
}
添加一个服务和域模型(IMovieService的实现只是示例源代码中的内存列表),
-
publicclass Movie
-
{
-
publicint Id { get; set; }
-
public string Title { get; set; }
-
publicint ReleaseYear { get; set; }
-
public string Summary { get; set; }
-
}
-
publicinterface IMovieService
-
{
-
List<Movie> GetMovies();
-
Movie GetMovie(int id);
-
void AddMovie(Movie item);
-
void UpdateMovie(Movie item);
-
void DeleteMovie(int id);
-
bool MovieExists(int id);
-
}
添加输入和输出模型(通过属性绑定接收和发送数据)。
-
publicclass MovieInputModel
-
{
-
publicint Id { get; set; }
-
[Required]
-
public string Title { get; set; }
-
publicint ReleaseYear { get; set; }
-
public string Summary { get; set; }
-
}
-
publicclass MovieOutputModel
-
{
-
publicint Id { get; set; }
-
public string Title { get; set; }
-
publicint ReleaseYear { get; set; }
-
public string Summary { get; set; }
-
public DateTime LastReadAt { get; set; }
-
}
添加一个名为Pages的文件夹,并为其添加Index,_Layout,_ViewImports和_ViewStart页面。这些页面与MVC没有区别。另外,为您的CRUD页面添加一个文件夹Movies。
添加4个新的RazorPage项目到电影文件夹 - 称为索引,创建,编辑和删除。这些将添加.cshtml和.cshtml.cs文件。
这些页面中的每一个都将通过构造器注入来注入我们的 IMovieService ,例如,
-
private readonly IMovieService service;
-
public IndexModel(IMovieService service)
-
{
-
this.service = service;
-
}
修改Index.cshtml。
-
@page
-
@model IndexModel
-
@{
-
}
-
<strong>Movie Listing</strong>
-
<div>
-
<a asp-page="/Index">Home</a> |
-
<a asp-page="./Create">Add New</a>
-
</div>
-
<table>
-
<thead>
-
<tr>
-
<th>Title</th>
-
<th>Year</th>
-
<th></th>
-
<th></th>
-
<th></th>
-
</tr>
-
</thead>
-
<tbody>
-
@foreach (var item in Model.Movies)
-
{
-
<tr>
-
<td>@item.Title</td>
-
<td>@item.ReleaseYear</td>
-
<td><a asp-page="./Edit" asp-route->Edit</a></td>
-
<td><a asp-page="./Delete" asp-route->Delete</a></td>
-
</tr>
-
}
-
</tbody>
-
</table>
修改Index.cshtml.cs。
-
public List<MovieOutputModel> Movies { get; set; }
-
publicvoid OnGet()
-
{
-
this.Movies = this.service.GetMovies()
-
.Select(item => new MovieOutputModel
-
{
-
Id = item.Id,
-
Title = item.Title,
-
ReleaseYear = item.ReleaseYear,
-
Summary = item.Summary,
-
LastReadAt = DateTime.Now
-
})
-
.ToList();
-
}
修改Create.cshtml。
-
@page
-
@model CreateModel
-
@{
-
}
-
<strong>New Movie</strong>
-
<form method="post">
-
<div asp-validation-summary="All"></div>
-
<label asp-for="Movie.Id"></label>
-
<input asp-for="Movie.Id" /><br />
-
<label asp-for="Movie.Title"></label>
-
<input asp-for="Movie.Title" />
-
<span asp-validation-for="Movie.Title"></span><br />
-
<label asp-for="Movie.ReleaseYear"></label>
-
<input asp-for="Movie.ReleaseYear" /><br />
-
<label asp-for="Movie.Summary"></label>
-
<textarea asp-for="Movie.Summary"></textarea><br />
-
<button type="submit">Save</button>
-
<a asp-page="./Index">Cancel</a>
-
</form>
修改Create.cshtml.cs。
-
[BindProperty]
-
public MovieInputModel Movie { get; set; }
-
publicvoid OnGet()
-
{
-
this.Movie = new MovieInputModel();
-
}
-
public IActionResult OnPost()
-
{
-
if (!ModelState.IsValid)
-
return Page();
-
var model = new Movie
-
{
-
Id = this.Movie.Id,
-
Title = this.Movie.Title,
-
ReleaseYear = this.Movie.ReleaseYear,
-
Summary = this.Movie.Summary
-
};
-
service.AddMovie(model);
-
return RedirectToPage("./Index");
-
}
修改Edit.cshtml。
-
@page "{id:int}"
-
@model EditModel
-
@{
-
}
-
<strong>Edit Movie - @Model.Movie.Title</strong>
-
<form method="post">
-
<div asp-validation-summary="All"></div>
-
<input type="hidden" asp-for="Movie.Id" />
-
<label asp-for="Movie.Title"></label>
-
<input asp-for="Movie.Title" />
-
<span asp-validation-for="Movie.Title"></span><br />
-
<label asp-for="Movie.ReleaseYear"></label>
-
<input asp-for="Movie.ReleaseYear" /><br />
-
<label asp-for="Movie.Summary"></label>
-
<textarea asp-for="Movie.Summary"></textarea><br />
-
<button type="submit">Save</button>
-
<a asp-page="./Index">Cancel</a>
-
</form>
修改Edit.cshtml.cs。
-
[BindProperty]
-
public MovieInputModel Movie { get; set; }
-
public IActionResult OnGet(int id)
-
{
-
var model = this.service.GetMovie(id);
-
if (model == null)
-
return RedirectToPage("./Index");
-
this.Movie = new MovieInputModel
-
{
-
Id = model.Id,
-
Title = model.Title,
-
ReleaseYear = model.ReleaseYear,
-
Summary = model.Summary
-
};
-
return Page();
-
}
-
public IActionResult OnPost()
-
{
-
if (!ModelState.IsValid)
-
return Page();
-
var model = new Movie
-
{
-
Id = this.Movie.Id,
-
Title = this.Movie.Title,
-
ReleaseYear = this.Movie.ReleaseYear,
-
Summary = this.Movie.Summary
-
};
-
service.UpdateMovie(model);
-
return RedirectToPage("./Index");
-
}
修改Delete.cshtml。
-
@page "{id:int}"
-
@model DeleteModel
-
@{
-
}
-
<strong>Delete Movie</strong>
-
<p>Are you sure you want to delete <strong>@Model.Title</strong>?</p>
-
<form method="post">
-
<input type="hidden" asp-for="Id" />
-
<button type="submit">Yes</button>
-
<a asp-page="./Index">No</a>
-
</form>
修改Delete.cshtml.cs。
-
[BindProperty]
-
publicint Id { get; set; }
-
public string Title { get; set; }
-
public IActionResult OnGet(int id)
-
{
-
var model = this.service.GetMovie(id);
-
if (model == null)
-
return RedirectToPage("./Index");
-
this.Id = model.Id;
-
this.Title = model.Title;
-
return Page();
-
}
-
public IActionResult OnPost()
-
{
-
if (!service.MovieExists(this.Id))
-
return RedirectToPage("./Index");
-
service.DeleteMovie(this.Id);
-
return RedirectToPage("./Index");
-
}
运行并浏览到/电影。
点击时,首先编辑(注意网址是/ Movies / Edit / 1),
点击删除(注意URL将是/电影/删除/ 3),
注意
你可以下载源代码来玩它。
讨论
在ASP.NET Core 2.0中引入了Razor页面,可以更快地构建简单的Web应用程序,并且是使用各种ASP.NET Core概念(如Razor,Layout Pages和Tag Helper等)的好方法。
Razor Pages使用 ASP.NET Core MVC ,但是编程模型不一样。与MVC不同,控制器,模型和视图是体系结构的不同组成部分,在Razor Pages中,这些概念被集中到一个页面模型的页面中。
页面模型
我喜欢将Page Model视为Controller和Models的组合。他们就像控制器,因为他们收到的HTTP请求,像一个模型,因为他们拥有视图的数据/属性。
对于.cshtml文件充当Page Model,它必须包含@page指令的第一行。.cshtml.cs(代码隐藏)类继承自PageModel抽象类。按照惯例,代码隐藏类有 模型 附加到页面的名称,例如索引页的代码隐藏是IndexModel。
路由
路由到页面取决于它们在您的项目目录结构中位于Pages文件夹下的位置(默认情况下)。如果URL中没有指定页面,则使用默认的索引。
在我们的示例中,我们导航到URL / Movies以查看位于我们的解决方案中的/ Pages / Movies / Index的页面。类似地,URL / Movies / Edit映射到/ Pages / Movies / Edit页面。
ASP.NET Core 2.0引入了用于生成URL的新构造,
-
Page()方法
-
asp页面标签助手
-
PageModel基类的RedirectToPage()方法
请注意,以/开头的URL是绝对路径并指向Pages文件夹。我们也可以使用与./或../相关的URL,或者简单的省略/。为了更好地理解,这里是从Page / Movies / Delete导航到各种URL时发生的情况,
我们可以将路由约束指定为@page指令的一部分,以指示运行时期望路由参数,或者丢失404(未找到)。在我们的编辑页面中,我们使用约束 -
-
@page “{id:int}”
如果您希望使用不同于Pages的名称作为根文件夹,则可以通过配置页面选项来实现。
-
services.AddMvc()
-
.AddRazorPagesOptions(options =>
-
{
-
options.RootDirectory = "/MyPages";
-
});
处理程序
如前所述,页面接收HTTP请求(即充当MVC世界中的Action),并由 处理程序方法处理。 这些处理程序返回IActionResult并使用On [verb]的约定命名。最常用的是OnGet()和OnPost()。对于异步,您可以将Async附加到名称,但是这是可选的。
PageModel基类具有RedirectToPage()方法(返回RedirectToPageResult)导航到其他页面和Page()方法(返回PageResult)返回当前页面。请注意,如果处理程序方法的返回类型为void,则运行时将返回PageResult。
为了让HTTP动词拥有多个处理器方法,我们可以使用 asp-page-handler属性来使用 命名处理器方法。这里指定的名字应该在页面类中使用约定On [动词] [处理程序]。让我们添加一个链接到我们的电影列表删除电影,
-
<td>
-
<a asp-page="./Index"
-
asp-page-handler="delete"
-
asp-route->Delete</a>
-
</td>
在页面模型类中添加一个方法来处理这个请求(注意它的名字和参数),
-
public IActionResult OnGetDelete(int id)
-
{
-
if (!service.MovieExists(id))
-
return RedirectToPage("./Index");
-
service.DeleteMovie(id);
-
return RedirectToPage("./Index");
-
}
将鼠标移到删除链接上,您会注意到像/ Movies?id = 1&handler = delete这样的URL。如果您希望用URL段替换查询参数,则将以下路由约束添加到@page指令,生成的URL将为/ Movies / delete / 1,
-
@page "{handler?}/{id:int?}"
捆绑
页面上的@model指令指向页面模型类,因为如前所述,该类充当Razor页面的模型。这适用于读取属性,但为了在发布数据时填充它们(即,当使用除GET之外的动词时),我们需要使用属性[BindProperty]来标记要使用模型绑定的属性。
请发表评论