net core RESTful Api笔记⑥
分页
针对集合资源的翻页
1.集合资源数量通常比较大,需要对他们进行翻页查询
2.避免性能问题
3.参数通过querystring进行传递:api/companies?pageNumber=1&pageSize=5
4.每一页书需要进行控制
5.默认应该进行分页
6.应该对底层数据存储进行分页
因为之前对查询,更新,添加的model分开写了,所以这里只需要修改查询的dto就行了,又因为这是多个查询所以修改
CompanyDtoParameters:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.DtoParameters { /// <summary> /// 复杂的查询条件 /// </summary> public class CompanyDtoParameters { public string CompanyName { get; set; } public string searchTerm { get; set; } //默认1 public int PageNumber { get; set; } = 1; //默认5 public int PageSize { get; set; } = 5; } }
修改CompanyRepository:添加了分页,删除之前两个判断冗余的查询
public async Task<IEnumerable<Company>> GetCompaniesAsync(CompanyDtoParameters parameters) { if (parameters == null) { throw new ArgumentException(nameof(parameters)); } var queryExpression = _context.companies as IQueryable<Company>; if (!string.IsNullOrEmpty(parameters.CompanyName)) { parameters.CompanyName = parameters.CompanyName.Trim(); queryExpression = queryExpression.Where(x => parameters.CompanyName == x.Name); } if (!string.IsNullOrEmpty(parameters.searchTerm)) { parameters.searchTerm = parameters.searchTerm.Trim(); queryExpression = queryExpression.Where(x => x.introduction.Contains(parameters.searchTerm) || x.Name.Contains(parameters.searchTerm)); } queryExpression = queryExpression.Skip(parameters.PageSize * (parameters.PageNumber - 1)).Take(parameters.PageSize); return await queryExpression.ToListAsync(); }
postman:
返回翻页的信息
1.包含前一页和后一页的连接
2.其他信息:pageNumber,pagesize,总数,总页数
翻页信息
{ "items":[{company},{company}...], "pagination":{"pageNumber":1,"pageSize":5,...} }
响应的body不符合请求的accept header,这不是application/json,他应该是一个新的media type
破坏了restful约束,api消费者不知道如何使用application/json这个media type
当时用application/json请求的时候,翻页信息不应该放到body里,应该有自定一的header里:x-pagination
实现自定义类:PagedList<T>:属性:CurrentPage,TotalPages,HasPrevious,HasNext
可以复用,在使用它创建翻页信息
添加pageList:里面都是分页的信息
using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.Helpers { public class PageList<T>:List<T> { public int CurrentPage { get; private set; } public int ToTalPages { get; private set; } public int PageSize { get; private set; } public int ToTalCount{ get; private set; } public Boolean HasPrevious => CurrentPage > 1; public Boolean HasNext => CurrentPage < ToTalPages; public PageList(List<T>items,int count,int pageNumber,int pageSize) { ToTalCount = count; PageSize = pageSize; CurrentPage = pageNumber; ToTalPages = (int)Math.Ceiling(count/(double)pageSize); AddRange(items); } public static async Task<PageList<T>> CreatAsync(IQueryable<T>source,int pageNumber,int pageSize) { var count = await source.CountAsync(); var items = await source.Skip((pageNumber - 1) + pageSize).Take(pageSize).ToListAsync(); return new PageList<T>(items,count,pageNumber,pageSize); } } }
CompanyDtoParameters:修改查询多个公司的载体model
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Rountine.API.DtoParameters { /// <summary> /// 复杂的查询条件 /// </summary> public class CompanyDtoParameters { private const int MaxPageSize = 100; public string CompanyName { get; set; } public string searchTerm { get; set; } //默认1 public int PageNumber { get; set; } = 1; //默认5 public int PageSize { get; set; } = 5; } }
CompainesController:修改了查询多个公司的api,UnsafeRelaxedJsonEscaping修正返回的链接里的&
[HttpGet(Name =nameof(GetCompanies))] public async Task<ActionResult<IEnumerable<CompanyDto>>> GetCompanies([FromQuery] CompanyDtoParameters parameters) { var companies = await _companyRepository.GetCompaniesAsync(parameters); var previousPageLink = companies.HasPrevious ? CreateCompaniesResourceUri(parameters, ResourceUriType.PreviousPage) : null; var nextPageLink = companies.HasNext ? CreateCompaniesResourceUri(parameters, ResourceUriType.NextPage) : null; var paginationMetadate = new { totalCount= companies.ToTalCount, pageSize=companies.PageSize, currentPage=companies.CurrentPage, totalPages=companies.ToTalPages, previousPageLink, nextPageLink }; Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(paginationMetadate, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }) ) ; var companyDto = _mapper.Map<IEnumerable<CompanyDto>>(companies); return Ok(companyDto); }
CreateCompaniesResourceUri:ControllerBase使用Url.Link将前一页后一页的链接弄出来
private string CreateCompaniesResourceUri(CompanyDtoParameters parmaters, ResourceUriType type) { switch (type) { case ResourceUriType.PreviousPage: return Url.Link(nameof(GetCompanies), new { pageNumber = parmaters.PageNumber - 1, pageSize= parmaters.PageSize, companyName= parmaters.CompanyName, searchTerm= parmaters.searchTerm }); case ResourceUriType.NextPage: return Url.Link(nameof(GetCompanies), new { pageNumber = parmaters.PageNumber + 1, pageSize = parmaters.PageSize, companyName = parmaters.CompanyName, searchTerm = parmaters.searchTerm }); default: return Url.Link(nameof(GetCompanies), new { pageNumber = parmaters.PageNumber, pageSize = parmaters.PageSize, companyName = parmaters.CompanyName, searchTerm = parmaters.searchTerm }); } }
postman: