分页
【摘要】 1. program中注册`builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();`
2. 控制器中增加依赖
分页
分页后需要将分页数据返回到head文件中,如
实现过程
-
program中注册
builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
-
控制器中增加依赖
private readonly IUrlHelper _urlHelper;
public TouristRoutesController( IActionContextAccessor actionContextAccessor)
{
_urlHelper = urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);
}
- 生成前一页和后一页的方法
public enum ResourceUriType
{
PreviousPage,
NextPage
}
private string? GenerateTouristRouteResourceURL(TouristRouteResourcecParamaters paramaters, PaginationResourceParamaters paramaters2, ResourceUriType type)
{
return type switch
{
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
})
};
}
- 控制器中的查询操作方法
[HttpGet(Name = "GetTouristRoutes")]
[HttpHead]
[Authorize(AuthenticationSchemes = "Bearer")]//identity多角色验证时必须指定
public async Task<IActionResult> GetTouristRoutes([FromQuery] TouristRouteResourcecParamaters paramaters, [FromQuery] PaginationResourceParamaters paramaters1)
{
var routes = await _tourisTouteRepository.GetTourisRoutesAsync(paramaters.Keyword, paramaters.RatingOperator,paramaters.RatingValue, paramaters1.PageSize,paramaters1.PageNumber);
if (routes == null || routes.Count() <= 0)
{
return NotFound("没有旅游路线");
}
var touristRouteDto = _mapper.Map<IEnumerable<TouristRouteDto>>(routes);
//生成前页面链接
var previousPageLink = routes.HasPrevious ? GenerateTouristRouteResourceURL(paramaters, paramaters1, ResourceUriType.PreviousPage) : null;
//生成前页面链接
var nextPageLink = routes.HasNext ? GenerateTouristRouteResourceURL(paramaters, paramaters1, ResourceUriType.NextPage) : null;
//增加头部信息
var paginationMetadata = 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));
return Ok(touristRouteDto);
}
排序
思路为解析地址栏中用户想以哪个字段进行那种排序(升降),核心就是要引用using System.Linq.Dynamic.Core;可以通过字符串,而不是对象使用result.OrderBy(t => t.OriginalPrice);
- 映射值类
public class PropertyMappingValue
{
//将要被映射的属性列表
public IEnumerable<string> DestinationProperties { get; private set; }
public PropertyMappingValue(IEnumerable<string> destinationProperties)
{
DestinationProperties = destinationProperties;
}
}
- 映射类
//为了方便注入,先定义接口
public interface IPropertyMapping
{
}
public class PropertyMapping<TSource,TDestination>:IPropertyMapping
{
//属性所匹配的字符串字典
public Dictionary<string,PropertyMappingValue> _mappingDictionary { set; get; }
public PropertyMapping(Dictionary<string, PropertyMappingValue> mappingDictionary)
{
_mappingDictionary = mappingDictionary;
}
}
- 映射服务类
//为了方便注入,先定义接口
public interface IPropertyMappingService
{
Dictionary<string, PropertyMappingValue> GetPropertyMapping<TSource, TDestination>();
bool IsMappingExists<TSource, TDestingation>(string fields);
}
public class PropertyMappingService : IPropertyMappingService
{
//设定字符串与PropertyMappingValue的对应关系
private Dictionary<string, PropertyMappingValue> _touristRoutePropertyMapping =
new Dictionary<string, PropertyMappingValue>(StringComparer.OrdinalIgnoreCase)
{
{ "Id", new PropertyMappingValue(new List<string>(){ "Id" }) },
{ "Title", new PropertyMappingValue(new List<string>(){ "Title" })},
{ "Rating", new PropertyMappingValue(new List<string>(){ "Rating" })},
{ "OriginalPrice", new PropertyMappingValue(new List<string>(){ "OriginalPrice" })},
};
//定义一个映射对象,映射对象里面包含字符串和PropertyMappingValue对应的字典
private IList<IPropertyMapping> _propertyMappings = new List<IPropertyMapping>();
public PropertyMappingService()
{ //将新建的对应列表加入到私有映射对象
_propertyMappings.Add(
new PropertyMapping<TouristRouteDto, TouristRoute>(
_touristRoutePropertyMapping));
}
public Dictionary<string, PropertyMappingValue>
GetPropertyMapping<TSource, TDestination>()
{
// 获得匹配的映射对象
var matchingMapping =
_propertyMappings.OfType<PropertyMapping<TSource, TDestination>>();
if (matchingMapping.Count() == 1)
{
return matchingMapping.First()._mappingDictionary;
}
throw new Exception(
$"Cannot find exact property mapping instance for <{typeof(TSource)},{typeof(TDestination)}");
}
public bool IsMappingExists<TSource, TDestination>(string fields)
{
var propertyMapping = GetPropertyMapping<TSource, TDestination>();
if (string.IsNullOrWhiteSpace(fields))
{
return true;
}
//逗号来分隔字段字符串
var fieldsAfterSplit = fields.Split(",");
foreach(var field in fieldsAfterSplit)
{
// 去掉空格
var trimmedField = field.Trim();
// 获得属性名称字符串
var indexOfFirstSpace = trimmedField.IndexOf(" ");
var propertyName = indexOfFirstSpace == -1 ?
trimmedField : trimmedField.Remove(indexOfFirstSpace);
if (!propertyMapping.ContainsKey(propertyName))
{
return false;
}
}
return true;
}
}
- nuget安装
System.Linq.Dynamic.Core
。IQueryable扩展类
public static class IQueryableExtensions
{
public static IQueryable<T> ApplySort<T>(
this IQueryable<T> source,
string orderBy,
Dictionary<string, PropertyMappingValue> mappingDictionary
)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (mappingDictionary == null)
{
throw new ArgumentNullException("mappingDictionary");
}
if (string.IsNullOrWhiteSpace(orderBy))
{
return source;
}
var orderByString = string.Empty;
var orderByAfterSplit = orderBy.Split(',');
foreach(var order in orderByAfterSplit)
{
var trimmedOrder = order.Trim();
// 通过字符串“ desc”来判断升序还是降序
var orderDescending = trimmedOrder.EndsWith(" desc");
// 删除升序或降序字符串 " asc" or " desc"来获得属性的名称
var indexOfFirstSpace = trimmedOrder.IndexOf(" ");
var propertyName = indexOfFirstSpace == -1
? trimmedOrder
: trimmedOrder.Remove(indexOfFirstSpace);
if (!mappingDictionary.ContainsKey(propertyName))
{
throw new ArgumentException($"Key mapping for {propertyName} is missing");
}
PropertyMappingValue propertyMappingValue = mappingDictionary[propertyName];
if (propertyMappingValue == null)
{
throw new ArgumentNullException("propertyMappingValue");
}
foreach(var destinationProperty in
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);
return source.OrderBy(orderByString);
}
}
-
program注册
builder.Services.AddTransient<IPropertyMappingService, PropertyMappingService>();
-
在数据库服务仓库中
...
if (!string.IsNullOrWhiteSpace(orderBy))
{
var touristRouteMappingDictonary = _propertyMappingService.GetPropertyMapping<TouristRouteDto, TouristRoute>();
result= result.ApplySort(orderBy, touristRouteMappingDictonary);
}
return await PaginationList<TouristRoute>.CreateAsync(pageNumber,pageSize,result);
- 控制器中的操作方法
//判断输入中是否有匹配的相应类型
if (!_propertyMappingService.IsMappingExists<TouristRouteDto,TouristRoute>(paramaters.OrderBy))
{
return BadRequest("输入正确排序参数");
}
数据塑形
在某些条件下,用户只想得到商品id和名称,如果返回商品的所有信息则造成了带宽的浪费。
- 对象或者集合的扩展方法,以集合为例
public static class IEnumberableExtensions
{
public static IEnumerable<ExpandoObject> ShapeData<TSource>(this IEnumerable<TSource> source,string fields)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var expandoObjectList = new List<ExpandoObject>();//保存动态对象
//避免列表中遍历数据,创建一个属性信息列表
var propertyInfoList = new List<PropertyInfo>();
if (string.IsNullOrWhiteSpace(fields))
{
//获得TSource所有的属性和实例
var propertyInfos = typeof(TSource)
.GetProperties(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
propertyInfoList.AddRange(propertyInfos);
}
else
{
var fieldAfterSplit = fields.Split(',');
foreach (var field in fieldAfterSplit)
{
var propertyName = field.Trim();
var propertyInfo = typeof(TSource)
.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo == null )
{
throw new ArgumentNullException("无相关属性");
}
propertyInfoList.Add(propertyInfo);
}
}
foreach (TSource sourceObject in source )
{
//创建动态对象
var dataShapedObject = new ExpandoObject();
foreach (var propertyInfo in propertyInfoList)
{
//获得对应属性的真实数据
var propertyValue = propertyInfo.GetValue(sourceObject);
((IDictionary<string,object>)dataShapedObject).Add(propertyInfo.Name, propertyValue);
}
expandoObjectList.Add(dataShapedObject);
}
return expandoObjectList;
}
}
- 只需要在操作方法的返回值中使用
return Ok(touristRouteDto.ShapeData(Fields));
访问地址为https://localhost:7243/api/touristroutes?fields=id,title
多媒体HATEOAS
即在返回数据的同时,返回相关的自发现链接
private IEnumerable<LinkDto> CreateLinkForTouristRoute(Guid touristRouteId, string? fields)
{
var links = new List<LinkDto>();
links.Add(
new LinkDto(
Url.Link("GetTouristRouteById",new { touristRouteId , fields }),
"self",
"Get"
)
);
return links;
}
[HttpGet("{touristRouteId:Guid}", Name = "GetTouristRouteById")]
public async Task<IActionResult> GetTouristRouteById(Guid touristRouteId,string? fields, [FromHeader(Name ="Accept")] string mediaType)
{
var routes = await _tourisTouteRepository.GetTourisRouteAsync(touristRouteId);
if (routes == null)
{
return NotFound($"旅游路线{touristRouteId}找不到");
}
var touristRouteDto = _mapper.Map<TouristRouteDto>(routes);
var linkDtos = CreateLinkForTouristRoute(touristRouteId, fields);
var result = routes.ShapeData(fields) as IDictionary<string, object>; //数据塑形
result.Add("links", linkDtos);
return Ok(result);
}
某些情况,会不想返回自发现链接,这时候就需要head中的内容协商来解决,如头文件中Accept:"application/vnd.qs.hateoas+json"
全局设定媒体类型
- program
builder.Services.Configure<MvcOptions>(config => {
var outputFormatter = config.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>()?.FirstOrDefault();
if (outputFormatter !=null)
{
outputFormatter.SupportedMediaTypes.Add("application/vnd.qs.hateoas+json");
}
});
- 操作方法
using Microsoft.Net.Http.Headers;
[HttpGet("{touristRouteId:Guid}", Name = "GetTouristRouteById")]
public async Task<IActionResult> GetTouristRouteById(Guid touristRouteId,string? fields, [FromHeader(Name ="Accept")] string mediaType)
{
//一般为TryParse,因为可以能会设置多个
if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue parasedMediatype))
{
return BadRequest();
}
var routes = await _tourisTouteRepository.GetTourisRouteAsync(touristRouteId);
if (routes == null)
{
return NotFound($"旅游路线{touristRouteId}找不到");
}
var touristRouteDto = _mapper.Map<TouristRouteDto>(routes);
var linkDtos = CreateLinkForTouristRoute(touristRouteId, fields);
var result = routes.ShapeData(fields) as IDictionary<string, object>;
if (parasedMediatype.MediaType == "application/vnd.qs.hateoas+json")
{
result.Add("links", linkDtos);
}
return Ok(result);
}
//只有头文件中的Accept中设置"application/vnd.qs.hateoas+json"才能获得link
局部设定媒体类型
//筛选器
[Produces(
"application/json",
"application/vnd.aleks.hateoas+json"
)]
[HttpGet("{touristRouteId:Guid}", Name = "GetTouristRouteById")]
public async Task<IActionResult> GetTouristRouteById(Guid touristRouteId,string? fields, [FromHeader(Name ="Accept")] string mediaType)
{
if (!MediaTypeHeaderValue.TryParse(mediaType, out MediaTypeHeaderValue parasedMediatype))
{
return BadRequest();
}
var routes = await _tourisTouteRepository.GetTourisRouteAsync(touristRouteId);
if (routes == null)
{
return NotFound($"旅游路线{touristRouteId}找不到");
}
var touristRouteDto = _mapper.Map<TouristRouteDto>(routes);
var linkDtos = CreateLinkForTouristRoute(touristRouteId, fields);
var result = routes.ShapeData(fields) as IDictionary<string, object>;
//SubTypeWithoutSuffix会把后面的+json去掉
bool isHateoas = parasedMediatype.SubTypeWithoutSuffix.EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase);
if (isHateoas)
{
result.Add("links", linkDtos);
}
return Ok(result);
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)