基于.NetCore开发博客项目 StarBlog - (23) 文章列表接口分页、过滤、搜索、排序 - 程...
source link: https://www.cnblogs.com/deali/p/16992573.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
上一篇留的坑,火速补上。
在之前的第6篇中,已经有初步介绍,本文做一些补充,已经搞定这部分的同学可以快速跳过,基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表
对标准的WebApi来说,分页、过滤、搜索、排序是很常见的功能,既可以方便用户查看数据,又可以提升程序性能。
通用请求参数
定义一个类来作为通用的请求参数
列表接口通用的参数是这几个:PageSize
, Page
, Search
, SortBy
反映到URL上,就是 Blog/?pageSize=10&page=1&search=关键词
这样的形式
public class QueryParameters {
/// <summary>
/// 最大页面条目
/// </summary>
public const int MaxPageSize = 50;
private int _pageSize = 10;
/// <summary>
/// 页面大小
/// </summary>
public int PageSize {
get => _pageSize;
set => _pageSize = (value > MaxPageSize) ? MaxPageSize : value;
}
/// <summary>
/// 当前页码
/// </summary>
public int Page { get; set; } = 1;
/// <summary>
/// 搜索关键词
/// </summary>
public string? Search { get; set; }
/// <summary>
/// 排序字段
/// </summary>
public string? SortBy { get; set; }
}
文章列表请求参数
在通用请求参数 QueryParameters
的基础上,派生出文章列表的请求参数类 PostQueryParameters
public class PostQueryParameters : QueryParameters {
/// <summary>
/// 仅请求已发布文章
/// </summary>
public bool OnlyPublished { get; set; } = false;
/// <summary>
/// 文章状态
/// </summary>
public string? Status { get; set; }
/// <summary>
/// 分类ID
/// </summary>
public int CategoryId { get; set; } = 0;
/// <summary>
/// 排序字段
/// </summary>
public new string? SortBy { get; set; } = "-LastUpdateTime";
}
在通用请求参数的基础上,增加文章相关的筛选字段。
SortBy
字段使用 new
关键词覆盖基类属性,设置为默认排序是最后更新时间,前面加个减号表示倒序。
service
在 StarBlog.Web/Services/PostService.cs
中封装获取分页列表的方法
代码里有注释,比较容易,根据 PostQueryParameters
中的各种参数来做过滤筛选
public IPagedList<Post> GetPagedList(PostQueryParameters param) {
var querySet = _postRepo.Select;
// 是否发布
if (param.OnlyPublished) {
querySet = _postRepo.Select.Where(a => a.IsPublish);
}
// 状态过滤
if (!string.IsNullOrEmpty(param.Status)) {
querySet = querySet.Where(a => a.Status == param.Status);
}
// 分类过滤
if (param.CategoryId != 0) {
querySet = querySet.Where(a => a.CategoryId == param.CategoryId);
}
// 关键词过滤
if (!string.IsNullOrEmpty(param.Search)) {
querySet = querySet.Where(a => a.Title.Contains(param.Search));
}
// 排序
if (!string.IsNullOrEmpty(param.SortBy)) {
// 是否升序
var isAscending = !param.SortBy.StartsWith("-");
var orderByProperty = param.SortBy.Trim('-');
querySet = querySet.OrderByPropertyName(orderByProperty, isAscending);
}
return querySet.Include(a => a.Category).ToList()
.ToPagedList(param.Page, param.PageSize);
}
搜索的实现
在上面 service 的代码中
可以看到搜索只是简单的“关键词过滤”
使用 Title.Contains(param.Search)
,转换成SQL就是
select * from post where title like '%关键词%'
单纯判断标题字符串中是否包含有关键词的子串。
这对于简单搜索一下文章是够用的,如果要像谷歌、百度这类搜索引擎一样能搜到文章的内容,需要用上全文检索。
现在主流的就是 ElasticSearch 和 Solr,后续可以考虑把这个功能加入本项目~
PS:关于全文检索,我之前写过一篇文章:全文检索引擎原理以及Lucene简单介绍
同时开源了一个玩具级的全文检索引擎,https://github.com/Deali-Axy/CloverSearch
分页的实现
本项目使用 X.PagedList
来实现分页功能
这个组件在结合MVC使用很方便,如果纯WebApi的话,用数据库自带的分页是更好的选择,性能更好。
这个分页组件是在
IEnumerable<T>
上添加了扩展方法ToPagedList
,所以在用的时候要先把数据都读取出来,再执行分页,性能不如在数据库里做好分页再读出来,很多ORM都支持这个功能,FreeSQL也不例外。用法例子:
var list = fsql.Select<Topic>() .Where(a => a.Id > 10) .Count(out var total) //总记录数量 .Page(1, 20) .Tolist();
详情请查看FreeSQL官方文档:https://freesql.net/guide/paging.html
用上 X.PagedList
这个组件后,在任意 IEnumerable<T>
对象上执行 ToPagedList
方法,可以得到 IPagedList<T>
对象
这个对象处理当前页面的列表数据,还有分页信息。
为了让前端可以方便的使用这部分信息,我又写了个扩展方法。
StarBlog.Web/Extensions/PagedListExt.cs
public static class PagedListExt {
public static PaginationMetadata ToPaginationMetadata(this IPagedList page) {
return new PaginationMetadata {
PageCount = page.PageCount,
TotalItemCount = page.TotalItemCount,
PageNumber = page.PageNumber,
PageSize = page.PageSize,
HasNextPage = page.HasNextPage,
HasPreviousPage = page.HasPreviousPage,
IsFirstPage = page.IsFirstPage,
IsLastPage = page.IsLastPage,
FirstItemOnPage = page.FirstItemOnPage,
LastItemOnPage = page.LastItemOnPage
};
}
public static string ToPaginationMetadataJson(this IPagedList page) {
return JsonSerializer.Serialize(ToPaginationMetadata(page));
}
}
这样就可以在分页后得到的 IPagedList
对象上执行 ToPaginationMetadata
得到分页元数据了。
这个 PaginationMetadata
也是本项目里定义的 ViewModel,StarBlog.Web/ViewModels/PaginationMetadata.cs
public class PaginationMetadata {
public int PageCount { get; set; }
public int TotalItemCount { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
public bool HasPreviousPage { get; set; }
public bool HasNextPage { get; set; }
public bool IsFirstPage { get; set; }
public bool IsLastPage { get; set; }
public int FirstItemOnPage { get; set; }
public int LastItemOnPage { get; set; }
}
controller与最终效果
[AllowAnonymous]
[HttpGet]
public ApiResponsePaged<Post> GetList([FromQuery] PostQueryParameters param) {
var pagedList = _postService.GetPagedList(param);
return new ApiResponsePaged<Post> {
Message = "Get posts list",
Data = pagedList.ToList(),
Pagination = pagedList.ToPaginationMetadata()
};
}
获取到分页数据之后,输出 ApiResponsePaged<T>
类型的返回值
这个也是我封装的接口返回值类型,下一篇文章会详细介绍
Data
属性就是列表数据,Pagination
属性是分页的信息。
请求这个接口返回的效果如下
{
"pagination": {
"pageCount": 40,
"totalItemCount": 394,
"pageNumber": 1,
"pageSize": 10,
"hasPreviousPage": false,
"hasNextPage": true,
"isFirstPage": true,
"isLastPage": false,
"firstItemOnPage": 1,
"lastItemOnPage": 10
},
"statusCode": 200,
"successful": true,
"message": "Get posts list",
"data": [{...},{...},{...},{...},{...}]
}
__EOF__
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK