欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【七】——实现资源的分页

程序员文章站 2022-04-19 10:40:28
前言   这篇文章我们将使用不同的方式实现手动分页(关于高端大气上档次的OData本文暂不涉及,但有可能会在系列的后期介绍,还没确定。。。),对于分页的结果,我们将采...
前言

 

这篇文章我们将使用不同的方式实现手动分页(关于高端大气上档次的OData本文暂不涉及,但有可能会在系列的后期介绍,还没确定。。。),对于分页的结果,我们将采用2种不同的方式响应给客户端(1.将分页元数据封装在响应Body中2.在http响应报文头部添加分页信息)。

 

众所周知,在服务器端一次性返回成百上千条数据是非常恐怖的,在我们设计Api的时候,对于Get方法我们应该以分页的方式返回。例如:每次响应给客户端10条数据,并且包含“上一页”和“下一页”的标签,这样用户就能去获得他想要的数据。

 

Way1.封装分页元数据封装在响应Body中

 

修改“CoursesController”的Get方法实现分页而不是一次性把所有数据返回,下面上代码:

 

复制代码

public Object Get(int page = 0, int pageSize = 10)

     {

         IQueryable<Course> 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请求就应该是这种形式:https://localhost:{your_port}/api/courses/?page=1,注意:在这里我们只给了一个参数,那么pageSize就是默认值10.

 

客户端收到的部分响应就应该是:

 

复制代码

{

    "totalCount": 32,

    "totalPages": 4,

    "prevPageLink": "https://localhost:3300/api/courses?page=0&pageSize=10",

    "nextPageLink": "https://localhost:3300/api/courses?page=2&pageSize=10",

    "results": [

        {

            "id": 11,

            "url": "https://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<StudentBaseModel> Get(int page = 0, int pageSize = 10)

        {

            IQueryable<Student> 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;

        }