diff --git "a/\351\242\234\346\234\235\345\213\207/20240624-\345\210\206\351\241\265.md" "b/\351\242\234\346\234\235\345\213\207/20240624-\345\210\206\351\241\265.md" new file mode 100644 index 0000000000000000000000000000000000000000..ebc552ac1d8a463c2ed6a42ddbf500460b811369 --- /dev/null +++ "b/\351\242\234\346\234\235\345\213\207/20240624-\345\210\206\351\241\265.md" @@ -0,0 +1,60 @@ +## 分页 + +### 分页功能代码 + +``` +using System.Text.Json; +using Blog.Api.Domain; +using Blog.Api.Dto; +using Blog.Api.Interfaces; +using Blog.Api.Params; +using Microsoft.AspNetCore.Mvc; + +namespace Blog.Api.Controllers; +public class BlogsController : ControllerBase +{ + _blogDb = blogDb; +} + + [HttpGet] + public ActionResult GetAll(string keywords) + { + var list = _blogDb.GetAll().Where(item=>item.Title.Contains(keywords) && item.Author.Contains(keywords)).ToList(); + return Ok(new {list,keywords}); + } + [HttpGet(Name = "abc")] + public ActionResult GetAll([FromQuery] BaseParams baseParams) + { // 第一个,考虑如何从前端传递分页数据到接口 + // 第二个,后面到底是如何完成分页这个操作的 + var count = _blogDb.Table.Count(); + var totalPages = Math.Ceiling(count * 1.0 / baseParams.PageSize); + var list = _blogDb.GetAll().Skip((baseParams.PageIndex - 1) * baseParams.PageSize).Take(baseParams.PageSize).ToList(); + + /* + 返回给请求端的页信息如下(猜测): + 总共多少条数据 + 第几页数据 + 每页条目数 + 共多少页 + + + */ + // 分页的数据信息 + var pagination = new + { + pageIndex = baseParams.PageIndex, + pageSize = baseParams.PageSize, + totalPages, + totalCount = count, + previousPageUrl = baseParams.PageIndex == 1 ? null : Url.Link("abc", + new { pageIndex = baseParams.PageIndex - 1, pageSize = baseParams.PageSize }), + nextPageUrl = baseParams.PageIndex < totalPages ? Url.Link("abc", + new { pageIndex = baseParams.PageIndex + 1, pageSize = baseParams.PageSize }) : null + }; + + // 将分页数据信息附加到响应头 + Response.Headers.Append("X-Pagination", JsonSerializer.Serialize(pagination)); + return Ok(new { baseParams, list, abc = 11 * 1.0 / 10, xyz = Math.Ceiling(11 / (10 * 1.0)) }); + } + +``` \ No newline at end of file diff --git "a/\351\242\234\346\234\235\345\213\207/20240625-\345\260\201\350\243\205\345\210\206\351\241\265.md" "b/\351\242\234\346\234\235\345\213\207/20240625-\345\260\201\350\243\205\345\210\206\351\241\265.md" new file mode 100644 index 0000000000000000000000000000000000000000..e0fd7b62fe4a4de5dc68351a68629c9d9f0a80d5 --- /dev/null +++ "b/\351\242\234\346\234\235\345\213\207/20240625-\345\260\201\350\243\205\345\210\206\351\241\265.md" @@ -0,0 +1,132 @@ +## 封装分页 + +### Way1.封装分页元数据封装在响应Body中 + +修改“CoursesController”的Get方法实现分页而不是一次性把所有数据返回 + +代码如下: + +``` +public Object Get(int page = 0, int pageSize = 10) + { + IQueryable query; + + query = TheRepository.GetAllCourses().OrderBy(c => c.CourseSubject.Id); + var totalCount = query.Count(); + var totalPages = (int)Math.Ceiling((double)totalCount / pageSize); + + var urlHelper = new UrlHelper(Request); + var prevLink = page > 0 ? urlHelper.Link("Courses", new { page = page - 1 }) : ""; + var nextLink = page < totalPages - 1 ? urlHelper.Link("Courses", new { page = page + 1 }) : ""; + + var results = query + .Skip(pageSize * page) + .Take(pageSize) + .ToList() + .Select(s => TheModelFactory.Create(s)); + + return new + { + TotalCount = totalCount, + TotalPages = totalPages, + PrevPageLink = prevLink, + NextPageLink = nextLink, + Results = results + }; + + } + + ``` + +解释一下上面的代码: + + 在Get方法上添加了2个有默认值的参数,这两个参数就是用来过滤我们查询结果集的——例如:我们想获取第2页的数据,那么对应的Get请求就应该是这种形式:http://localhost:{your_port}/api/courses/?page=1,注意:在这里我们只给了一个参数,那么pageSize就是默认值10. + +客户端收到的部分响应就应该是: + +``` +{ + "totalCount": 32, + "totalPages": 4, + "prevPageLink": "http://localhost:3300/api/courses?page=0&pageSize=10", + "nextPageLink": "http://localhost:3300/api/courses?page=2&pageSize=10", + "results": [ + { + "id": 11, + "url": "http://localhost:3300/api/courses/11", + "name": "English Education 2", + "duration": 4, + "description": "The course will talk in depth about: English Education 2", + "tutor": { + "id": 4, + "email": "Kareem.Ismail@outlook.com", + "userName": "KareemIsmail", + "firstName": "Kareem", + "lastName": "Ismail", + "gender": 0 + }, + "subject": { + "id": 4, + "name": "English" + } + + }, + ] +} + +``` + + Repository中GetAllCourses的返回值为IQueryable,因此在执行skip和take方法时并没有到SQL Server中执行SQL语句,最后查询的也是分页好的数据,体现出按需查询的特色。 + + + +在我们返回给客户端的数据中,其中分页元数据中包含了totalCount, totalPages, prevPageLink, nextPageLink这些数据,对于客户端来说我们返回totalCount, totalPages这两条数据非常有用,这样就可以与一些Grid配合使用来绑定结果。 + + + +通常来说我们会将分页元数据封装在响应Body中,对于开发者来说我们提供了所有分页信息。但有的API消费者因此只想获取它请求的数据而不需要分页元数据,那么他在解析响应结果是就会很费劲,因此这里出现了另一种方式来向客户端响应分页元数据——在响应报文头部附加分页元数据:Body部分只包含请求的资源,我们新增一个头部信息“X-Pagination”。 + + + +### Way2.封装分页元数据到响应Header中 + +我们修改StudentsController来实现将分页元数据封装在Header中,使用这种方法后,需要分页元数据的客户端直接从Header部分获取,不需要的客户端直接解析响应的Body即可。 + +实现起来也非常简单,代码如下: + +``` + +public IEnumerable Get(int page = 0, int pageSize = 10) + { + IQueryable query; + + query = TheRepository.GetAllStudentsWithEnrollments().OrderBy(c => c.LastName); + + var totalCount = query.Count(); + var totalPages = (int)Math.Ceiling((double)totalCount / pageSize); + + var urlHelper = new UrlHelper(Request); + var prevLink = page > 0 ? urlHelper.Link("Students", new { page = page - 1, pageSize = pageSize }) : ""; + var nextLink = page < totalPages - 1 ? urlHelper.Link("Students", new { page = page + 1, pageSize = pageSize }) : ""; + + var paginationHeader = new + { + TotalCount = totalCount, + TotalPages = totalPages, + PrevPageLink = prevLink, + NextPageLink = nextLink + }; + + System.Web.HttpContext.Current.Response.Headers.Add("X-Pagination", + Newtonsoft.Json.JsonConvert.SerializeObject(paginationHeader)); + + var results = query + .Skip(pageSize * page) + .Take(pageSize) + .ToList() + .Select(s => TheModelFactory.CreateSummary(s)); + + return results; + } + + ``` \ No newline at end of file