Bit of Technology

  • Archive
  • About Me
    • Advertise
    • Disclaimer
  • Speaking
  • Contact

Building ASP.Net Web API RESTful Service – Part 7

November 25, 2013 By Taiseer Joudeh 33 Comments

Be Sociable, Share!

  • Tweet
  • Email
  • WhatsApp

This is the seventh part of Building ASP.Net Web API RESTful Service Series. The topics we’ll cover are:

  • Building the Database Model using Entity Framework Code First – Part 1.
  • Applying the Repository Pattern for the Data Access Layer – Part 2.
  • Getting started with ASP.Net Web API – Part 3.
  • Implement Model Factory, Dependency Injection and Configuring Formatters – Part 4.
  • Implement HTTP actions POST, PUT, and DELETE In Web API – Part 5.
  • Implement Resources Association – Part 6.
  • Implement Resources Pagination – Part 7 (This Post).
  • Securing Web API – Part 8.
  • Preparing Web API for Versioning – Part 9.
  • Different techniques to Implement Versioning – Part 10.
  • Caching resources using CacheCow and ETag – Part 11.

Update (2014-March-5) Two new posts which cover ASP.Net Web API 2 new features:

  • ASP.NET Web API 2 Attribute Routing.
  • IHttpActionResult as new response type and CORS Support.

Implement Resources Pagination

In this post we’ll discuss the different ways to implement results pagination, we’ll implement manual pagination then format the response in two different ways (having pagination meta-data in an envelope, and in pagination header).

It is well known that overwhelming your server with a query which returns hundreds of thousand of records is a bad thing, when we are designing an API, we should consider returning the results of our GET methods in paginated way, i.e. providing 10 results on each request, and giving the API consumer the ability to navigate through results, specify the size of the page,  and which page he wants.

Manual Pagination and Envelopes

We’ll modify the “CoursesController” to use pagination instead of returning the whole courses at once.

Let’s see the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
        };
 
    }

What we’ve done here is simple, we’ve introduced the below to “CoursesController”

  • Added two new optional parameters to the GET method with default values, those optional parameters are translated to query string values, i.e. if we want to request the second page of courses our GET request will be on the form: http://localhost:{your_port}/api/courses/?page=1 Notice we didn’t specify the pageSize parameter and it took the default values 10. Sample of response will be on the form below:

1
2
3
4
5
6
7
8
9
{
 
    "totalCount": 33,
    "totalPages": 4,
    "prevPageLink": "http://localhost:8323/api/courses?page=0&pageSize=10",
    "nextPageLink": "http://localhost:8323/api/courses?page=2&pageSize=10",
    "results": [ /* Array containts the results*/ ]
 
}

  • The method “GetAllCourses” in our Repository returns “IQueryable” response, which is perfect because till this moment the query is represented in memory and didn’t execute against SQL server, so paging and order by for query are executing correctly.
  • We’re using envelope to wrap our response, this envelope contains pagination meta-data inside the JSON response such as: totalCount, totalPages, prevPageLink, nextPageLink. It is important to return the total records count and total pages so API consumer will be able to bind results and apply pagination on grid easily.

Returning the pagination meta-data in the response body is a common technique, there is nothing wrong about it as everything is visible for the developer, the draw back of this approach is that API consumer will dig into the response to extract data he was originally asking for and maybe ignoring all the pagination meta data we returned if he do not need to use it. So the other cleaner way to return pagination meta-data is to include them in response header, so we’ll keep the response body for the results only and we’ll add new header called “X-Pagination” which contains all pagination meta-data.

Manual Pagination and Pagination Headers

We’ll modify “StudentsController” to use headers to return pagination meta-data, in this approach API consumer can use this header if he is interested in the pagination meta-data, other wise he can just ignore this header and read the results of the query directly from response body.

Applying this is fairly simple, the pagination technique we used in “CoursesControrler” will be used the same here, except for returning the pagination meta-data in new header. Take a look on the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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;
    }

Notice how we added a new header to the response collection headers which contains a serialized JSON object containing all pagination meta-data.

In the next post we’ll talk briefly about web API security and how we can implement Basic authentication.

Source code is available on GitHub.

Be Sociable, Share!

  • Tweet
  • Email
  • WhatsApp

Related Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Identity 2.1 Accounts Confirmation, and Password Policy Configuration – Part 2
  • ASP.NET Identity 2.1 with ASP.NET Web API 2.2 (Accounts Management) – Part 1

Filed Under: ASP.NET, ASP.Net Web API, Entity Framework, Web API Tutorial Tagged With: API, ASP.NET, Entity Framework, Pagination, REST, RESTful, Tutorial, Web API, Web Service

