Similar Posts

Leave a Reply

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

45 Comments

  1. Hi Mukesh,

    I like your idea I use similar approach for pagination. But I don’t think API should be responsible for link generation. Too many details comes from API that can be easily calculated on the front-end. Beside modern pagination components are doing this out of the box.

    1. Hi Alexander, Thanks for your feedback. However, when it comes to APIs that are going to be consumed by various clients (MVC, React.js, etc), instead of having the same logic written on different tech stacks, I believe it would be a better practice to have the central API generate it for you.

      Yes, I know that this is quite a lot of data that the API is generating. But this article was for demonstration purposes only. Ultimately it depends on your project requirements. Meanwhile, I have worked with quite a lot of public APIs that return the Links as well, which I found to be very convenient. The one issue with having the front end do the pagination link is, it would still be possible to exploit the data limit factor. It gets perfectly secured in this approach. What do you think about it?

      1. If pagination component allows user to change page size or select page other than next/previous front end must calculate links.
        PageNumber, pageSize and totalRecords are my set

  2. Nice article. However, you should consider the case where the APIs are behind an API gateway. In this case, the base URI shouldn’t be ever the base URI of the API itself (the base URI is behind the gateway and therefore unknown or hidden to the API’s clients).

    1. Agree with you, maybe can just remove/separate the base URI from the result. Anyway, thanks for the great article.

  3. Dear Mukesh,
    I am sorry for disturbing you but I am new to web technology and API’s since all my experience on desktop applications.
    i am trying to call the API from Blazor Server service to get all rows with this excellent Technic as :
    async Task IOwnerService.GetCityForTest(string uri)
    {
    try
    {

    IEnumerable Founded;
    Founded = await _httpClient.GetFromJsonAsync<IEnumerable>(“Products?pagenumber=1&pagesize=10″);
    return Founded;
    }
    catch (Exception ex)
    {

    errorString = $”There was an error: {ex.Message}”;
    }
    return null;
    }
    but when the data returns from the api controller – which working good – i got the following error :
    “The JSON value could not be converted to System.Collections.Generic.IEnumerable`1[System.Object]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.”
    Could you please help me to solve this, your help will be highly appreciated.
    Thank you so much for your effort.
    Best Regards.

    1. Hi.

      At the part where you are calling the httpClient, shouldn’t it be IEnumerable of Product instead of just IEnumumerable? Because I guess you need to specify the type of List of object for the JSON to parse through it.

      Also you defined your function as a TASK, but i guess it should be something like Task of Product or at least Task of IActionResult, as you are returning something (either a list or null)

      Just check these 2 points maybe.

      PS, I am not able to use ‘ / <> / ‘ for security reasons, So i used things like ‘Task of ‘ and so on.. you get the point

      Thanks

      1. Sorry Mukesh, but i did not succeed to read the data from the controller.
        If you could please give me an example to follow of how to call this controller from Blazor server service :

        public async Task GetAll([FromQuery] PaginationFilter filter)
        {
        var route = Request.Path.Value;
        var validFilter = new PaginationFilter(filter.PageNumber, filter.PageSize);
        var pagedData = await _context.PmsCities
        .Skip((validFilter.PageNumber – 1) * validFilter.PageSize)
        .Take(validFilter.PageSize)
        .ToListAsync();
        var totalRecords = await _context.PmsCities.CountAsync();
        var pagedReponse = PaginationHelper.CreatePagedReponse(pagedData, validFilter, totalRecords, uriService, route);
        return Ok(pagedReponse);
        }

        and receive the pagedRespnse result from it using httpclient, i will be very thankful.
        Thank you very much indeed.
        Best Regards

          1. Dear Mukesh,
            First of all i apologize for bothering you.
            I am following the example of this article (with many thanks to you) step by step and every thing works fine when i run it form the postman or call the API direct form the browser.
            but my problem is when i call this API action :
            public async Task GetAll([FromQuery] PaginationFilter filter)
            {
            var route = Request.Path.Value;
            var validFilter = new PaginationFilter(filter.PageNumber, filter.PageSize);
            var pagedData = await _context.PmsCities
            .Skip((validFilter.PageNumber – 1) * validFilter.PageSize)
            .Take(validFilter.PageSize)
            .ToListAsync();
            var totalRecords = await _context.PmsCities.CountAsync();
            var pagedReponse = PaginationHelper.CreatePagedReponse(pagedData, validFilter, totalRecords, uriService, route);
            return Ok(pagedReponse);
            }
            the only deference that i am using PmsCity model instead of the customer model you are using.
            I also followed the article you referred me to and use the same Technic as the following
            1- PmsCity[] developers { get; set; }
            2- in OnInitializedAsync() i used
            developers = await client.GetFromJsonAsync(“http://localhost:5000/API/cities?pagenumber=1&pagesize=10”);

            Put unfortunately it gives me the following error at run time :
            “The JSON value could not be converted to DataTowerPMS.Entities.Models.StModels.PmsCity[]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.”
            I think i am missing something in calling function.

            I also tried the following syntax :
            var response1 = await _httpClient.GetAsync(“Cities?pagenumber=1&pagesize=10”);
            var content1 = await response1.Content.ReadAsStringAsync();

            var ct1 = (IEnumerable)JsonConvert.DeserializeObject<Response<IEnumerable>>(content1);

            the response1 work good but when i Deserialized it it gives me null Data.
            I know i am asking to much, but i am really stuck on this and can not go father in my project due to this problem.
            Thank you so much for your patient, and your help will be highly appreciated.
            Best Regards.

  4. I really don’t know how to thank you.
    finally it works for me.
    So many thanks for your help.
    I wish you the best, with best regards.

  5. Your code is very clean and elegant, thanks a lot …. would you please make another article explaining the sorting and searching using a generic way like you did with pagination.

  6. Hi I like your detailed style. I have manged to get it to work with Comos Db SQL-API and the CosmosClient API, although it would be nice to have all the goodness from the EF core API.

    Do you have a suggestion about how implement pagination using the ContinuationToken from the CosmosClient API ?

    I was thinking that in order to have the previousPage-option , I’d have to somehow keep track of all the pages a client has visited for it to work correct.

  7. Hi I am having trouble with Generating Pagination URLs section of your tutorial when I add the code (public void ConfigureServices(IServiceCollection services) to Startup.cs) I am getting

    Error CS1061 ‘IServiceCollection’ does not contain a definition for ‘AddDbContext’ and no accessible extension method ‘AddDbContext’ accepting a first argument of type ‘IServiceCollection’ could be found (are you missing a using directive or an assembly reference?) mAPI Startup.cs 41

    Also in I am getting the following error for public interface IUriService

    Severity Code Description Project File Line Suppression State
    Error CS8703 The modifier ‘public’ is not valid for this item in C# 7.3. Please use language version ‘8.0’ or greater. mAPI F:\SFA Others\source\repos\API\mAPI 26_10_2020\mAPI\Services\Class1.cs 10 Active

    Can you please help?

  8. Hello Mukesh,

    I was following your amazing tutorial Advanced Pagination in ASP.NET Core WebApi but I got stuck in Generating Pagination URLs.

    When I try to add your code as a interface I get the following error.

    Severity Code Description Project File Line Suppression State
    Error CS8703 The modifier ‘public’ is not valid for this item in C# 7.3. Please use language version ‘8.0’ or greater. mAPI F:\SFA Others\source\repos\API\mAPI 26_10_2020\mAPI\Services\Class1.cs 10 Active

    Which I tried updating my .net api to 8.0 I couldn’t find the LangVersion tag in my solution file.

    Also when I tried adding your code (public void ConfigureServices(IServiceCollection services)) from Configuring the Service to get the Base URL. I get the following errors

    Severity Code Description Project File Line Suppression State
    Error CS0246 The type or namespace name ‘PaginationFilter’ could not be found (are you missing a using directive or an assembly reference?) mAPI F:\SFA Others\source\repos\API\mAPI 26_10_2020\mAPI\Services\Class1.cs 10 Active
    Error CS1061 ‘IServiceCollection’ does not contain a definition for ‘AddDbContext’ and no accessible extension method ‘AddDbContext’ accepting a first argument of type ‘IServiceCollection’ could be found (are you missing a using directive or an assembly reference?) mAPI F:\SFA Others\source\repos\API\mAPI 26_10_2020\mAPI\Startup.cs 41 Active
    Error CS0246 The type or namespace name ‘IHttpContextAccessor’ could not be found (are you missing a using directive or an assembly reference?) mAPI F:\SFA Others\source\repos\API\mAPI 26_10_2020\mAPI\Startup.cs 48 Active
    Error CS0246 The type or namespace name ‘UriService’ could not be found (are you missing a using directive or an assembly reference?) mAPI F:\SFA Others\source\repos\API\mAPI 26_10_2020\mAPI\Startup.cs 51 Active
    Error CS1061 ‘IServiceCollection’ does not contain a definition for ‘AddControllers’ and no accessible extension method ‘AddControllers’ accepting a first argument of type ‘IServiceCollection’ could be found (are you missing a using directive or an assembly reference?) mAPI F:\SFA Others\source\repos\API\mAPI 26_10_2020\mAPI\Startup.cs 53 Active

    I am new to web API coding and this is really defeating me

  9. Nice Article,

    I am using Dapper and Stored Procedures in my api, can you please help me to implement this functionality in this situation?

  10. Thank You . Great Article. Can you please explain to me how to use this API with JQuery Data Table and server side processing ?

  11. Hi Mukesh,

    Awesome work, I easily implemented on my .net 5 web api project and works fantastic.

    Thank you very much for this awesome work, I like it because I can listen to music while learning.

  12. Hi Mukesh,
    Thank you for the great article. I know this is an older article at this point but do you have a technique for implementing this in Onion Architecture? In my attempt I have an interface for Wrapper/IResponse in my application layer, which takes a type parameter TEntity. The implementation is in my infrastructure layer. The problem comes up when trying to deserialize (my infrastructure is an external REST API) — you cannot deserialize to an interface, but I also cannot implement my repository interface using the implementation of IResponse, because the repository interface is also part of my app layer, and thus has no access to the infrastructure implementation of Response.

    How would you approach?

  13. Thanks for the nice article. I found one issue in page: https://github.com/iammukeshm/Pagination.WebApi/blob/master/Pagination.WebApi/Helpers/PaginationHelper.cs
    The below code will show the firstPage always as 1. That should not be the case when I request for second or other page. So instead of hard coding as 1, we can set it as validFilter.PageNumber.

    respose.FirstPage = uriService.GetPageUri(new PaginationFilter(1, validFilter.PageSize), route);

  14. Thank you for this article, I like the way you present and deliver the information. Straightforward and easy to follow.

  15. Hi. Some good ideas here. A few small bits of feedback.

    1. You should perhaps look to see if there is an x-forwarded-for header incase you are behind a load balancer.
    2. You really should not use offset paging. Rather use keyset pagination.

  16. Hi Mukesh,

    thank you for your post. It was beautiful to see that you are concerned about reusability and clean code. I learned very useful tips.

    One question though: in section “Pagination Filter” what do you mean by “Ps, you would probably want to use a mapper here. But this current approach is fine for our guide.”?

    Do you mean using a tool like AutoMapper? I am using it to map application DTO’s and domain models but I don’t see why we would need it here just to get the query string parameters. AFAIK, ASP.NET Core performs automatic model binding for [FromQuery] (and [FromBody] and others). So in this case we can use the parameter paginationFilter straightforward. No need to map it to anything. Or yes? That’s my question.

  17. This is very helpful tutorial, can you please create one more step for this tutorial that, how can use the api in separate mvc project with datatable? Thanks in advance.