.net core 塑形资源
电脑版发表于:2019/10/30 10:53
1.什么是塑形资源
结合http://www.tnblog.net/hb/article/details/2787
对于排序可能要晚点讲解
塑形资源:相当于你去请求一个Api接口时你要请求多少个字段返回给,服务器端就输出多少字段给你
利用的是反射的原理
2.集合塑形资源与单个塑形资源
集合塑形资源EnumerableExtensions.cs
public static class EnumerableExtensions { /// <summary> /// 动态反射集合资源 /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="source"></param> /// <param name="fields"> 指定哪些属性或字段需要进行实例 </param> /// <returns></returns> public static IEnumerable<ExpandoObject> ToDynamicIEnumerable<TSource>(this IEnumerable<TSource> source, string fields = null) { if (source == null) { throw new ArgumentNullException(nameof(source)); } var expandoObjectList = new List<ExpandoObject>(); var propertyInfoList = new List<PropertyInfo>(); if (string.IsNullOrWhiteSpace(fields)) { //获取类中所有的公共的字段与实例 var propertyInfos = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance); //添加到propertyInfoList集合中去 propertyInfoList.AddRange(propertyInfos); } else { var fieldsAfterSplit = fields.Split(',').ToList(); foreach (var field in fieldsAfterSplit) { var propertyName = field.Trim(); if (string.IsNullOrEmpty(propertyName)) { continue; } //获取属性 var propertyInfo = typeof(TSource).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (propertyInfo == null) { throw new Exception($"Property {propertyName} wasn't found on {typeof(TSource)}"); } //添加到集合中 propertyInfoList.Add(propertyInfo); } } //通过每一个类获取其中的属性,字段的值到List<ExpandoObject> expandoObjectList中 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; } }
单个塑形资源ObjectExtensions.cs
public static class ObjectExtensions { /// <summary> /// 动态返回单个资源 /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="source"></param> /// <param name="fields"> 反射所需要的字段(用逗号分隔) </param> /// <returns></returns> public static ExpandoObject ToDynamic<TSource>(this TSource source, string fields = null) { if (source == null) { throw new ArgumentNullException(nameof(source)); } var dataShapedObject = new ExpandoObject(); //当为空时表示返回所有字段 if (string.IsNullOrWhiteSpace(fields)) { var propertyInfos = typeof(TSource).GetProperties(BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); foreach (var propertyInfo in propertyInfos) { var propertyValue = propertyInfo.GetValue(source); ((IDictionary<string, object>)dataShapedObject).Add(propertyInfo.Name, propertyValue); } return dataShapedObject; } //返回指定字段或属性 var fieldsAfterSplit = fields.Split(',').ToList(); foreach (var field in fieldsAfterSplit) { var propertyName = field.Trim(); var propertyInfo = typeof(TSource).GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (propertyInfo == null) { throw new Exception($"Can't found property ??{typeof(TSource)}?ˉ on ??{propertyName}?ˉ"); } var propertyValue = propertyInfo.GetValue(source); ((IDictionary<string, object>)dataShapedObject).Add(propertyInfo.Name, propertyValue); } return dataShapedObject; } }
3.结合我们对前文的PostController.cs的Get方法的修改
[HttpGet(Name = "GetPosts")] public async Task<IActionResult> Get(PostParameters postParameters) { var posts = await _PostRepository.GetAllPosts(postParameters); var postResources = _mapper.Map<IEnumerable<Post>, IEnumerable<PostResource>>(posts); #region 塑形资源() //这里我们是通过Field字段进行查找 var result = postResources.ToDynamicIEnumerable(postParameters.Fields); #endregion //判断上一页 var previousPageLink = posts.HasPrevious ? CreatePostUri(postParameters, PaginationResourceUriType.PreviousPage) : null; //判断下一页 var nextPageLink = posts.HasNext ? CreatePostUri(postParameters, PaginationResourceUriType.NextPage) : null; var metta = new { Pagesize = posts.PageSize, posts.PageIndex, posts.TotalItemsCount, posts.PageCount, PreviousPageLink=previousPageLink, NextPageLink = nextPageLink }; //标准化 Response.Headers.Add("x-Pagination", JsonConvert.SerializeObject(metta,new JsonSerializerSettings() { //Json输出格式小写 ContractResolver = new CamelCasePropertyNamesContractResolver() })); return Ok(result); }
Result:
没塑形之前的
塑形之后(只要id与anthor):
4.问题
问题一:
从上面看出来一个问题,我们发现输出接口的字段首字母变大写了
解决:
Startup.cs-->ConfigureServices方法
services.AddMvc(option => { option.ReturnHttpNotAcceptable = true; option.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter()); }).AddJsonOptions(options=> { //输出小写 options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); });
Result:
问题二:
如果字段不存在那么内部就会异常,所以我们需要进行验证一下
解决:
创建一个ITypeHelperService接口
public interface ITypeHelperService { bool TypeHasProperties<T>(string fields); }
创建一个TypeHelperService.cs验证Failed字段并实现ITypeHelperService接口
public class TypeHelperService : ITypeHelperService { public bool TypeHasProperties<T>(string fields) { if (string.IsNullOrWhiteSpace(fields)) { return true; } var fieldsAfterSplit = fields.Split(','); foreach (var field in fieldsAfterSplit) { var propertyName = field.Trim(); if (string.IsNullOrEmpty(propertyName)) { continue; } var propertyInfo = typeof(T) .GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (propertyInfo == null) { return false; } } return true; } }
服务注入
#region 验证塑形字段注册 services.AddTransient<ITypeHelperService, TypeHelperService>(); #endregion
然后再控制器中
[HttpGet(Name = "GetPosts")] public async Task<IActionResult> Get(PostParameters postParameters) { #region 排序验证 if (!_propertyMappingContainer.ValidateMappingExistsFor<PostResource,Post>(postParameters.OrderBy)) { return BadRequest(" Can't finds fields not sorting "); } #endregion #region 判断塑形字段是否存在 if (!_typeHelperService.TypeHasProperties<PostResource>(postParameters.Fields)) { return BadRequest("Fields not exist"); } #endregion ...
Result: