前言
這篇文章我們將使用不同的方式實現手動分頁(關於高端大氣上檔次的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請求就應該是這種形式: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<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; }
這里我們添加了一個Header,里面包含一個Json序列化的分頁元數據,客戶端收到的響應:
總結
2種分頁技術各有優劣,感覺碰到具體應用的時候再做選擇了