我们创建了项目ZL.Poem.WebApi用来向外界暴露Api,这个项目本身就是一个网站,这里我们在这个项目中增加一些页面,实现一些基本功能。我们使用Asp.Net Core引入的轻量级的页面技术RazorPage来实现这些功能。
RazorPage让我想起了WebForm的编程模型:前端页面和后端代码,也有MVC的痕迹,后端代码实际是前端视图的控制器和模型的集成。
创建基本页面
我们先在项目中创建Pages目录,然后创建两个Razor页面:
这两个页面分别为Index.cshtml和SearchPoet.cshtml。
然后,我们在项目属性->调试中修改启动路径:
修改为index,现在,启动项目,会发现起始页面已经是Index页面了。
简单的Index页面
为了说明Razor页面的工作过程,我们编写一个简单的功能:在页面中显示分类列表。
修改Index.cshtml.cs:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.RazorPages;
using ZL.Poem.Application.Poems;
namespace ZL.Poem.WebApi.Pages
{
public class IndexModel : PageModel
{
private IPoemAppService _appService;
public List<CategoryDto> Categories;
/// <summary>
/// 这里通过依赖注入获取IPoemAppService
/// </summary>
/// <param name="appService"></param>
public IndexModel(IPoemAppService appService)
{
_appService = appService;
}
/// <summary>
/// 相应Get动作
/// </summary>
public void OnGet()
{
Categories=_appService.GetAllCategories();
}
}
}
代码很简单:1)增加了IndexModel的构造函数,目的是让依赖注入框架可以将IPoemAppService作为参数传入。2)增加了Categories列表,这是用来在页面上显示的数据。3)在OnGet中获取Categories列表。
页面代码也很简单:
@page
@model ZL.Poem.WebApi.Pages.IndexModel
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<table class="table">
<thead>
<tr>
<th>
分类
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Categories)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.CategoryName)
</td>
</tr>
}
</tbody>
</table>
与MVC的视图基本一样,采用Razor语法。这段代码很简单,就是显示分类列表。
增加布局页面
现在我们为网站的视图页面增加模板页,这样就不需要为每个页面都设置相同的布局内容。首先,我们在Pages目录下创建Shared目录,然后增加一个Razor页面,命名为_Layout。请注意,在创建时,取消“生成PageModel”类的选项,这样就不会生成后台模型代码。
页面代码如下:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewData["Title"]</title>
</head>
<body>
@RenderBody()
</body>
</html>
然后在Pages目录中增加_ViewStart.cshtml,这个页面定义其它页面使用的模板页:
@{
Layout = "_Layout";
}
还有页面_ViewImports.cshtml,这个页面定义其它页面需要的引用,这里我们需要引用TagHelpers:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
增加诗人查询页面和诗查询页面
下面增加主要的两个页面,诗人查询和诗查询。
在Pages目录下增加一个Razor页面,命名为SearchPoet.cshtml,后台代码如下:
using Abp.Application.Services.Dto;
using Microsoft.AspNetCore.Mvc.RazorPages;
using ZL.Poem.Application.Poems;
namespace ZL.Poem.WebApi.Pages
{
/// <summary>
/// 诗人查询的后台代码
/// </summary>
public class SearchPoetModel : PageModel
{
/// <summary>
/// 服务
/// </summary>
private IPoemAppService _appService;
/// <summary>
/// 查询结果
/// </summary>
public PagedResultDto<PoetDto> poetResults { get; set; }
/// <summary>
/// 当前的查询关键字
/// </summary>
public string CurrentFilter { get; set; }
/// <summary>
/// 每页显示条数
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 当前页面
/// </summary>
public int CurrentPage { get; set; }
/// <summary>
/// 这里通过依赖注入获取IPoemAppService
/// </summary>
/// <param name="appService"></param>
public SearchPoetModel(IPoemAppService appService)
{
_appService = appService;
PageSize = 20;
CurrentPage = 0;
CurrentFilter = "";
}
/// <summary>
/// 响应Http Get
/// </summary>
/// <param name="SearchString">
/// 查询的关键字
/// </param>
/// <param name="pageIndex">
/// 页面Index
/// </param>
public void OnGet(string SearchString,int? pageIndex)
{
if (pageIndex.HasValue && pageIndex.Value>0)
{
CurrentPage = pageIndex.Value;
}
CurrentFilter = SearchString;
var req = new SearchPoetDto
{
Keyword = CurrentFilter,
SkipCount = CurrentPage * PageSize,
MaxResultCount = PageSize
};
poetResults = _appService.SearchPoets(req);
}
}
}
代码比较简单:1)在构造函数中,通过依赖注入引入IPoemAppService。2)在OnGet中响应http的Get,通过传入的查询关键字和页面索引进行查询。
对应的cshtml代码:
@page
@model ZL.Poem.WebApi.Pages.SearchPoetModel
@{
ViewData["Title"] = "诗人查询";
}
<h1>诗人查询</h1>
<form asp-page="./SearchPoet" method="get">
<div class="form-actions no-color">
<p>
Find by name:
<input type="text" name="SearchString" value="@Model.CurrentFilter" />
<input type="submit" value="查询" class="btn btn-default" /> |
<a asp-page="./SearchPoet">显示全部</a>
</p>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>
姓名
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.poetResults.Items)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
</tr>
}
</tbody>
</table>
@{
var HasPreviousPage = Model.CurrentPage > 0;
var HasNextPage = Model.CurrentPage * Model.PageSize < Model.poetResults.TotalCount;
var prevDisabled = !HasPreviousPage ? "disabled" : "";
var nextDisabled = !HasNextPage ? "disabled" : "";
}
@if (HasPreviousPage)
{
<a asp-page="./SearchPoet"
asp-route-pageIndex="@(Model.CurrentPage - 1)"
asp-route-SearchString="@Model.CurrentFilter"
class="btn btn-default @prevDisabled">
前一页
</a>
}
@if (HasNextPage)
{
<a asp-page="./SearchPoet"
asp-route-pageIndex="@(Model.CurrentPage + 1)"
asp-route-SearchString="@Model.CurrentFilter"
class="btn btn-default @nextDisabled">
后一页
</a>
}
诗的查询与此类似,这里不再列出。运行效果如下图:
从效果图上看,页面没有使用样式,下面,我们需要在页面中引入bootstrap和jquery等客户端库。
引入客户端库
在项目目录下,检查是否有wwwroot目录,如果没有,创建这个目录,并在目录下创建lib子目录,用来保存第三方库。
选择需要引入的库:
然后再_Layout.cshtml中增加对css文件和js文件的引用:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewData["Title"]</title>
<environment names="Development,Production">
<link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" asp-append-version="true" />
</environment>
</head>
<body>
@RenderBody()
<environment names="Development,Production">
<script src="~/lib/jquery3.3.1/dist/jquery.min.js" asp-append-version="true"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js" asp-append-version="true"></script>
</environment>
</body>
</html>
还有,就是需要注意,再Startup Configure中增加 app.UseStaticFiles();:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAbp();
app.UseSwagger();
//Enable middleware to serve swagger - ui assets(HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Poem API V1");
}); //URL: /swagger
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
//使用静态文件
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseMvc();
}
到这里,我们完成了基于RazorPage的多页面客户端。接下来,我们编写基于单页面的客户端和基于Xamarin的移动客户端,来说明Web Api的使用。
本文同步发布在我的个人网站 http://www.jiagoushi.cn