如何在ASP.NET Core 2.0中使用Razor頁面

問題
如何在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-id="@item.Id">Edit</a></td>
-
<td><a asp-page="./Delete" asp-route-id="@item.Id">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-id="@item.Id">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]來標記要使用模型綁定的屬性。