分页

简介: 1. program中注册`builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();`2. 控制器中增加依赖

分页

分页后需要将分页数据返回到head文件中,如实现过程

  1. program中注册builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
  2. 控制器中增加依赖

privatereadonlyIUrlHelper_urlHelper;

publicTouristRoutesController( IActionContextAccessoractionContextAccessor)

{

   _urlHelper=urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);

}

  1. 生成前一页和后一页的方法

publicenumResourceUriType

{

   PreviousPage,

   NextPage

}

 

privatestring?GenerateTouristRouteResourceURL(TouristRouteResourcecParamatersparamaters, PaginationResourceParamatersparamaters2, ResourceUriTypetype)

{

   returntypeswitch

   {

       ResourceUriType.PreviousPage=>_urlHelper.Link("GetTouristRoutes",

       new

       {

           keyword=paramaters.Keyword,

           rating=paramaters.Rating,

           pageNumber=paramaters2.PageNumber-1,

           pageSize=paramaters2.PageSize

       }),

       ResourceUriType.NextPage=>_urlHelper.Link("GetTouristRoutes",

       new

       {

           keyword=paramaters.Keyword,

           rating=paramaters.Rating,

           pageNumber=paramaters2.PageNumber+1,

           pageSize=paramaters2.PageSize

       }),

       _=>_urlHelper.Link("GetTouristRoutes",

       new

       {

           keyword=paramaters.Keyword,

           rating=paramaters.Rating,

           pageNumber=paramaters2.PageNumber,

           pageSize=paramaters2.PageSize

       })

   };

}

  1. 控制器中的查询操作方法

[HttpGet(Name="GetTouristRoutes")]

[HttpHead]

[Authorize(AuthenticationSchemes="Bearer")]//identity多角色验证时必须指定

publicasyncTask<IActionResult>GetTouristRoutes([FromQuery] TouristRouteResourcecParamatersparamaters, [FromQuery] PaginationResourceParamatersparamaters1)

{

 

   varroutes=await_tourisTouteRepository.GetTourisRoutesAsync(paramaters.Keyword, paramaters.RatingOperator,paramaters.RatingValue, paramaters1.PageSize,paramaters1.PageNumber);

   if (routes==null||routes.Count() <=0)

   {

       returnNotFound("没有旅游路线");

   }

   vartouristRouteDto=_mapper.Map<IEnumerable<TouristRouteDto>>(routes);

   //生成前页面链接

   varpreviousPageLink=routes.HasPrevious?GenerateTouristRouteResourceURL(paramaters, paramaters1, ResourceUriType.PreviousPage) : null;

   //生成前页面链接

   varnextPageLink=routes.HasNext?GenerateTouristRouteResourceURL(paramaters, paramaters1, ResourceUriType.NextPage) : null;

 

   //增加头部信息

   varpaginationMetadata=new

   {

       previousPageLink,

       nextPageLink,

       totalCount=routes.TotalCount,

       pageSize=routes.PageSize,

       currentPage=routes.CurrentPage,

       totalPages=routes.TotalPages

   };

   Response.Headers.Add("x-pagination",Newtonsoft.Json.JsonConvert.SerializeObject(paginationMetadata));

   returnOk(touristRouteDto);

}

排序

思路为解析地址栏中用户想以哪个字段进行那种排序(升降),核心就是要引用using System.Linq.Dynamic.Core;可以通过字符串,而不是对象使用result.OrderBy(t => t.OriginalPrice);

  1. 映射值类

publicclassPropertyMappingValue

{

   //将要被映射的属性列表

   publicIEnumerable<string>DestinationProperties { get; privateset; }

   publicPropertyMappingValue(IEnumerable<string>destinationProperties)

   {

       DestinationProperties=destinationProperties;

   }

}

  1. 映射类

//为了方便注入,先定义接口

publicinterfaceIPropertyMapping

{

}

publicclassPropertyMapping<TSource,TDestination>:IPropertyMapping

{

   //属性所匹配的字符串字典

   publicDictionary<string,PropertyMappingValue>_mappingDictionary { set; get; }

   publicPropertyMapping(Dictionary<string, PropertyMappingValue>mappingDictionary)

   {

       _mappingDictionary=mappingDictionary;

   }

}

  1. 映射服务类

//为了方便注入,先定义接口

publicinterfaceIPropertyMappingService

{

   Dictionary<string, PropertyMappingValue>GetPropertyMapping<TSource, TDestination>();

   boolIsMappingExists<TSource, TDestingation>(stringfields);

}

publicclassPropertyMappingService : IPropertyMappingService

