Generic Method Pagination for All API Controllers,

2019-08-27 02:50发布

I am trying to take this API and generalize Pagination, so it can be used for Any of my APIs and Models. How would I conduct this? I placed existing code base below, and want to have applicable to all apis and their getmethods. Researching to modify this function. If Microsoft has built into functionality to support this, I am open to Any other strategies.

From Resource (Existing Code only used can be used for 1 Class API):

https://dejanstojanovic.net/aspnet/2019/january/filtering-and-paging-in-aspnet-core-web-api/

Background:

We prepared many Get APIs, and now want pagination support for all 200+ apis. (I came into the project later) Without manually update/recoding everything, is there a automatic method to apply routing/pagination to all APIs for our end point customers using maybe resource link above or other ideas? Seeking way to filter all APIs.

public ActionResult<PagedCollectionResponse<Person>> Get([FromQuery] SampleFilterModel filter)  


/api/Persons?Page=2&Limit=3

/api/Inventory?Page=2&Limit=3

/api/Product?Page=2&Limit=3
/api/Product/GetAll/Page=2&Limit=4
/api/Product/GetByProductCategory/7/Page=2&Limit=4
/api/Product/GetByVendorId/2/Page=2&Limit=4
/api/Product/GetByProductLocation/US/CA/Page=2&Limit=4

Code:

namespace Sample.Web.Api.Models  
{  
    public abstract class FilterModelBase:ICloneable  
    {  
        public int Page { get; set; }        
        public int Limit { get; set; }  
        public FilterModelBase()  
        {  
            this.Page = 1;  
            this.Limit = 100;  
        }  
        public abstract object Clone();  
    }  
}  

public class SampleFilterModel:FilterModelBase  
{  
    public string Term { get; set; }  
    public SampleFilterModel():base()  
    {  
        this.Limit = 3;  
    }  
    public override object Clone()  
    {  
        var jsonString = JsonConvert.SerializeObject(this);  
        return JsonConvert.DeserializeObject(jsonString,this.GetType());  
    }  
}  

namespace Sample.Web.Api.Models  
{  
    public class PagedCollectionResponse<T> where T:class  
    {  
        public IEnumerable<T> Items { get; set; }  
        public Uri NextPage { get; set; }  
        public Uri PreviousPage { get; set; }  
    }  
}  

namespace Sample.Web.Api.Models  
{  
    public class Person  
    {  
        public String Name { get; set; }  
        public DateTime DOB { get; set; }  
        public String Email { get; set; }  
    }  
}  

namespace Sample.Web.Api.Controllers  
{  
    [Route("api/[controller]")]  
    [ApiController]  
    public class PersonsController : ControllerBase  
    {  

        IEnumerable<Person> persons = new List<Person>() {  
            new Person() { Name = "Nancy Davolio", DOB = DateTime.Parse("1948-12-08"), Email = "nancy.davolio@test.com" },  
            new Person() { Name = "Andrew Fuller", DOB = DateTime.Parse("1952-02-19"), Email = "andrew.fuller@test.com" },  
            new Person() { Name = "Janet Leverling", DOB = DateTime.Parse("1963-08-30"), Email = "janet.leverling@test.com" },  
            new Person() { Name = "Margaret Peacock", DOB = DateTime.Parse("1937-09-19"), Email = "margaret.peacock@test.com" },  
            new Person() { Name = "Steven Buchanan", DOB = DateTime.Parse("1955-03-04"), Email = "steven.buchanan@test.com" },  
            new Person() { Name = "Michael Suyama", DOB = DateTime.Parse("1963-07-02"), Email = "michael.suyama@test.com" },  
            new Person() { Name = "Robert King", DOB = DateTime.Parse("1960-05-29"), Email = "robert.king@test.com" },  
            new Person() { Name = "Laura Callahan", DOB = DateTime.Parse("1958-01-09"), Email = "laura.callahan@test.com" },  
            new Person() { Name = "Anne Dodsworth", DOB = DateTime.Parse("1966-01-27"), Email = "anne.dodsworth@test.com" }  
            };  

