• net core RESTful Api笔记⑧


    数据的塑形:

    允许api的消费者选择返回的资源的字段

    /api.companies?fields=id,name

    针对资源的字段,而不是其他更低层次的对象的字段。

    创建自定义方法IEnumerableExtensions:针对集合的,这里使用反射+扩展方法,就可以对这种问题处理了

    using Microsoft.AspNetCore.Mvc.Filters;
    using System;
    using System.Collections.Generic;
    using System.Dynamic;
    using System.Linq;
    using System.Reflection;
    using System.Threading.Tasks;
    
    namespace Rountine.API.Helpers
    {
        public  static class IEnumerableExtensions
        {
            public static IEnumerable<ExpandoObject> ShapeData<TSource>(this IEnumerable<TSource> sources, string fields) 
            {
                if (sources == null) {
                    throw new ArgumentException(nameof(sources));
                }
                var expandoObject = new List<ExpandoObject>(sources.Count());
    
                var propertyInfoList = new List<PropertyInfo>();
                if (string.IsNullOrEmpty(fields))
                {
                    var properTyInfos = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    propertyInfoList.AddRange(properTyInfos);
                }
                else 
                {
                    var fieldsAfterSplit = fields.Split(",");
                    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($"Property:{propertyName}没找到:{typeof(TSource)}");
                        }
                        propertyInfoList.Add(propertyInfo);
                    }
                }
                foreach (TSource obj in sources) 
                {
                    var shapedObj = new ExpandoObject();
                    foreach (var propertyInfo in propertyInfoList) 
                    {
                        var propertyValue = propertyInfo.GetValue(obj);
                        ((IDictionary<string, object>)shapedObj).Add(propertyInfo.Name,propertyValue);
                    }
                    expandoObject.Add(shapedObj);
                }
                return expandoObject;
            }
        }
    }

    修改controller:

            [HttpGet(Name =nameof(GetCompanies))]
            public async Task<IActionResult> GetCompanies([FromQuery] CompanyDtoParameters parameters)
            {
                var companies = await _companyRepository.GetCompaniesAsync(parameters);
    
                var previousPageLink = companies.HasPrevious ? CreateCompaniesResourceUri(parameters, ResourceUriType.PreviousPage) : null;
                var nextPageLink = companies.HasNext ? CreateCompaniesResourceUri(parameters, ResourceUriType.NextPage) : null;
    
                var paginationMetadate = new
                {
                      totalCount= companies.ToTalCount,
                      pageSize=companies.PageSize,
                      currentPage=companies.CurrentPage,
                      totalPages=companies.ToTalPages,
                      previousPageLink,
                      nextPageLink
                };
                Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(paginationMetadate, new JsonSerializerOptions
                {
                    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                }) ) ;
                var companyDto = _mapper.Map<IEnumerable<CompanyDto>>(companies);
                
                return Ok(companyDto.ShapeData(parameters.Fields));
            }

    postman:

     针对对象ObjectExtensions:

    using System;
    using System.Collections.Generic;
    using System.Dynamic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Threading.Tasks;
    
    namespace Rountine.API.Helpers
    {
        public static class ObjectExtensions
        {
            public static ExpandoObject shapeData<TSource>(this TSource source, string fields) 
            {
                if (source == null) 
                {
                    throw new ArgumentNullException(nameof(source));
                }
                var expandoObject = new ExpandoObject();
                if (string.IsNullOrEmpty(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>)expandoObject).Add(propertyInfo.Name, propertyValue);
                    }
                }
                else 
                {
                    var fieldsAfterSplit = fields.Split(",");
                    foreach (var field in fieldsAfterSplit) 
                    {
                        var propertyName = field.Trim();
                        var propertyInfo = typeof(TSource).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
                        if (propertyInfo == null) 
                        {
                            throw new Exception();
                        }
                        var propertyValue = propertyInfo.GetValue(source);
                        ((IDictionary<string, object>)expandoObject).Add(propertyInfo.Name, propertyValue);
                    }
                }
                return expandoObject;
            }
        }
    }

    hatos

    Hypermedia as the engine of application state

    hateoas是rest风格架构中最复杂的约束,也是构建成熟rest服务核心,它的重要性是打破客户端和服务器之间严格的契约,使得客户端能智能自适应,

    而rest本身进化更新跟冗余

    有助于进化和自我描述

    超媒体提如何驱动

    支持hateos两种办法

    静态类型方案:需要基类(包含link)和包装类,也就是返回的资源里面都含有link,通过集成同一个基类实现。

    动态类型方案:需要使用匿名类型或者ExpandoObject等,对于单个资源可以使用ExpandoObject,而对于集合使用匿名类。

    单个资源的hateos支持:

     LinkDto:

    using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace Rountine.API.Models
    {
        public class LinkDto
        {
            public string  Href { get; }
            public string Rel { get;  }
            public string Method { get;  }
            public LinkDto(string href,string rel,string method)
            {
                Href = href;
                Rel = rel;
                Method = method;
            }
        }
    }

    CompainesController:

            [HttpGet("{companyId}", Name = nameof(GetCompany))]
            public async Task<IActionResult> GetCompany(Guid companyId, string field)
            {
                var company = await _companyRepository.GetCompanyAsync(companyId);
                if (company == null)
                {
                    return NotFound();
                }
                var companyDto = _mapper.Map<CompanyDto>(company);
    
                var links = CreatLinkForCompany(companyId, field);
                var linkedResoruce = companyDto.shapeData(field) as IDictionary<string, object>;
                linkedResoruce.Add("links",links);
                return Ok(linkedResoruce);
            }
         private IEnumerable<LinkDto> CreatLinkForCompany(Guid companyId, String field)
            {
                var links = new List<LinkDto>();
    
                if (string.IsNullOrEmpty(field))
                {
                    links.Add(new LinkDto(Url.Link(nameof(GetCompany), new { companyId }), "self", "GET"));
                }
                else 
                {
                    links.Add(new LinkDto(Url.Link(nameof(GetCompany), new { companyId, field }), "self", "GET"));
                }
                
                return links;
            }

    postman:

     集合资源的hateos支持:

        [HttpGet(Name = nameof(GetCompanies))]
            public async Task<IActionResult> GetCompanies([FromQuery] CompanyDtoParameters parameters)
            {
                var companies = await _companyRepository.GetCompaniesAsync(parameters);
    
                var previousPageLink = companies.HasPrevious ? CreateCompaniesResourceUri(parameters, ResourceUriType.PreviousPage) : null;
                var nextPageLink = companies.HasNext ? CreateCompaniesResourceUri(parameters, ResourceUriType.NextPage) : null;
    
                var paginationMetadate = new
                {
                    totalCount = companies.ToTalCount,
                    pageSize = companies.PageSize,
                    currentPage = companies.CurrentPage,
                    totalPages = companies.ToTalPages,
                    previousPageLink,
                    nextPageLink
                };
                Response.Headers.Add("X-Pagination", JsonSerializer.Serialize(paginationMetadate, new JsonSerializerOptions
                {
                    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                }));
    
                var companyDto = _mapper.Map<IEnumerable<CompanyDto>>(companies);
                var shape=companyDto.ShapeData(parameters.Fields);
    
                var links = createLinksForCompany(parameters);
    
                var shapedCompaniesWithLinks = shape.Select(c => {
                    var companyDict = c as IDictionary<string, Object>;
                    var companyLinks = CreatLinkForCompany((Guid)companyDict["Id"],null);
                    companyDict.Add("links",companyLinks);
                    return companyDict;
                });
    
                var linkedCollectionResource = new
                {
                    value = shapedCompaniesWithLinks,
                    links
                };
                return Ok(linkedCollectionResource);
            }
          private IEnumerable<LinkDto> createLinksForCompany(CompanyDtoParameters parameters) 
            {
                var links = new List<LinkDto>();
                links.Add(new LinkDto(CreateCompaniesResourceUri(parameters, ResourceUriType.Currentpage), "self", "GET"));
    
                return links;
            }

    响应:

  • 相关阅读:
    在Windows环境下搭建redis
    三种主流的Web服务实现方案(REST+SOAP+XML-RPC)简述及比较
    ASP.NET Web API身份验证和授权
    quartz 设置时间格式
    服务端发post请求产生的编码问题
    大型网站的灵魂——性能
    大型网站系统架构的演化
    c# url自动解码解决方案
    C# RSA非对称加密实现
    .net上传图片之使用第三方平台七牛上传图片接口
  • 原文地址:https://www.cnblogs.com/liuyang95/p/13252073.html
Copyright © 2020-2023  润新知