生成用于ASP.NET Web API的C#客户端API
目录
介绍
主要特征
主要好处
背景
推定(Presumptions)
使用代码
步骤0:将NuGet软件包WebApiClientGen安装到Web MVC/API项目
步骤1:建立.NET Client API项目
步骤2:准备JSON配置数据
步骤3:运行Web API项目的DEBUG构建
步骤4:发布JSON Config数据以触发客户端API代码的生成
发布客户端API库
使用生成的API代码
支持通用Windows应用,Android应用和iOS应用
优点总结
SDLC
团队合作
兴趣点
Swashbuckle +AutoRest VS. WebApiClientGen
- GitHub上的代码示例
介绍
用于开发ASP.NET Web API或ASP.NET Core Web API的客户端程序, 强类型客户端API生成器以C#代码和TypeScript代码生成强类型客户端API,以最大程度地减少重复性任务并提高应用程序开发人员的生产率和产品质量。
这个开源项目提供以下产品:
- C#中用于强类型客户端API的代码生成器,支持桌面,Universal Windows,Android和iOS。
- 用于jQuery,Angular 2+和Aurelia的TypeScript以及使用Axios的 TypeScript / JavaScript应用程序中的强类型客户端API的代码生成器。
- TypeScript CodeDOM,一种TypeScript的CodeDOM组件,从.NET Framework的CodeDOM派生而来。
- POCO2TS.exe,这是一个从POCO类生成TypsScript接口的命令行程序。
- Fonlow.Poco2Ts,从POCO类生成TypsScript接口的组件。
主要特征
- 生成的客户端API代码直接从Web API控制器方法,.NET基本类型和POCO类进行映射。这类似于WCF中提供的svcutil.exe。
- 控制器方法和POCO类的文档注释将复制到客户端代码。
主要好处
- WebApiClientGen 在RAD或敏捷软件开发期间,只需很少的步骤/开销就可以与ASP.NET Web API无缝集成,以在Web API和客户端API之间进行设置、维护和同步。
- 支持所有.NET基本类型,包括十进制。
- 支持DataTime,DataTimeOffset,Array,Tuple,动态对象,Dictionary和KeyValuePair
- 强类型生成的代码需要进行设计时类型检查和编译时类型检查。
- 提供高级抽象,使应用程序开发人员免受传统HTTP客户端调用带来的RESTful实践琐碎技术细节的影响。
- 丰富的元信息(包括文档注释)使IDE intellisense更加有用,因此应用程序开发人员较少需要阅读单独的API文档。
背景
如果您曾经使用WCF开发过基于SOAP的Web服务,则可能会喜欢使用SvcUtil.exe或Visual Studio IDE的服务引用生成的客户端API代码。转向Web API时,我感到自己回到了石器时代,因为我不得不在设计时花很多时间检查数据类型和函数原型,这消耗了我太多宝贵的脑力,而计算机本应该做这样的检查。
早在2010年,我就在IHttpHandler/IHttpModule 的基础上开发了一些RESTful Web服务,这些服务不需要强类型数据,而需要文档和流之类的任意数据。但是,我一直在开发更多需要高度抽象和语义数据类型的Web项目。
我看到ASP.NET Web API确实通过类ApiController支持高度抽象和强类型化的函数原型,并且ASP.NET MVC框架可选地提供生成良好的描述API函数的帮助页面。但是,在开发了Web API之后,我必须手工制作一些非常原始且重复的客户端代码以使用Web服务。如果Web API是由其他人开发的,我将必须阅读在线帮助页面。
我怀念WCF的美好时光。:)应该减少客户端编程的开销。
因此,我进行了搜索并试图找到一些解决方案,这些解决方案可以使我摆脱编写原始和重复的代码的麻烦,因此我可以专注于在客户端构建业务逻辑。以下是协助客户程序开发的开源项目列表:
- WADL
- RAML with .NET
- WebApiProxy
- Swashbuckle based on Swagger
- AutoRest
- OData
虽然这些解决方案可以生成强类型的客户端代码并在某种程度上减少重复的任务,但我发现它们都无法给我带来我所期望的所有流畅而高效的编程经验:
- 映射到服务数据模型的强类型客户端数据模型。
- 强类型函数原型映射到的派生类ApiController的功能。
- 像WCF编程那样以批发方式生成代,因此SDLC期间的开销最少。
- 使用流行的属性(如DataContractAttribute和JsonObjectAttribute等)通过数据注释来挑选数据模型。
- 在设计时和编译时进行类型检查。
- 用于客户端数据模型,功能原型和文档注释的智能感知。
这是WebApiClientGen。
推定(Presumptions)
- 您将开发ASP.NET Web API 2.x应用程序,并将使用C#作为主要编程语言来开发在Windows桌面、Universal Windows、Android或iOS上运行的应用程序。
- 您和开发人员都喜欢通过服务器端和客户端中的强类型函数来实现高度抽象。
- Web API和Entity Framework Code First都使用POCO类,并且您可能不想将所有数据类和成员发布到客户端程序。
为了跟进这种开发客户端程序的新方法,最好拥有一个ASP.NET Web API项目或一个包含Web API的MVC项目。您可以使用现有项目,也可以创建一个演示项目。
使用代码
步骤0:将NuGet软件包WebApiClientGen安装到Web MVC/API项目
安装还将安装相关的NuGet软件包Fonlow.TypeScriptCodeDOM和Fonlow.Poco2Ts到项目引用。
此外,用于触发CodeGen的CodeGenController.cs被添加到项目的Controllers文件夹中。
CodeGenController只在调试版本开发过程中应该是可用的,因为客户端API在每个Web API版本中近生成一次。
#if DEBUG //This controller is not needed in production release, //since the client API should be generated during development of the Web API
using Fonlow.CodeDom.Web;
using System.Linq;
using System.Web.Http;namespace Fonlow.WebApiClientGen
{[System.Web.Http.Description.ApiExplorerSettings(IgnoreApi = true)]//this controller //is a dev backdoor during development, no need to be visible in ApiExplorerpublic class CodeGenController : ApiController{/// <summary>/// Trigger the API to generate WebApiClientAuto.cs /// for an established client API project./// </summary>/// <param name="settings"></param>/// <returns>OK if OK</returns>[HttpPost]public IHttpActionResult TriggerCodeGen(CodeGenSettings settings){if (settings == null)return BadRequest("No settings");if (settings.ClientApiOutputs == null)return BadRequest("No settings/ClientApiOutputs");string webRootPath = System.Web.Hosting.HostingEnvironment.MapPath("~");Fonlow.Web.Meta.WebApiDescription[] apiDescriptions;try{apiDescriptions = Configuration.Services.GetApiExplorer().ApiDescriptions.Select(d => Fonlow.Web.Meta.MetaTransform.GetWebApiDescription(d)).OrderBy(d => d.ActionDescriptor.ActionName).ToArray();}catch (System.InvalidOperationException e){System.Diagnostics.Trace.TraceWarning(e.Message);return InternalServerError(e);}if (!settings.ClientApiOutputs.CamelCase.HasValue){var camelCase = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver is Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver;settings.ClientApiOutputs.CamelCase = camelCase;}try{CodeGen.GenerateClientAPIs(webRootPath, settings, apiDescriptions);}catch (Fonlow.Web.Meta.CodeGenException e){var s = e.Message + " : " + e.Description;System.Diagnostics.Trace.TraceError(s);return BadRequest(s);}return Ok("Done");}}}
#endif
备注
- CodeGenController安装在YourMvcOrWebApiProject / Controllers中,即使MVC项目的脚手架上具有ApiController派生类的文件夹API。
- WebApiClientGenCore没有安装CodeGenController,您应该将文件复制过来。
步骤1:建立.NET Client API项目
确保引用了以下软件包:
- Microsoft ASP.NET Web API 2.2客户端库
- Newtonsoft Json.NET。
- System.Runtime.Serialization
- System.ServiceModel
- System.ComponentModel.DataAnnotations
如此屏幕截图如下:
步骤2:准备JSON配置数据
您的Web API项目可能具有POCO类和API函数,如下所示:
namespace DemoWebApi.DemoData
{public sealed class Constants{public const string DataNamespace = "http://fonlow.com/DemoData/2014/02";}[DataContract(Namespace = Constants.DataNamespace)]public enum AddressType{[EnumMember]Postal,[EnumMember]Residential,};[DataContract(Namespace = Constants.DataNamespace)]public enum Days{[EnumMember]Sat = 1,[EnumMember]Sun,[EnumMember]Mon,[EnumMember]Tue,[EnumMember]Wed,[EnumMember]Thu,[EnumMember]Fri};[DataContract(Namespace = Constants.DataNamespace)]public class Address{[DataMember]public Guid Id { get; set; }public Entity Entity { get; set; }/// <summary>/// Foreign key to Entity/// </summary>public Guid EntityId { get; set; }[DataMember]public string Street1 { get; set; }[DataMember]public string Street2 { get; set; }[DataMember]public string City { get; set; }[DataMember]public string State { get; set; }[DataMember]public string PostalCode { get; set; }[DataMember]public string Country { get; set; }[DataMember]public AddressType Type { get; set; }[DataMember]public DemoWebApi.DemoData.Another.MyPoint Location;}[DataContract(Namespace = Constants.DataNamespace)]public class Entity{public Entity(){Addresses = new List<Address>();}[DataMember]public Guid Id { get; set; }[DataMember(IsRequired =true)]//MVC and Web API does not care[System.ComponentModel.DataAnnotations.Required]//MVC and Web API care about only thispublic string Name { get; set; }[DataMember]public IList<Address> Addresses { get; set; }public override string ToString(){return Name;}}[DataContract(Namespace = Constants.DataNamespace)]public class Person : Entity{[DataMember]public string Surname { get; set; }[DataMember]public string GivenName { get; set; }[DataMember]public DateTime? BirthDate { get; set; }public override string ToString(){return Surname + ", " + GivenName;}}[DataContract(Namespace = Constants.DataNamespace)]public class Company : Entity{[DataMember]public string BusinessNumber { get; set; }[DataMember]public string BusinessNumberType { get; set; }[DataMember]public string[][] TextMatrix{ get; set; }[DataMember]public int[][] Int2DJagged;[DataMember]public int[,] Int2D;[DataMember]public IEnumerable<string> Lines;}...
...namespace DemoWebApi.Controllers
{[RoutePrefix("api/SuperDemo")]public class EntitiesController : ApiController{/// <summary>/// Get a person/// </summary>/// <param name="id">unique id of that guy</param>/// <returns>person in db</returns>[HttpGet]public Person GetPerson(long id){return new Person(){Surname = "Huang",GivenName = "Z",Name = "Z Huang",BirthDate = DateTime.Now.AddYears(-20),};}[HttpPost]public long CreatePerson(Person p){Debug.WriteLine("CreatePerson: " + p.Name);if (p.Name == "Exception")throw new InvalidOperationException("It is exception");Debug.WriteLine("Create " + p);return 1000;}[HttpPut]public void UpdatePerson(Person person){Debug.WriteLine("Update " + person);}[HttpPut][Route("link")]public bool LinkPerson(long id, string relationship, [FromBody] Person person){return person != null && !String.IsNullOrEmpty(relationship);}[HttpDelete]public void Delete(long id){Debug.WriteLine("Delete " + id);}[Route("Company")][HttpGet]public Company GetCompany(long id){
JSON有效负载是这样的:
{"ApiSelections": {"ExcludedControllerNames": ["DemoWebApi.Controllers.Account","DemoWebApi.Controllers.FileUpload"],"DataModelAssemblyNames": ["DemoWebApi.DemoData","DemoWebApi"],"CherryPickingMethods": 3},"ClientApiOutputs": {"ClientLibraryProjectFolderName": "..\\DemoWebApi.ClientApi","GenerateBothAsyncAndSync": true,"Plugins": []}
}
有效负载示例随v1.8.0一起提供,并且可以在此处找到早期版本。
建议的JSON有效载荷保存到像一个这样的文件中。
如果在Web API项目中定义了所有POCO类,则应将Web API项目的程序集名称放在“DataModelAssemblyNames”数组中。如果您有一些专用的数据模型程序集可以很好地分离关注点,则应将相应的程序集名称放入数组中。您可以选择生成TypeScript客户端API代码或C#客户端API代码,或同时生成两者。
CodeGen根据“CherryPickingMethods”从POCO类生成C#客户端代理类,如以下文档注释中所述:
/// <summary>
/// Flagged options for cherry picking in various development processes.
/// </summary>
[Flags]
public enum CherryPickingMethods
{/// <summary>/// Include all public classes, properties and properties./// </summary>All = 0,/// <summary>/// Include all public classes decorated by DataContractAttribute,/// and public properties or fields decorated by DataMemberAttribute./// And use DataMemberAttribute.IsRequired/// </summary>DataContract =1,/// <summary>/// Include all public classes decorated by JsonObjectAttribute,/// and public properties or fields decorated by JsonPropertyAttribute./// And use JsonPropertyAttribute.Required/// </summary>NewtonsoftJson = 2,/// <summary>/// Include all public classes decorated by SerializableAttribute,/// and all public properties or fields/// but excluding those decorated by NonSerializedAttribute./// And use System.ComponentModel.DataAnnotations.RequiredAttribute./// </summary>Serializable = 4,/// <summary>/// Include all public classes, properties and properties./// And use System.ComponentModel.DataAnnotations.RequiredAttribute./// </summary>AspNet = 8,
}
默认选项是“选择加入”的DataContract。您可以使用任何一种方法或方法的组合。
步骤3:运行Web API项目的DEBUG构建
步骤4:发布JSON Config数据以触发客户端API代码的生成
在IIS Express上的IDE中运行Web项目。
然后,您可以使用Curl或Poster或任何您喜欢的客户端工具通过使用content-type=application/json POST到http://localhost:10965/api/CodeGen 。
提示
因此,基本上,您只需要一步就可以生成客户端API,因为您不需要每次都安装NuGet软件包。
编写一些批处理脚本来启动Web API和POST JSON配置数据应该不难。实际上,我已经起草了一个供您参考:CreateClientApi.ps1,它在IIS Express上启动Web(API)项目,然后发布JSON配置文件。因此,基本上,在大多数情况下,要连续更新/同步Web API和客户端API,您只需要通过运行Powershell脚本执行步骤3。这减少了持续集成的大量开销。
此序列图说明了开发周期:
发布客户端API库
完成这些步骤之后,现在您已经生成了C#客户端API,类似于以下示例:
namespace DemoWebApi.DemoData.Client
{ public enum AddressType{ Postal, Residential,}public enum Days{ Sat = 1, Sun = 2, Mon = 3, Tue = 4, Wed = 5, Thu = 6, Fri = 7,}public class Address : object{ private System.Guid _Id; private string _Street1; private string _Street2; private string _City; private string _State; private string _PostalCode; private string _Country; private DemoWebApi.DemoData.Client.AddressType _Type; private DemoWebApi.DemoData.Another.Client.MyPoint _Location; public System.Guid Id{get{return _Id;}set{_Id = value;}}public string Street1{get{return _Street1;}set{_Street1 = value;}}public string Street2{get{return _Street2;}set{_Street2 = value;}}public string City{get{return _City;}set{_City = value;}}public string State{get{return _State;}set{_State = value;}}public string PostalCode{get{return _PostalCode;}set{_PostalCode = value;}}public string Country{get{return _Country;}set{_Country = value;}}public DemoWebApi.DemoData.Client.AddressType Type{get{return _Type;}set{_Type = value;}}public DemoWebApi.DemoData.Another.Client.MyPoint Location{get{return _Location;}set{_Location = value;}}}public class Entity : object{private System.Guid _Id;private string _Name;private DemoWebApi.DemoData.Client.Address[] _Addresses;public System.Guid Id{get{return _Id;}set{_Id = value;}}[System.ComponentModel.DataAnnotations.RequiredAttribute()]public string Name{get{return _Name;}set{_Name = value;}}public DemoWebApi.DemoData.Client.Address[] Addresses{get{return _Addresses;}set{_Addresses = value;}}}public class Person : DemoWebApi.DemoData.Client.Entity{private string _Surname;private string _GivenName;private System.Nullable<System.DateTime> _BirthDate;public string Surname{get{return _Surname;}set{_Surname = value;}}public string GivenName{get{return _GivenName;}set{_GivenName = value;}}public System.Nullable<System.DateTime> BirthDate{get{return _BirthDate;}set{_BirthDate = value;}}}public class Company : DemoWebApi.DemoData.Client.Entity{private string _BusinessNumber;private string _BusinessNumberType;private string[][] _TextMatrix;private int[][] _Int2DJagged;private int[,] _Int2D;private string[] _Lines;public string BusinessNumber{get{return _BusinessNumber;}set{_BusinessNumber = value;}}public string BusinessNumberType{get{return _BusinessNumberType;}set{_BusinessNumberType = value;}}public string[][] TextMatrix{get{return _TextMatrix;}set{_TextMatrix = value;}}public int[][] Int2DJagged{get{return _Int2DJagged;}set{_Int2DJagged = value;}}public int[,] Int2D{get{return _Int2D;}set{_Int2D = value;}}public string[] Lines{get{return _Lines;}set{_Lines = value;}}}public class MyPeopleDic : object{private System.Collections.Generic.Dictionary<string, DemoWebApi.DemoData.Client.Person> _Dic;private System.Collections.Generic.Dictionary<string, string> _AnotherDic;private System.Collections.Generic.Dictionary<int, string> _IntDic;public System.Collections.Generic.Dictionary<string, DemoWebApi.DemoData.Client.Person> Dic{get{return _Dic;}set{_Dic = value;}}public System.Collections.Generic.Dictionary<string, string> AnotherDic{get{return _AnotherDic;}set{_AnotherDic = value;}}public System.Collections.Generic.Dictionary<int, string> IntDic{get{return _IntDic;}set{_IntDic = value;}}}
}
namespace DemoWebApi.DemoData.Another.Client
{ public struct MyPoint{public double X;public double Y;}
}public partial class Entities{private System.Net.Http.HttpClient client;private System.Uri baseUri;public Entities(System.Net.Http.HttpClient client, System.Uri baseUri){if (client == null)throw new ArgumentNullException("client", "Null HttpClient.");if (baseUri == null)throw new ArgumentNullException("baseUri", "Null baseUri");this.client = client;this.baseUri = baseUri;}/// <summary>////// PUT api/SuperDemo/link?id={id}&relationship={relationship}/// </summary>public async Task<bool> LinkPersonAsync(long id, string relationship, DemoWebApi.DemoData.Client.Person person){var requestUri = this.baseUri + "api/SuperDemo/link?id="+id+"&relationship="+relationship;using (var requestWriter = new System.IO.StringWriter()){var requestSerializer = JsonSerializer.Create();requestSerializer.Serialize(requestWriter, person);var content = new StringContent(requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");var responseMessage = await client.PutAsync(requestUri, content);responseMessage.EnsureSuccessStatusCode();var stream = await responseMessage.Content.ReadAsStreamAsync();using (JsonReader jsonReader = new JsonTextReader(new System.IO.StreamReader(stream))){var serializer = new JsonSerializer();return System.Boolean.Parse(jsonReader.ReadAsString());}}}/// <summary>////// PUT api/SuperDemo/link?id={id}&relationship={relationship}/// </summary>public bool LinkPerson(long id, string relationship, DemoWebApi.DemoData.Client.Person person){var requestUri = this.baseUri + "api/SuperDemo/link?id="+id+"&relationship="+relationship;using (var requestWriter = new System.IO.StringWriter()){var requestSerializer = JsonSerializer.Create();requestSerializer.Serialize(requestWriter, person);var content = new StringContent(requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");var responseMessage = this.client.PutAsync(requestUri, content).Result;responseMessage.EnsureSuccessStatusCode();var stream = responseMessage.Content.ReadAsStreamAsync().Result;using (JsonReader jsonReader = new JsonTextReader(new System.IO.StreamReader(stream))){var serializer = new JsonSerializer();return System.Boolean.Parse(jsonReader.ReadAsString());}}}/// <summary>////// GET api/SuperDemo/Company?id={id}/// </summary>public async Task<DemoWebApi.DemoData.Client.Company> GetCompanyAsync(long id){var requestUri = this.baseUri + "api/SuperDemo/Company?id="+id;var responseMessage = await client.GetAsync(Uri.EscapeUriString(requestUri));responseMessage.EnsureSuccessStatusCode();var stream = await responseMessage.Content.ReadAsStreamAsync();using (JsonReader jsonReader = new JsonTextReader(new System.IO.StreamReader(stream))){var serializer = new JsonSerializer();return serializer.Deserialize<DemoWebApi.DemoData.Client.Company>(jsonReader);}}/// <summary>////// GET api/SuperDemo/Company?id={id}/// </summary>public DemoWebApi.DemoData.Client.Company GetCompany(long id){var requestUri = this.baseUri + "api/SuperDemo/Company?id="+id;var responseMessage = this.client.GetAsync(Uri.EscapeUriString(requestUri)).Result;responseMessage.EnsureSuccessStatusCode();var stream = responseMessage.Content.ReadAsStreamAsync().Result;using (JsonReader jsonReader = new JsonTextReader(new System.IO.StreamReader(stream))){var serializer = new JsonSerializer();return serializer.Deserialize<DemoWebApi.DemoData.Client.Company>(jsonReader);}}
如果希望某些外部开发人员使用您的Web API,则可以发布针对各种平台的C#客户端API代码或编译的库,以及由ASP.NET MVC框架生成的帮助页面。
使用生成的API代码
这是一个简单的例子:
var httpclient = new system.net.http.httpclient();
var api = new demowebapi.controllers.client.entities(httpclient, baseuri);
person person = new person()
{name = "some one",surname = "one",givenname = "some",birthdate = datetime.now.addyears(-20),addresses = new address[]{new address(){city="brisbane",state="qld",street1="somewhere",street2="over the rainbow",postalcode="4000",country="australia",type= addresstype.postal,location = new demowebapi.demodata.another.client.mypoint() {x=4, y=9 },}},
};var id = api.createperson(person);
在像Visual Studio这样的不错的文本编辑器中编写客户端代码时,您可能会得到很好的智能提示,因此您几乎不需要阅读Web API帮助页面。
支持通用Windows应用,Android应用和iOS应用
对于通用Windows应用,您可以创建如下客户端API库:
对于Android应用程序,您可能具有这样的客户端API项目,如Mono.Android:
对于iOS应用,您可以使用以下命令创建一个客户端API项目,如Xamarin.iOS:
提示
如果你想用相同的代码库为各种平台提供编译库,您可以创建一个符号链接到文件WebApiClientAuto.cs,其在文件夹DemoWebApi.ClientApi中生成。
如屏幕快照所示,单击“添加为链接 ”,您将在项目DemoWebApi.iOSClientApi中创建一个指向CS文件的符号链接。或者,您可以使用Shared Project,或更优选地使用.NET Standard项目。
优点总结
- 与ASP.NET Web API无缝集成,只需很少的步骤/开销即可在Web API和客户端API之间进行设置,维护和同步
- 支持所有内置类型,包括小数
- 支持DataTime,DataTimeOffset,Array,Tuple,动态对象,Dictionary并KeyValuePair
- 强类型生成的代码需要进行设计时类型检查和编译时类型检查
- 高抽象
- 智能感知
SDLC
因此,基本上,您可以制作包括API控制器和数据模型在内的Web API代码,然后执行CreateClientApi.ps1。就是这样。WebApiClientGen和CreateClientApi.ps1将为您完成其余工作。
团队合作
本节描述团队合作的一些基本方案。在不同的公司和团队中,情况和上下文可能有所不同,因此您应相应地调整团队实践。
您的团队有一个在Web API上工作的后端开发人员Brenda,以及在前端上工作的前端开发人员Frank。每台开发机器都正确设置了集成测试环境,因此,无需团队CI服务器就可以在每台开发机器上完成大多数CI工作。主干基本开发是默认的分支实践。
1个存储库,包括后端代码和前端代码
- Brenda编写了一些新的Web API代码,并进行了构建。
- Brenda执行CreateClientApi.ps1生成客户端代码。
- Brenda针对Web API编写并运行了一些基本的集成测试用例。
- Brenda将更改提交/推送到主开发分支或主干。
- Frank更新/拉动更改,构建并运行测试用例。
- Frank基于新的Web API和客户端API开发了新的前端功能。
1个后端存储库和1个前端存储库
Brenda调整了CodeGen.json,它将把生成的代码定向到前端存储库工作文件夹中的客户端API文件夹。
- Brenda编写了一些新的Web API代码,并进行了构建。
- Brenda执行CreateClientApi.ps1生成客户端代码。
- Brenda针对Web API编写并运行了一些基本的集成测试用例。
- Brenda将更改提交/推送到主开发分支或两个存储库的主干。
- Frank使用两个存储库更新/拉出更改,构建并运行测试用例。
- Frank基于新的Web API和客户端API开发了新的前端功能。
兴趣点
虽然ASP.NET MVC和Web API将NewtonSoft.Json用于JSON应用程序,但NewtonSoft.Json可以很好地处理由DataContractAttribute装饰的POCO类。
通过添加后缀“Client”将CLR名称空间转换为客户端名称空间。例如,名称空间My.Name.space将转换为My.Name.space.Client。
从某种角度来看,服务名称空间/函数名称与客户端名称空间/函数名称之间的一对一映射公开了服务的实现细节,通常不建议这样做。但是,传统的RESTful客户端编程要求程序员注意服务函数的URL查询模板,并且查询模板包含服务的实现细节。因此,这两种方法都在某种程度上(一种或另一种)公开了服务的实现细节。
对于客户端应用程序开发人员,经典的函数原型如下:
ReturnType DoSomething(Type1 t1, Type2 t2 ...)
是API函数,其余是传输的技术实现细节:TCP / IP,HTTP,SOAP,面向资源,基于CRUD的URI,RESTful,XML和JSON等。函数原型和一段API文档应该足以调用API函数。至少在操作成功时,客户端应用程序开发人员不必关心传输的那些实现细节。仅当出现错误时,开发人员才需要关心处理错误的技术细节。例如,在基于SOAP的Web服务中,您必须了解SOAP错误。在RESTful Web服务中,您可能必须处理HTTP状态代码。
而且查询模板几乎没有提供API函数的语义含义。相比之下,WebApiClientGen以服务函数命名客户端函数,就像默认情况下WCF中的SvcUtil.exe一样,因此只要服务开发人员以良好的语义名称命名服务函数,生成的客户端函数就具有良好的语义。
在同时涵盖服务开发和客户端开发的SDLC全景图中,服务开发人员具有服务函数的语义含义,通常在函数描述之后命名函数是一种良好的编程习惯。面向资源的CRUD可能具有语义含义,或者仅仅是函数描述的技术翻译。
WebApiClientGen 将文档注释复制到生成的C#代码中,因此您几乎不需要阅读由MVC生成的帮助页面,并且使用该服务的客户端编程将变得更加无缝。
提示
对于持续集成,编写脚本以完全自动化某些步骤并不难。
Swashbuckle +AutoRest VS. WebApiClientGen
Swashbukle通过阅读Web API的ApiExplorer生成Swagger元(meta),因此不同平台的客户端程序员可以使用相应的语言生成客户端API。
AutoRest读取Swagger元数据并使用C#和JavaScript生成客户端API,并且可以肯定的是,只要服务可以提供Swagger元数据,它就可以为以任何编程语言编码的RESTful Web服务生成客户端API。
因此,Swashbukle + AutoRest 几乎可以提供WebApiClientGen所提供的服务。
WebApiClientGen通过阅读ApiExplorer生成C#和TypeScript中的客户端API代码,但不涉及Swagger元(meta)。它使用起来更简单、更有效,并且涵盖了更多的数据类型,例如用于货币计算的.NET十进制类型和Tuple类型等。并且Swagger不支持泛型、Tuple和.NET的十进制类型。
在较高级别的,Swagger适用于“模型优先”方法,而WebApiClient适用于“代码优先”方法。Swashbuckle + AutoRest尝试支持“代码优先”方法,但要进行一些额外的操作。
WebApiClientGen不能完全取代Swashbukle和AutoRest。如果您正在开发Web API,并且您将以C#和为您自己、您的团队或外部团队生成的TypeScript格式提供客户端API库,那么WebApiClientGen将更加无缝、直接和全面,但是SDLC的开销更少。
如果您需要支持PHP,Java,JavaScript,C#和C ++等客户端API,并针对Swagger的不足来调整Web API设计,那么您可能会发现Swagger及其附件可能更可行。
简而言之,Swagger工具链支持更广泛的格局,因此在SDLC期间具有更多开销,而支持的数据类型较少,而WebApiClientGen针对ASP.NET Web API进行了优化,因此在SDLC期间具有更少的开销,并且支持的数据类型也更多。
生成用于ASP.NET Web API的C#客户端API相关推荐
- 生成用于ASP.NET Web API的TypeScript客户端API
目录 介绍 备注 背景 推定(Presumptions) 使用代码 步骤0:将NuGet package WebApiClientGen和WebApiClientGen.jQuery安装到Web AP ...
- api 原生hbase_HBase客户端API
文章来源:加米谷大数据 本文介绍用于对HBase表上执行CRUD操作的HBase Java客户端API. HBase是用Java编写的,并具有Java原生API.因此,它提供了编程访问数据操纵语言(D ...
- sqlserv已生成用于更新的结果集。_ML.NET API 和工具八月更新
ML.NET是一个开源的跨平台机器学习框架,适合 .NET 开发人员.它允许将机器学习集成到 .NET 应用中,而无需离开 .NET 生态系统.ML.NET工具(Visual Studio 中的模型生 ...
- ASP.NET Web API 安全筛选器
原文:https://msdn.microsoft.com/zh-cn/magazine/dn781361.aspx 身份验证和授权是应用程序安全的基础.身份验证通过验证提供的凭据来确定用户身份,而授 ...
- asp.net web services
现在Internet正在不断地发展着,在互联网应用刚开始的时候,我们浏览的网页只是静态的,不可交互的.而现在随着技术的日益发展,将提供给网页浏览者一个可编程的Web 站点.这些站点将在组织.应用.服务 ...
- sqoop2 java api实现_Sqoop2 Java客户端API指南
原文连接:http://sqoop.apache.org/docs/1.99.6/ClientAPI.html Sqoop Java客户端API指南 这篇文章秒描述了额如何在外部应用中使用sqoop ...
- ASP.NET Web API 使用Swagger生成在线帮助测试文档
ASP.NET Web API 使用Swagger生成在线帮助测试文档 Swagger-UI简单而一目了然.它能够纯碎的基于html+javascript实现,只要稍微整合一下便能成为方便的API在线 ...
- 【转】ASP.NET Web API 使用Swagger生成在线帮助测试文档,支持多个GET
以下为教程: 在现有webapi项目中,nuget安装以下两个插件 swagger.net.ui swashbuckle 安装完毕后可以卸载Swagger.NET,此处不需要! 安装完毕后屏蔽以下代码 ...
- WCF 和 ASP.NET Web API
地址:https://docs.microsoft.com/zh-cn/dotnet/framework/wcf/wcf-and-aspnet-web-api WCF 是 Microsoft 为生成面 ...
最新文章
- 【二级java】排序技术
- CodeBlocks 更改 gui 程序为 命令行
- 网站性能优化之HTTP请求过程简述!
- ajax加php实现三级联动
- oracle字段加约束,Oracle数据库的字段约束创建和维护示例
- 一键换ip命令_软网推荐:高效命令行备份更简单
- ruby设计模式之观察者模式2————更加一般化的观察者模式
- a^x ≡1(mod n) Ord_n(a)=x什么意思
- 【spring】spring_IOC和DI
- python语言程序设计实践教程答案实验五_Python程序设计实验五
- 增量式PID公式的4点疑问和理解
- 小米mix2安兔兔html5跑分,vivo X21跑分多少?高通骁龙660 AIE安兔兔跑分实测
- u盘计算机里读不出来怎么修复,u盘读不出来怎么办?各种U盘无法显示无法读取修复方案...
- 简单理解格拉姆矩阵(Gram matrix)
- CentOS 7.2设置中英文环境
- 教资篇(1)—高中信息技术科目
- 号外 ! 号外 ! V7包下的View都来此参加同学会 , 快来看,快来看...
- Android传感器(三):方向传感器
- leetcode题解-647. Palindromic Substrings 5. Longest Palindromic Substring
- 自媒体人想要写好实时热点文章,一定要掌握好这三点
热门文章
- linnux 流量控制模块tc_FS4008-40-08-CV-A气体质量流量计【汉川仪器】阿坝资讯
- mysql function_MySQL基础函数——数学函数详解
- 设计师必备超人气设计素材网站
- 买买买!!!电商促销必备的气氛PSD分层海报
- 纯文字极简风格平面海报,PSD分层模板!
- 个性艺术创意风采人物海报,psd分层模板
- 新手做UI?手里有几种常见的界面套路模板素材,你就成功一大半了!
- UI设计实用排版法则,优秀可临摹案例,剖析设计实例
- 博学谷html css,博学谷 - CSS笔记12 - 清除浮动
- Python--tkinter迷你天气预报软件(11.8)