{

   //设定字符串与PropertyMappingValue的对应关系

   privateDictionary<string, PropertyMappingValue>_touristRoutePropertyMapping=

      newDictionary<string, PropertyMappingValue>(StringComparer.OrdinalIgnoreCase)

      {

          { "Id", newPropertyMappingValue(newList<string>(){ "Id" }) },

          { "Title", newPropertyMappingValue(newList<string>(){ "Title" })},

          { "Rating", newPropertyMappingValue(newList<string>(){ "Rating" })},

          { "OriginalPrice", newPropertyMappingValue(newList<string>(){ "OriginalPrice" })},

      };

    //定义一个映射对象,映射对象里面包含字符串和PropertyMappingValue对应的字典

   privateIList<IPropertyMapping>_propertyMappings=newList<IPropertyMapping>();

 

   publicPropertyMappingService()

   {   //将新建的对应列表加入到私有映射对象

       _propertyMappings.Add(

           newPropertyMapping<TouristRouteDto, TouristRoute>(

               _touristRoutePropertyMapping));

   }

 

   publicDictionary<string, PropertyMappingValue>

       GetPropertyMapping<TSource, TDestination>()

   {

       // 获得匹配的映射对象

       varmatchingMapping=

           _propertyMappings.OfType<PropertyMapping<TSource, TDestination>>();

 

       if (matchingMapping.Count() ==1)

       {

           returnmatchingMapping.First()._mappingDictionary;

       }

 

       thrownewException(

           $"Cannot find exact property mapping instance for <{typeof(TSource)},{typeof(TDestination)}");

 

   }

 

   publicboolIsMappingExists<TSource, TDestination>(stringfields)

   {

       varpropertyMapping=GetPropertyMapping<TSource, TDestination>();

 

       if (string.IsNullOrWhiteSpace(fields))

       {

           returntrue;

       }

 

       //逗号来分隔字段字符串

       varfieldsAfterSplit=fields.Split(",");

 

       foreach(varfieldinfieldsAfterSplit)

       {

           // 去掉空格

           vartrimmedField=field.Trim();

           // 获得属性名称字符串

           varindexOfFirstSpace=trimmedField.IndexOf(" ");

           varpropertyName=indexOfFirstSpace==-1?

               trimmedField : trimmedField.Remove(indexOfFirstSpace);

 

           if (!propertyMapping.ContainsKey(propertyName))

           {

               returnfalse;

           }

       }

       returntrue;

   }

}

 

  1. nuget安装System.Linq.Dynamic.Core。IQueryable扩展类

publicstaticclassIQueryableExtensions

{

   publicstaticIQueryable<T>ApplySort<T>(

       thisIQueryable<T>source,

       stringorderBy,

       Dictionary<string, PropertyMappingValue>mappingDictionary

   )

   {

       if (source==null)

       {

           thrownewArgumentNullException("source");

       }

 

       if (mappingDictionary==null)

       {

           thrownewArgumentNullException("mappingDictionary");

       }

 

       if (string.IsNullOrWhiteSpace(orderBy))

       {

           returnsource;

       }

 

       varorderByString=string.Empty;

 

       varorderByAfterSplit=orderBy.Split(',');

 

       foreach(varorderinorderByAfterSplit)

       {

           vartrimmedOrder=order.Trim();

 

           // 通过字符串“ desc”来判断升序还是降序

           varorderDescending=trimmedOrder.EndsWith(" desc");

 

           // 删除升序或降序字符串 " asc" or " desc"来获得属性的名称

           varindexOfFirstSpace=trimmedOrder.IndexOf(" ");

           varpropertyName=indexOfFirstSpace==-1

               ?trimmedOrder

               : trimmedOrder.Remove(indexOfFirstSpace);

 

           if (!mappingDictionary.ContainsKey(propertyName))

           {

               thrownewArgumentException($"Key mapping for {propertyName} is missing");

           }

           

           PropertyMappingValue  propertyMappingValue=mappingDictionary[propertyName];

           if (propertyMappingValue==null)

           {

               thrownewArgumentNullException("propertyMappingValue");

           }

 

           foreach(vardestinationPropertyin

               propertyMappingValue.DestinationProperties.Reverse())

           {

               // 给IQueryable 添加排序字符串

               orderByString=orderByString+

                   (string.IsNullOrWhiteSpace(orderByString) ?string.Empty : ", ")

                   +destinationProperty

                   + (orderDescending?" descending" : " ascending");

           }

       }

       //核心

       //要引用using System.Linq.Dynamic.Core;可以通过字符串,而不是result.OrderBy(t => t.OriginalPrice);

       returnsource.OrderBy(orderByString);

   }

}

  1. program注册builder.Services.AddTransient<IPropertyMappingService, PropertyMappingService>();
  2. 在数据库服务仓库中

