高性能分页REST API查询生成器

2023-05-16 08:28:17

目录

介绍

先决条件

抽象

客户请求

通过Postman Client使用REST API

REST API服务器端

在解决方案错误的情况下


介绍

满足在海量数据源上通过REST API进行记录分页的需求。因此,我们使用REST API提出了这个解决方案,希望它也能对您的工作有所帮助。其目的是执行特定和多列搜索、排序、动态页面大小调整、具有优雅的代码结构、最少的编码工作,以及最重要的性能。

先决条件

为了学习本文,您需要对MVC框架有一定的了解。如果您认为自己有足够的专业知识,那么您就可以轻松地进一步阅读本文了。

抽象

该解决方案使用Swagger作为文档和LINQKit包。LINQKit用于构建灵活且高性能的查询。

客户请求

我们使用Postman查询我们的REST APIjson请求结构如下:

{
  "CurrentPage": 1,
  "PageSize": 1,
  "SearchText": "",
  "SortColumn": "Id",
  "SortDirection": "Desc",
  "HasPagination": true,
  "Columns": [
  ]
}

属性

描述

CurrentPage

请求的页码应该在这个属性中定义,例如,要有第3页的数组响应,那么3应该被分配给它。

PageSize

它定义了arraylist请求的返回大小或页面大小的大小。

SearchText

要搜索所有文本列,应填充此属性,例如,在这种情况下,abbot将搜索所有文本列ContactPersonPhoneCityRegionCountry

SortColumn

我们需要用哪一列来排序我们的数据,例如按联系人排序,传递的值应该是 ContactPerson

SortDirection

对于升序排序,Asc 应该被传递,对于降序,Desc

HasPagination

要将这个查询产生的每一个数据返回为json,它需要是false否则true 应该被传递。

Columns[]

它是用于特定搜索的列的json数组列表,它包含Column对象数组。

Column

这个json对象有两个属性,ColumnNameColumnSearchText 这些搜索提供了特定的列搜索功能,例如:

示例 1

{
     "ColumnName":"Balance",
     "ColumnSearchText":"<=,50"
}

它将返回余额小于等于50的所有客户。

示例 2

{
    "ColumnName":"Country",
    "ColumnSearchText":"Pakistan"
}

它将 仅返回来自国家/地区Pakistan的所有客户:

示例 3

您也可以进行多个场景搜索,例如:

"Columns":[
   {
       "ColumnName":"Country",
       "ColumnSearchText":"Pakistan"
   }
   ,
   { 
        "ColumnName":"Balance",    
        "ColumnSearchText":"<=,50" 
   } 
]

上述json查询应返回customer余额小于等于50 客户所在国家/地区仅为Pakistan

通过Postman Client使用REST API

为了使用这个REST API,我们使用了postman。你可以从他们的网站下载。打开客户端后,将请求类型设置为Post body选项卡中,选择下面选择的raw 并将请求内容类型设置为位于combobox最右边的JSON

上述API请求返回余额大于5000000的客户,并为第1页分页

快照显示了来自国家/地区oman的所有客户。

上面的快照按客户ID编号按降序显示了记录。

REST API服务器端

在服务器端,我们有LINQKit来实现我们的愿望。在我们上面的例子中,它的控制器操作方法之一设置对象PaginationRequest,该对象将由客户端作为定义json对象传递。

[HttpPost]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(CustomerPaginationResponseExample))]
[SwaggerRequestExample(typeof(PaginationRequest<customerpaginationgridcolumns>),
typeof(CustomerPaginationRequestExample), jsonConverter: typeof(StringEnumConverter))]
[ResponseType(typeof(PaginationResponse<customerpaginationmodel>))]
[HelperMethods.DeflateCompression]
[ValidateModelState]
[CheckModelForNull]
[SwaggerConsumes("application/json")]
[SwaggerProduces("application/json")]
[SwaggerResponse(HttpStatusCode.NotFound,
"No customer found", typeof(GenericResponseModel))]
[Route("")]
public async Task<system.web.http.ihttpactionresult>
       Get(PaginationRequest<customerpaginationgridcolumns> request)
{
    BusinessLayer.Entity.Customer obj = new BusinessLayer.Entity.Customer(this);
    PaginationResponse<customerpaginationmodel> response = obj.Get(request).Result;

    if (response.Items == null)
    {
        return APIResponse(HttpStatusCode.InternalServerError,
                           $"Error: {obj.errorMessage}");
    }
    else
    if (response.Items.Count() == 0)
    {
        return APIResponse(HttpStatusCode.NotFound, $"No customer found");
    }
    return Ok(response);
}

深入研究我们的请求结构化的详细业务层方法如下。它使用LINQKit和实体框架作为我们的ORM动态地组成查询。它将所有列解析为与此业务实体关联的定义,并动态组装查询并同时实现分页。

