【问题标题】:Generic Method Pagination for All API Controllers, Filter All APIs所有 API 控制器的通用方法分页,过滤所有 API
【发布时间】:2019-08-22 09:36:14
【问题描述】:

我正在尝试使用这个 API 并概括 Pagination,因此它可以用于我的任何 API 和模型。我将如何进行?我将现有的代码库放在下面,并希望适用于所有 api 及其 getmethods。正在研究修改此功能。如果 Microsoft 已内置功能来支持这一点,我愿意接受任何其他策略。

来自资源(现有代码仅可用于 1 类 API):

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

背景:

我们准备了许多 Get API,现在希望为所有 200 多个 API 提供分页支持。 (我后来进入该项目)无需手动更新/重新编码所有内容,是否有一种自动方法可以使用上述资源链接或其他想法为我们的端点客户应用路由/分页到所有 API?寻求过滤所有 API 的方法。

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

代码:

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;  

        }  
    }  
}  

注意:

需要根据资源将其扩展到 API 级别。我们知道它可以在业务存储库方法中完成,但希望对所有控制器 API 进行泛化,如上面的作者。

【问题讨论】:

  • @mjwills 问题被删除,我只是进行了更多分析

标签: c# asp.net-core .net-core pagination asp.net-core-mvc


【解决方案1】:

回应:

如果 Microsoft 已内置功能来支持这一点,我愿意接受任何其他策略。

如果可以的话,我会考虑实施OData v4。然后,您可以使用 $top$skip 查询参数跨所有 API 标准化分页。

所以大致上你会想做这样的事情:

在项目上安装Microsoft.AspNetCore.OData Nuget 包。有关这些功能的更多文档可以在OData WebApi changelog 中找到。

然后如下修改代码,开始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;
}

然后在浏览器中根据 OData 规范执行查询。例如:

GET /api/Persons?$skip=1&$top=3

【讨论】:

    猜你喜欢
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 2019-08-11
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-27
    相关资源
    最近更新 更多