前言
這篇文章我們將使用不同的方式實現手動分頁(關於高端大氣上檔次的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種分頁技術各有優劣,感覺碰到具體應用的時候再做選擇了