Comments

  1. SutoCom says

    November 29, 2013 at 2:31 am

    Reblogged this on Sutoprise Avenue, A SutoCom Source.

    Reply
  2. Martin says

    December 20, 2013 at 12:50 am

    Instead of creating your own pagination code you might want to consider using the existing WebAPI OData package instead.

    Reply
    • tjoudeh says

      December 20, 2013 at 1:05 am

      Hello Martin,

      You are absolutely right, but I wanted to dominstrste pagination manually, I might blog about oData support for web API.

      Reply
      • Amit says

        August 22, 2017 at 8:34 am

        Thakns I learn a lot from you

        Reply
  3. Zaid says

    February 13, 2014 at 11:11 am

    thanks Taiseer for this … I was facing a terrible code and found the solution here

    Reply
    • tjoudeh says

      February 13, 2014 at 11:12 am

      Your welcome 😉 glad it solved your issue!

      Reply
  4. Chris says

    June 5, 2014 at 7:23 pm

    Taiseer you have really been of immense help to me by this wonderful tutorial. Thanks so much!

    Reply
    • Taiseer Joudeh says

      June 5, 2014 at 11:19 pm

      Thanks Chris, it is always pleasure to hear nice comments from blog readers 🙂

      Reply
  5. Michael says

    July 10, 2014 at 1:58 pm

    Hello Teiseer, How would you proceed in order to move pagination more to a database level ? I am new in Web API, but as I rememeber from classic ASP.NET, it was always recommended to send a paginated query (e.g. using rownum for Oracle, using keword “limit” for other databases) – it supposed to be faster and less resources consuming.

    What is the best way of pagination in web Api when taking performance into consideration ?

    Reply
    • Taiseer Joudeh says

      July 10, 2014 at 11:42 pm

      Hi Michael,
      The current pagination is done on the database level not on the returned data. In EF The IQueryable interface allows you to prepare in memory query and this query do not get executed against database until you call .ToList() or .ToArray(), etc… You can run SQL profiler and watch how the query is executed or you can use .ToString() and watch the TSQL that will be generated against the database.
      Hope this answers your question.

      Reply
  6. Stan92 says

    November 6, 2014 at 4:32 pm

    A quick question..
    If I need to add additional criterias (like search by name, keywords, etc…) and also add the Sort parameters (key and direction).. What would be the best approach to handle theses parameters from the Restful service?
    Thanks.

    Reply
    • Taiseer Joudeh says

      November 7, 2014 at 1:13 am

      Hi Stan,
      As long you are doing search (Getting data from the server) then you need to pass those fields as part of the query string. There is lot of examples on SO which shows you how to do sorting as wel..

      Reply
      • Stan92 says

        November 7, 2014 at 8:21 am

        Hi Taiseer,
        Thanks.. I’ve seen some examples. and yes, they pass the fields as part of the query string.
        My additional question, read as a best practice, they say it’s to give the ability to choose returned fields.
        How this can be done knowing you define each Entity ?

        Reply
        • Taiseer Joudeh says

          November 7, 2014 at 11:41 am

          Well what are you looking for can be implemented using OData, check my other tutorial which shows how you can select specific columns.

          Reply
  7. JC says

    November 13, 2014 at 7:01 pm

    Awesome. Exactly what I needed.

    Reply
    • Taiseer Joudeh says

      November 14, 2014 at 12:29 am

      Happy to help, just simple note here, do not prefix your custom header with (X) it is not good practice anymore, you can prefix them with your company name.

      Reply
  8. andystarkgnvl says

    January 14, 2015 at 6:35 am

    Taiseer, would the GET method you described above and the pagination headers work with jquery DataTables? It seems they have a very specific return format. If I understand correctly WebApi returns generic objects and the format is left to content negotiation?

    Reply
    • Taiseer Joudeh says

      January 14, 2015 at 4:41 pm

      Hi, I do not know how Jquery DataTable pagination works, but this way is standard way to do pagination, so I guess it should work.

      Reply
    • Chen L says

      January 29, 2015 at 6:11 am

      You need create a wrapper mapper in JS to transfer your model back to whatever JS(JQuery) required format.

      Reply
  9. Chen L says

    January 29, 2015 at 5:47 am

    hmm, I prefer build a generic type class like:
    public class PagedList : List, IPagedList,,
    This implementation couple the code too much, for each entity you have to specific the paging logic again and again.

    Reply
    • Chen L says

      January 29, 2015 at 6:02 am

      the interface will looks like the following:
      public interface IPagedList : IList
      {
      int PageIndex { get; }
      int PageSize { get; }
      int TotalCount { get; }
      int TotalPages { get; }
      bool HasPreviousPage { get; }
      bool HasNextPage { get; }

      //I personal don’t like the previous/next page implementation, that’s the UI logic, it better handle in the front end(either //javascript or .Net view, the front end need to specify each index page’s logic anyway and build the front end paging by using PageIndex, PageSize, TotalCount) that’s just my personal preference.
      }

      Reply
      • Taiseer Joudeh says

        January 29, 2015 at 10:42 am

        Thanks for your message, the next/previous link is part of the HATEOAS specifications, but your suggestion is valid too and there is many ways to implement pagination, not only the ones I’ve mentioned.

        Reply
  10. dvnandover says

    February 1, 2015 at 10:19 pm

    when I changed the Get method in the CoursesController from public IEnumerable Get() to
    public Object Get(int page = 0, int pageSize = 10). I got the error below. How do I get around the error?
    An error has occurred.The ‘ObjectContent`1’ type failed to serialize the response body for content type ‘application/xml; charset=utf-8’.System.InvalidOperationException

    Reply
  11. Haitham Shaaban says

    December 8, 2015 at 4:42 pm

    Thanks Taiseer for your great efforts
    Please , what is the benefit of domain models ? because every time deal with DAL direct
    IQueryable query;

    query = TheRepository.GetAllStudentsWithEnrollments().OrderBy(c => c.LastName);

    thanks

    Reply
    • Taiseer Joudeh says

      December 9, 2015 at 11:21 am

      Hi Haitham,
      It is good practice to distinguish your domain business models from your lower database ORM classes, I do not want to expose the EF classes as a response to API consumers, I want to have my own POCO classes (Models, DTOs) so I can shape the response as I want. You can use AutoMapper for this task too, but I prefer to do it manually as it gives great flexibility.
      Hope this answers your question.

      Reply
  12. Tomas says

    February 7, 2016 at 9:21 pm

    Thanks ! But where do I put the decoration [Required] if I need a field required

    Reply
    • Taiseer Joudeh says

      February 7, 2016 at 11:42 pm

      Check how I used this attribute here

      Reply
      • Tomas says

        February 8, 2016 at 12:33 am

        Ok, but I would like to use it in this project. Tested it in the entity model class. Perhaps this project is not design to permit [Required] attribute on model class?

        Reply
  13. Armind says

    May 12, 2016 at 7:02 pm

    Thank you a lot Taiseer for your great effot , im new in the web api , so i need an advice from an expert.

    my problematic is that i want to develope an api that can retrieve data from a distant oracle database ( in json format )

    how can i proceed ? for a 1st step.

    i have already an mvc webservice developed in c# that manage my back-office site and the database , what i want to do : it’s an api wich will play the role of a bridge beetween a distant oracle database and my webservice.

    thanks.

    Reply
    • Taiseer Joudeh says

      May 23, 2016 at 11:17 am

      Hello Armind,
      You need to follow everything here except when you want to use your Oracle ORM or data provider to access your oracle tables and do your queries, everything else will work the same, you can get your data from oracle source, put them in a model (POCO class) and send them as a response and Web API formatter will serialise those to JSON response.

      Reply
  14. jordy says

    October 13, 2016 at 12:04 am

    hi, actually i have a problem, i cant to get the url path, i’m using .net core, the error is specify in UrlHelper

    Reply
    • Taiseer Joudeh says

      October 16, 2016 at 12:49 pm

      Hi Jordy, well that’s tutorial is for ASP.NET 4, I do not think it will help in case of asp.net core

      Reply
  15. salam says

    November 30, 2017 at 3:04 pm

    hi,
    please help me

    how can i get prePageLink in Odata web api?

    Uri prePageLink = GetPrePageLink(Request.RequestUri,Request.GetQueryNameValuePairs(),5); ????

    ODataQuerySettings settingsQueryOptions = new ODataQuerySettings() { PageSize = 5 };

    var CityListQuery = DIManager.Get().GetCityList();
    IQueryable appliedOdataToCityListQuery = (IQueryable)queryOptionsCity.ApplyTo(CityListQuery, settingsQueryOptions);

    return new ResultDto(
    appliedOdataToCityListQuery.ToList(),
    Request.GetNextPageLink(),
    prePageLink
    );

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

About Taiseer

Husband, Father, Consultant @ MSFT, Life Time Learner... Read More…

Buy me a coffeeBuy me a coffee

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Blog Archives

Recent Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
  • ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4

Tags

AJAX AngularJS API API Versioning ASP.NET Authentication Autherization Server Azure Active Directory B2C Azure AD B2C basic authentication C# CacheCow Client Side Templating Code First Dependency Injection Entity Framework ETag Foursquare API HTTP Caching HTTP Verbs IMDB API IoC Javascript jQuery JSON JSON Web Tokens JWT Model Factory Ninject OAuth OData Pagination Resources Association Resource Server REST RESTful Single Page Applications SPA Token Authentication Tutorial Web API Web API 2 Web API Security Web Service wordpress.com

Search

Copyright © 2019 · eleven40 Pro Theme on Genesis Framework · WordPress · Log in

loading Cancel
Post was not sent - check your email addresses!
Email check failed, please try again
Sorry, your blog cannot share posts by email.