分页
分页后需要将分页数据返回到head文件中,如实现过程
- program中注册
builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
- 控制器中增加依赖
privatereadonlyIUrlHelper_urlHelper;
publicTouristRoutesController( IActionContextAccessoractionContextAccessor)
{
_urlHelper=urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext);
}
- 生成前一页和后一页的方法
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
})
};
}
- 控制器中的查询操作方法
[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);
- 映射值类
publicclassPropertyMappingValue
{
//将要被映射的属性列表
publicIEnumerable<string>DestinationProperties { get; privateset; }
publicPropertyMappingValue(IEnumerable<string>destinationProperties)
{
DestinationProperties=destinationProperties;
}
}
- 映射类
//为了方便注入,先定义接口
publicinterfaceIPropertyMapping
{
}
publicclassPropertyMapping<TSource,TDestination>:IPropertyMapping
{
//属性所匹配的字符串字典
publicDictionary<string,PropertyMappingValue>_mappingDictionary { set; get; }
publicPropertyMapping(Dictionary<string, PropertyMappingValue>mappingDictionary)
{
_mappingDictionary=mappingDictionary;
}
}
- 映射服务类
//为了方便注入,先定义接口
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;
}
}
- 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);
}
}
- program注册
builder.Services.AddTransient<IPropertyMappingService, PropertyMappingService>();
- 在数据库服务仓库中
...
if (!string.IsNullOrWhiteSpace(orderBy))
{
vartouristRouteMappingDictonary=_propertyMappingService.GetPropertyMapping<TouristRouteDto, TouristRoute>();
result=result.ApplySort(orderBy, touristRouteMappingDictonary);
}
returnawaitPaginationList<TouristRoute>.CreateAsync(pageNumber,pageSize,result);
- 控制器中的操作方法
//判断输入中是否有匹配的相应类型
if (!_propertyMappingService.IsMappingExists<TouristRouteDto,TouristRoute>(paramaters.OrderBy))
{
returnBadRequest("输入正确排序参数");
}
数据塑形
在某些条件下,用户只想得到商品id和名称,如果返回商品的所有信息则造成了带宽的浪费。
- 对象或者集合的扩展方法,以集合为例
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;
}
}
- 只需要在操作方法的返回值中使用
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"
全局设定媒体类型
- program
builder.Services.Configure<MvcOptions>(config=> {
varoutputFormatter=config.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>()?.FirstOrDefault();
if (outputFormatter!=null)
{
outputFormatter.SupportedMediaTypes.Add("application/vnd.qs.hateoas+json");
}
});
- 操作方法
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);