目录
介绍
主要特征
主要好处
背景
推定(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
用于开发ASP.NET Web API或ASP.NET Core Web API的客户端程序, 强类型客户端API生成器以C#代码和TypeScript代码生成强类型客户端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的美好时光。:)应该减少客户端编程的开销。
因此,我进行了搜索并试图找到一些解决方案,这些解决方案可以使我摆脱编写原始和重复的代码的麻烦,因此我可以专注于在客户端构建业务逻辑。以下是协助客户程序开发的开源项目列表:
虽然这些解决方案可以生成强类型的客户端代码并在某种程度上减少重复的任务,但我发现它们都无法给我带来我所期望的所有流畅而高效的编程经验:
这是WebApiClientGen。
为了跟进这种开发客户端程序的新方法,最好拥有一个ASP.NET Web API项目或一个包含Web API的MVC项目。您可以使用现有项目,也可以创建一个演示项目。
安装还将安装相关的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 ApiExplorer
public class CodeGenController : ApiController
{
///
/// Trigger the API to generate WebApiClientAuto.cs
/// for an established client API project.
///
///
/// OK if OK
[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
备注
确保引用了以下软件包:
如此屏幕截图如下:
您的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; }
///
/// Foreign key to Entity
///
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();
}
[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 this
public string Name { get; set; }
[DataMember]
public IList 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 Lines;
}
...
...
namespace DemoWebApi.Controllers
{
[RoutePrefix("api/SuperDemo")]
public class EntitiesController : ApiController
{
///
/// Get a person
///
/// unique id of that guy
/// person in db
[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#客户端代理类,如以下文档注释中所述:
///
/// Flagged options for cherry picking in various development processes.
///
[Flags]
public enum CherryPickingMethods
{
///
/// Include all public classes, properties and properties.
///
All = 0,
///
/// Include all public classes decorated by DataContractAttribute,
/// and public properties or fields decorated by DataMemberAttribute.
/// And use DataMemberAttribute.IsRequired
///
DataContract =1,
///
/// Include all public classes decorated by JsonObjectAttribute,
/// and public properties or fields decorated by JsonPropertyAttribute.
/// And use JsonPropertyAttribute.Required
///
NewtonsoftJson = 2,
///
/// 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.
///
Serializable = 4,
///
/// Include all public classes, properties and properties.
/// And use System.ComponentModel.DataAnnotations.RequiredAttribute.
///
AspNet = 8,
}
默认选项是“选择加入”的DataContract。您可以使用任何一种方法或方法的组合。
在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。这减少了持续集成的大量开销。
此序列图说明了开发周期:
完成这些步骤之后,现在您已经生成了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 _BirthDate;
public string Surname
{
get
{
return _Surname;
}
set
{
_Surname = value;
}
}
public string GivenName
{
get
{
return _GivenName;
}
set
{
_GivenName = value;
}
}
public System.Nullable 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
_Dic;
private System.Collections.Generic.Dictionary _AnotherDic;
private System.Collections.Generic.Dictionary _IntDic;
public System.Collections.Generic.Dictionary
Dic
{
get
{
return _Dic;
}
set
{
_Dic = value;
}
}
public System.Collections.Generic.Dictionary AnotherDic
{
get
{
return _AnotherDic;
}
set
{
_AnotherDic = value;
}
}
public System.Collections.Generic.Dictionary 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;
}
///
///
/// PUT api/SuperDemo/link?id={id}&relationship={relationship}
///
public async Task 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());
}
}
}
///
///
/// PUT api/SuperDemo/link?id={id}&relationship={relationship}
///
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());
}
}
}
///
///
/// GET api/SuperDemo/Company?id={id}
///
public async Task 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(jsonReader);
}
}
///
///
/// GET api/SuperDemo/Company?id={id}
///
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(jsonReader);
}
}
如果希望某些外部开发人员使用您的Web API,则可以发布针对各种平台的C#客户端API代码或编译的库,以及由ASP.NET MVC框架生成的帮助页面。
这是一个简单的例子:
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应用,您可以创建如下客户端API库:
对于Android应用程序,您可能具有这样的客户端API项目,如Mono.Android:
对于iOS应用,您可以使用以下命令创建一个客户端API项目,如Xamarin.iOS:
提示
如果你想用相同的代码库为各种平台提供编译库,您可以创建一个符号链接到文件WebApiClientAuto.cs,其在文件夹DemoWebApi.ClientApi中生成。
如屏幕快照所示,单击“添加为链接 ”,您将在项目DemoWebApi.iOSClientApi中创建一个指向CS文件的符号链接。或者,您可以使用Shared Project,或更优选地使用.NET Standard项目。
因此,基本上,您可以制作包括API控制器和数据模型在内的Web API代码,然后执行CreateClientApi.ps1。就是这样。WebApiClientGen和CreateClientApi.ps1将为您完成其余工作。
本节描述团队合作的一些基本方案。在不同的公司和团队中,情况和上下文可能有所不同,因此您应相应地调整团队实践。
您的团队有一个在Web API上工作的后端开发人员Brenda,以及在前端上工作的前端开发人员Frank。每台开发机器都正确设置了集成测试环境,因此,无需团队CI服务器就可以在每台开发机器上完成大多数CI工作。主干基本开发是默认的分支实践。
1个存储库,包括后端代码和前端代码
1个后端存储库和1个前端存储库
Brenda调整了CodeGen.json,它将把生成的代码定向到前端存储库工作文件夹中的客户端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生成的帮助页面,并且使用该服务的客户端编程将变得更加无缝。
提示
对于持续集成,编写脚本以完全自动化某些步骤并不难。
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期间具有更少的开销,并且支持的数据类型也更多。