...

if (!string.IsNullOrWhiteSpace(orderBy))

{

   vartouristRouteMappingDictonary=_propertyMappingService.GetPropertyMapping<TouristRouteDto, TouristRoute>();

   result=result.ApplySort(orderBy, touristRouteMappingDictonary);

}

returnawaitPaginationList<TouristRoute>.CreateAsync(pageNumber,pageSize,result);

 

  1. 控制器中的操作方法

//判断输入中是否有匹配的相应类型

if (!_propertyMappingService.IsMappingExists<TouristRouteDto,TouristRoute>(paramaters.OrderBy))

{

   returnBadRequest("输入正确排序参数");

}

数据塑形

在某些条件下,用户只想得到商品id和名称,如果返回商品的所有信息则造成了带宽的浪费。

  1. 对象或者集合的扩展方法,以集合为例

publicstaticclassIEnumberableExtensions

{

   publicstaticIEnumerable<ExpandoObject>ShapeData<TSource>(thisIEnumerable<TSource>source,stringfields)

   {

       if (source==null)

       {

           thrownewArgumentNullException(nameof(source));

       }

       varexpandoObjectList=newList<ExpandoObject>();//保存动态对象

 

       //避免列表中遍历数据,创建一个属性信息列表

       varpropertyInfoList=newList<PropertyInfo>();

 

       if (string.IsNullOrWhiteSpace(fields))

       {

           //获得TSource所有的属性和实例

           varpropertyInfos=typeof(TSource)

               .GetProperties(BindingFlags.IgnoreCase|BindingFlags.Public|BindingFlags.Instance);

           propertyInfoList.AddRange(propertyInfos);

       }

       else

       {

           varfieldAfterSplit=fields.Split(',');

           foreach (varfieldinfieldAfterSplit)

           {

               varpropertyName=field.Trim();

               varpropertyInfo=typeof(TSource)

                   .GetProperty(propertyName, BindingFlags.IgnoreCase|BindingFlags.Public|BindingFlags.Instance);

               if (propertyInfo==null )

               {

                   thrownewArgumentNullException("无相关属性");

               }

               propertyInfoList.Add(propertyInfo);

           }

       }

       foreach (TSourcesourceObjectinsource )

       {

           //创建动态对象

           vardataShapedObject=newExpandoObject();

           foreach (varpropertyInfoinpropertyInfoList)

           {

               //获得对应属性的真实数据

               varpropertyValue=propertyInfo.GetValue(sourceObject);

               ((IDictionary<string,object>)dataShapedObject).Add(propertyInfo.Name, propertyValue);

           }

           expandoObjectList.Add(dataShapedObject);

       }

       returnexpandoObjectList;

   }

}

 

  1. 只需要在操作方法的返回值中使用return Ok(touristRouteDto.ShapeData(Fields));访问地址为https://localhost:7243/api/touristroutes?fields=id,title

多媒体HATEOAS

即在返回数据的同时,返回相关的自发现链接

privateIEnumerable<LinkDto>CreateLinkForTouristRoute(GuidtouristRouteId, string?fields)

{

   varlinks=newList<LinkDto>();

   links.Add(

       newLinkDto(

               Url.Link("GetTouristRouteById",new { touristRouteId , fields }),

               "self",

               "Get"

           )

       

       );

   returnlinks;

}

 

[HttpGet("{touristRouteId:Guid}", Name="GetTouristRouteById")]

publicasyncTask<IActionResult>GetTouristRouteById(GuidtouristRouteId,string?fields, [FromHeader(Name="Accept")] stringmediaType)

{

   varroutes=await_tourisTouteRepository.GetTourisRouteAsync(touristRouteId);

 

   if (routes==null)

   {

       returnNotFound($"旅游路线{touristRouteId}找不到");

   }

   vartouristRouteDto=_mapper.Map<TouristRouteDto>(routes);

   varlinkDtos=CreateLinkForTouristRoute(touristRouteId, fields);

   varresult=routes.ShapeData(fields) asIDictionary<string, object>; //数据塑形

    result.Add("links", linkDtos);

   returnOk(result);

}

 

某些情况,会不想返回自发现链接,这时候就需要head中的内容协商来解决,如头文件中Accept:"application/vnd.qs.hateoas+json"

全局设定媒体类型

  1. program