public async Task<paginationresponse<customerpaginationmodel>>
Get(PaginationRequest<common.enum.customerpaginationgridcolumns> paginationRequest)
    {
        try
        {
            BusinessEntity.CustomerDBEntities obj =
                           new BusinessEntity.CustomerDBEntities();

            records = (from cus in obj.customers.AsNoTracking()
                       // join count in obj.countries on
                       // cus.countryId equals count.countryId
                       select new CustomerPaginationModel
                       {
                           Id = cus.customerID,
                           ContactPerson = cus.contactPerson,
                           Phone = cus.phone,
                           Fax = cus.phone,
                           City = cus.city,
                           Region = cus.region,
                           Country = cus.countryName,
                           CountryId = cus.countryId,
                           Balance = cus.balance
                       }).AsQueryable();

            if (paginationRequest.SortColumn != CustomerPaginationGridColumns.None)
            {
                InitSorting(paginationRequest);
            }
            else
            {
                paginationRequest.SortColumn = CustomerPaginationGridColumns.Id;
                InitSorting(paginationRequest);
            }

            genericSearchText = paginationRequest.SearchText == null ?
                                null : paginationRequest.SearchText.Trim(); // set generic
                                                                            // search value

            ColumnParameter<common.enum.customerpaginationgridcolumns> column =
                                 new ColumnParameter<customerpaginationgridcolumns>() { };

            // Iterate through filter grid column to construct query predicate
            // foreach (ColumnParameter<common.enum.customerpaginationgridcolumns>
            // column in paginationRequest.Columns)
            foreach (CustomerPaginationGridColumns columnParse in Enum.GetValues
                    (typeof(CustomerPaginationGridColumns)))
            {

                if (!string.IsNullOrEmpty(genericSearchText))
                {
                    // these is no specific column search
                    if (paginationRequest.Columns.Where
                       (x => x.ColumnName == columnParse).Count() == 0)
                    {
                        column = new ColumnParameter<customerpaginationgridcolumns>()
                                  { ColumnName = columnParse, ColumnSearchText = "" };
                    }
                    else
                    {
                        column = paginationRequest.Columns.Where
                                 (x => x.ColumnName == columnParse).FirstOrDefault();
                    }
                }
                else
                {
                    column = paginationRequest.Columns.Where
                             (x => x.ColumnName == columnParse).FirstOrDefault();
                }

                if (column == null)
                {
                    continue;
                }

                searchColumnText =
                (column.ColumnSearchText ?? "").Trim();      // set current column
                                                             // search value

                switch (column.ColumnName)
                {
                    case Common.Enum.CustomerPaginationGridColumns.Balance:
                        EvaluateNumericComparisonFilter(paginationRequest, column,
                           searchColumnText,
                           "Balance",
                           x => x.Balance
                       );
                        break;
                    case Common.Enum.CustomerPaginationGridColumns.City:
                        EvaluateFilter(paginationRequest, column,
                            x => x.City.StartsWith(searchColumnText),
                            x => x.City.StartsWith(genericSearchText),
                            x => x.City
                            );
                        break;
                    case Common.Enum.CustomerPaginationGridColumns.ContactPerson:
                        EvaluateFilter(paginationRequest, column,
                            x => x.ContactPerson.StartsWith(searchColumnText),
                            x => x.ContactPerson.StartsWith(genericSearchText),
                            x => x.ContactPerson
                            );
                        break;
                    case Common.Enum.CustomerPaginationGridColumns.Country:
                        EvaluateFilter(paginationRequest, column,
                            x => x.Country.StartsWith(searchColumnText),
                            x => x.Country.StartsWith(genericSearchText),
                            x => x.Country
                            );
                        break;
                    case Common.Enum.CustomerPaginationGridColumns.CountryId:
                        if (!IsNumber(searchColumnText))
                        {
                            continue;
                        }

                        string type = searchColumnText;

                        EvaluateFilter(paginationRequest, column,
                            x => x.CountryId == type,
                            null,
                            x => x.CountryId
                            );
                        break;
                    case Common.Enum.CustomerPaginationGridColumns.Fax:
                        EvaluateFilter(paginationRequest, column,
                            x => x.Fax.StartsWith(searchColumnText),
                            x => x.Fax.StartsWith(genericSearchText),
                            x => x.Fax
                            );
                        break;
                    case Common.Enum.CustomerPaginationGridColumns.Phone:
                        EvaluateFilter(paginationRequest, column,
                            x => x.Phone.StartsWith(searchColumnText),
                            x => x.Phone.StartsWith(genericSearchText),
                            x => x.Phone
                            );
                        break;
                    case Common.Enum.CustomerPaginationGridColumns.Region:
                        EvaluateFilter(paginationRequest, column,
                            x => x.Region.StartsWith(searchColumnText),
                            x => x.Region.StartsWith(genericSearchText),
                            x => x.Region
                            );
                        break;
                }
            }

            PaginationResponse<customerpaginationmodel> response =
                                 new PaginationResponse<customerpaginationmodel>();

            IQueryable<customerpaginationmodel> countQuery = records;
            response.Items = ForgeGridData(paginationRequest, x => x.ContactPerson).Result;
            response.RecordsTotal = totalRows;

            // Generating data
            return response;
        }
        catch (Exception exp)
        {
            CompileExceptionHandleMessage(exp);
            return new PaginationResponse<customerpaginationmodel>() { Items = null };
        }
        finally
        {
            records = null;
        }
    }

在解决方案错误的情况下

如果解决方案源代码拒绝编译,则重建解决方案,之后,如果您有运行时错误,请在主项目的包管理控制台中运行以下命令,然后就可以了。

Update-Package Microsoft.CodeDom.Providers.DotNetCompilerPlatform -r

https://www.codeproject.com/Articles/5297265/High-Performance-Pagination-REST-API-Query-Builder

  • 作者:寒冰屋
  • 原文链接:https://blog.csdn.net/mzl87/article/details/118463390
    更新时间:2023-05-16 08:28:17