        // GET api/values  
        [HttpGet]  
        public ActionResult<PagedCollectionResponse<Person>> Get([FromQuery] SampleFilterModel filter)  
        {  

            //Filtering logic  
            Func<SampleFilterModel, IEnumerable<Person>> filterData = (filterModel) =>  
            {  
                return persons.Where(p => p.Name.StartsWith(filterModel.Term ?? String.Empty, StringComparison.InvariantCultureIgnoreCase))  
                .Skip((filterModel.Page-1) * filter.Limit)  
                .Take(filterModel.Limit);  
            };  

            //Get the data for the current page  
            var result = new PagedCollectionResponse<Person>();  
            result.Items = filterData(filter);  

            //Get next page URL string  
            SampleFilterModel nextFilter = filter.Clone() as SampleFilterModel;  
            nextFilter.Page += 1;  
            String nextUrl = filterData(nextFilter).Count() <= 0 ? null : this.Url.Action("Get", null, nextFilter, Request.Scheme);  

            //Get previous page URL string  
            SampleFilterModel previousFilter = filter.Clone() as SampleFilterModel;  
            previousFilter.Page -= 1;  
            String previousUrl = previousFilter.Page <= 0 ? null : this.Url.Action("Get", null, previousFilter, Request.Scheme);  

            result.NextPage = !String.IsNullOrWhiteSpace(nextUrl) ? new Uri(nextUrl) : null;  
            result.PreviousPage = !String.IsNullOrWhiteSpace(previousUrl) ? new Uri(previousUrl) : null;  

            return result;  

        }  
    }  
}  

Note:

Need to Extend it to the API level as per resource. We know it can be done in business repository method, but want to generalize for all Controller APIs like author from above .

1条回答
\"骚年 ilove
2楼-- · 2019-08-27 03:05

In response to:

If Microsoft has built into functionality to support this, I am open to Any other strategies.

I would look into implementing OData v4 if that's an option. You can then standardize pagination across all your APIs using $top and $skip query parameters.

So roughly you would want to do something like this:

Install the Microsoft.AspNetCore.OData Nuget package on the project. More documentation about the features can be found in the OData WebApi changelog.

Then modify the code as shown below to get started with a basic implementation of OData.

Startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        // Configure the use of OData
        services.AddOData();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();

        // Configure MVC to support OData routes 
        app.UseMvc(config =>
        {
            config.EnableDependencyInjection();
            config.Select().Expand().Filter().OrderBy().MaxTop(null).Count();
        });
    }
}

Models/Person.cs

// Set the MaxTop value accordingly
[Page(MaxTop = 100)]
public class Person
{
    public string Name { get; set; }
    public string Email { get; set; }
    public DateTime DOB { get; set; }
}

Controllers/PersonsController.cs

[Route("api/[controller]")]
[ApiController]
public class PersonsController : ControllerBase
{
    private readonly IEnumerable<Person> persons = new List<Person>()
    {
        new Person() { Name = "Nancy Davolio", DOB = DateTime.Parse("1948-12-08"), Email = "nancy.davolio@test.com" },
        new Person() { Name = "Andrew Fuller", DOB = DateTime.Parse("1952-02-19"), Email = "andrew.fuller@test.com" },
        new Person() { Name = "Janet Leverling", DOB = DateTime.Parse("1963-08-30"), Email = "janet.leverling@test.com" },
        new Person() { Name = "Margaret Peacock", DOB = DateTime.Parse("1937-09-19"), Email = "margaret.peacock@test.com" },
        new Person() { Name = "Steven Buchanan", DOB = DateTime.Parse("1955-03-04"), Email = "steven.buchanan@test.com" },
        new Person() { Name = "Michael Suyama", DOB = DateTime.Parse("1963-07-02"), Email = "michael.suyama@test.com" },
        new Person() { Name = "Robert King", DOB = DateTime.Parse("1960-05-29"), Email = "robert.king@test.com" },
        new Person() { Name = "Laura Callahan", DOB = DateTime.Parse("1958-01-09"), Email = "laura.callahan@test.com" },
        new Person() { Name = "Anne Dodsworth", DOB = DateTime.Parse("1966-01-27"), Email = "anne.dodsworth@test.com" }
    };

    // GET: api/Persons
    [HttpGet]
    [EnableQuery]
    public IEnumerable<Person> Get() => persons;
}

Then in the browser you execute queries in accordance with the OData spec. For example:

GET /api/Persons?$skip=1&$top=3
查看更多
登录 后发表回答