builder.Services.Configure<MvcOptions>(config=> {

   varoutputFormatter=config.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>()?.FirstOrDefault();

   if (outputFormatter!=null)

   {

       outputFormatter.SupportedMediaTypes.Add("application/vnd.qs.hateoas+json");

   }

});

  1. 操作方法

usingMicrosoft.Net.Http.Headers;

[HttpGet("{touristRouteId:Guid}", Name="GetTouristRouteById")]

publicasyncTask<IActionResult>GetTouristRouteById(GuidtouristRouteId,string?fields, [FromHeader(Name="Accept")] stringmediaType)

{

//一般为TryParse,因为可以能会设置多个

   if (!MediaTypeHeaderValue.TryParse(mediaType, outMediaTypeHeaderValueparasedMediatype))

   {

       returnBadRequest();

   }

   

 

   varroutes=await_tourisTouteRepository.GetTourisRouteAsync(touristRouteId);

 

   if (routes==null)

   {

       returnNotFound($"旅游路线{touristRouteId}找不到");

   }

   vartouristRouteDto=_mapper.Map<TouristRouteDto>(routes);

   varlinkDtos=CreateLinkForTouristRoute(touristRouteId, fields);

   varresult=routes.ShapeData(fields) asIDictionary<string, object>;

   

   if (parasedMediatype.MediaType=="application/vnd.qs.hateoas+json")

   {

       result.Add("links", linkDtos);

 

   }

   returnOk(result);

   

}

//只有头文件中的Accept中设置"application/vnd.qs.hateoas+json"才能获得link

局部设定媒体类型

//筛选器

[Produces(

   "application/json",

   "application/vnd.aleks.hateoas+json"

   )]

[HttpGet("{touristRouteId:Guid}", Name="GetTouristRouteById")]

publicasyncTask<IActionResult>GetTouristRouteById(GuidtouristRouteId,string?fields, [FromHeader(Name="Accept")] stringmediaType)

{

   if (!MediaTypeHeaderValue.TryParse(mediaType, outMediaTypeHeaderValueparasedMediatype))

   {

       returnBadRequest();

   }

   

 

   varroutes=await_tourisTouteRepository.GetTourisRouteAsync(touristRouteId);

 

   if (routes==null)

   {

       returnNotFound($"旅游路线{touristRouteId}找不到");

   }

   vartouristRouteDto=_mapper.Map<TouristRouteDto>(routes);

   varlinkDtos=CreateLinkForTouristRoute(touristRouteId, fields);

   varresult=routes.ShapeData(fields) asIDictionary<string, object>;

    //SubTypeWithoutSuffix会把后面的+json去掉

   boolisHateoas=parasedMediatype.SubTypeWithoutSuffix.EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase);

   

 

 

   if (isHateoas)

   {

       result.Add("links", linkDtos);

 

   }

   returnOk(result);

 


相关文章
|
1月前
|
SQL Oracle 关系型数据库
分页
分页
26 1
|
4月前
|
SQL Java 关系型数据库
3.分页
本文介绍了MyBatis中的分页技术,包括四种主要方法:自带`RowBounds`分页、第三方插件PageHelper、SQL分页以及数组分页。`RowBounds`通过内存处理所有查询结果实现分页;PageHelper插件能智能识别数据库类型并自动添加相应的分页关键字;SQL分页直接利用SQL语句中的`LIMIT`或类似关键字;数组分页则是查询所有数据后使用`subList`进行切片。此外,还提到了自定义拦截器实现分页的方式。物理分页虽在小数据量场景下效率较低,但在大数据量时更为适用,优于逻辑分页。
|
7月前
分页实现
分页实现
40 0
|
SQL Oracle 关系型数据库
什么是分页?如何使用分页?(一)
什么是分页?如何使用分页?
189 0
|
7月前
|
SQL Oracle 关系型数据库
3.分页
3.分页
|
SQL 存储 关系型数据库
什么是分页?如何使用分页?(二)
什么是分页?如何使用分页?
82 0
|
SQL 分布式计算 前端开发
分页 fromsize|学习笔记
快速学习分页 fromsize。
分页 fromsize|学习笔记
|
Java 数据库连接 开发者
分页的实现| 学习笔记
快速学习分页的实现
166 0
|
数据采集 算法 前端开发
查询分页不只有 limit,这四种分页方法值得掌握
查询分页不只有 limit,这四种分页方法值得掌握
278 0
查询分页不只有 limit,这四种分页方法值得掌握
|
SQL HIVE 开发者
分页 fromsize | 学习笔记
快速学习分页 fromsize