65.9K
CodeProject 正在变化。 阅读更多。
Home

OpenApiClientGen,用于基于 Open API 定义生成强类型客户端 API 代码

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2020年7月3日

CPOL

4分钟阅读

viewsIcon

11405

引言

OpenAPI Client Generators 是一个.NET Core命令行程序,用于在.NET Frameworks和.NET Core中生成C#强类型客户端API代码,以及为Angular 5+、Aurelia、jQuery、AXIOS和Fetch API生成TypeScript代码。

假定您已经具备Swagger/Open API规范以及相应客户端应用程序开发的丰富知识和经验。

OpenApiClientGen 是.NET应用程序开发人员通常使用的以下工具之外的一个替代解决方案:

这些都是不错的工具,覆盖的范围比OpenApiClientGen更广。本文旨在概述它们之间的区别,以便您在合适的场景下选择正确的工具。

背景

OpenApiClientGen 的开发基于 WebApiClientGen 的一些核心组件,并共享相同的基本设计原则。因此,生成的客户端代码也具有相同的特性。

使用代码

如何生成

当不带参数运行Fonlow.OpenApiClientGen.exe时,您将看到以下提示:

Parameter 1: Open API YAML/JSON definition file
Parameter 2: Settings file in JSON format.
Example: 
  Fonlow.OpenApiClientGen.exe my.yaml
  Fonlow.OpenApiClientGen.exe my.yaml myproj.json
  Fonlow.OpenApiClientGen.exe my.yaml ..\myproj.json</code>

典型的CodeGen JSON文件如下所示: "DemoCodeGen.json"

{
	"ClientNamespace": "My.Pet.Client",
	"ClientLibraryProjectFolderName": "./Tests/DemoClientApi",
	"ContainerClassName": "PetClient",
	"ClientLibraryFileName": "PetAuto.cs",
	"ActionNameStrategy": 4,
	"UseEnsureSuccessStatusCodeEx": true,
	"DecorateDataModelWithDataContract": true,
	"DataContractNamespace": "http://pet.domain/2020/03",
	"DataAnnotationsEnabled": true,
	"DataAnnotationsToComments": true,
	"HandleHttpRequestHeaders": true,

	"Plugins": [
		{
			"AssemblyName": "Fonlow.OpenApiClientGen.NG2",
			"TargetDir": "./ng2/src/clientapi",
			"TSFile": "ClientApiAuto.ts",
			"AsModule": true,
			"ContentType": "application/json;charset=UTF-8"
		}
	]

}

基于 pet.yaml 生成的 C# 代码示例

    /// <summary>
    /// A representation of a cat
    /// </summary>
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Cat : Pet
    {
        
        /// <summary>
        /// The measured skill for hunting
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="huntingSkill")]
        public CatHuntingSkill HuntingSkill { get; set; } = CatHuntingSkill.lazy;
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public enum CatHuntingSkill
    {
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        clueless = 0,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        lazy = 1,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        adventurous = 2,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        aggressive = 3,
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Category
    {
        
        /// <summary>
        /// Category ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Category name
        /// Min length: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="name")]
        [System.ComponentModel.DataAnnotations.StringLength(int.MaxValue, MinimumLength=1)]
        public string Name { get; set; }
        
        /// <summary>
        /// Test Sub Category
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="sub")]
        public CategorySub Sub { get; set; }
    }
    
    public class CategorySub
    {
        
        /// <summary>
        /// Dumb Property
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="prop1")]
        public string Prop1 { get; set; }
    }
    
    /// <summary>
    /// A representation of a dog
    /// </summary>
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Dog : Pet
    {
        
        /// <summary>
        /// The size of the pack the dog is from
        /// Minimum: 1
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="packSize")]
        [System.ComponentModel.DataAnnotations.Range(1, System.Int32.MaxValue)]
        public int PackSize { get; set; } = 1;
    }
    
    /// <summary>
    /// A representation of a honey bee
    /// </summary>
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class HoneyBee : Pet
    {
        
        /// <summary>
        /// Average amount of honey produced per day in ounces
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="honeyPerDay")]
        public float HoneyPerDay { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Order
    {
        
        /// <summary>
        /// Order ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Pet ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="petId")]
        public System.Nullable<System.Int64> PetId { get; set; }
        
        /// <summary>
        /// Minimum: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="quantity")]
        [System.ComponentModel.DataAnnotations.Range(1, System.Int32.MaxValue)]
        public System.Nullable<System.Int32> Quantity { get; set; }
        
        /// <summary>
        /// Estimated ship date
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="shipDate")]
        public System.Nullable<System.DateTimeOffset> ShipDate { get; set; }
        
        /// <summary>
        /// Order Status
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="status")]
        public OrderStatus Status { get; set; }
        
        /// <summary>
        /// Indicates whenever order was completed or not
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="complete")]
        public System.Nullable<System.Boolean> Complete { get; set; }
        
        /// <summary>
        /// Unique Request Id
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="requestId")]
        public string RequestId { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public enum OrderStatus
    {
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        placed = 0,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        approved = 1,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        delivered = 2,
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Pet
    {
        
        /// <summary>
        /// Pet ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Categories this pet belongs to
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="category")]
        public Category Category { get; set; }
        
        /// <summary>
        /// The name given to a pet
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="name")]
        public string Name { get; set; }
        
        /// <summary>
        /// The list of URL to a cute photos featuring pet
        /// Maximum items: 20
        /// </summary>
        [System.ComponentModel.DataAnnotations.Required()]
        [System.Runtime.Serialization.DataMember(Name="photoUrls")]
        [System.ComponentModel.DataAnnotations.MaxLength(20)]
        public string[] PhotoUrls { get; set; }
        
        [System.Runtime.Serialization.DataMember(Name="friend")]
        public Pet Friend { get; set; }
        
        /// <summary>
        /// Tags attached to the pet
        /// Minimum items: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="tags")]
        [System.ComponentModel.DataAnnotations.MinLength(1)]
        public Tag[] Tags { get; set; }
        
        /// <summary>
        /// Pet status in the store
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="status")]
        public PetStatus Status { get; set; }
        
        /// <summary>
        /// Type of a pet
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="petType")]
        public string PetType { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public class Tag
    {
        
        /// <summary>
        /// Tag ID
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="id")]
        public System.Nullable<System.Int64> Id { get; set; }
        
        /// <summary>
        /// Tag name
        /// Min length: 1
        /// </summary>
        [System.Runtime.Serialization.DataMember(Name="name")]
        [System.ComponentModel.DataAnnotations.StringLength(int.MaxValue, MinimumLength=1)]
        public string Name { get; set; }
    }
    
    [System.Runtime.Serialization.DataContract(Name="http://pet.domain/2020/03")]
    public enum PetStatus
    {
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        available = 0,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        pending = 1,
        
        [System.Runtime.Serialization.EnumMemberAttribute()]
        sold = 2,
    }
    
    public partial class PetClient
    {        
        private System.Net.Http.HttpClient client;
        
        private JsonSerializerSettings jsonSerializerSettings;
        
        public PetClient(System.Net.Http.HttpClient client, 
                         JsonSerializerSettings jsonSerializerSettings=null)
        {
            if (client == null)
                throw new ArgumentNullException("Null HttpClient.", "client");

            if (client.BaseAddress == null)
                throw new ArgumentNullException("HttpClient has no BaseAddress", "client");

            this.client = client;
            this.jsonSerializerSettings = jsonSerializerSettings;
        }
        
        /// <summary>
        /// Add a new pet to the store
        /// Add new pet to the store inventory.
        /// AddPet pet
        /// </summary>
        /// <param name="requestBody">Pet object that needs to be added to the store</param>
        public async Task AddPetAsync(Pet requestBody, 
               Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet";
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri))
            {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create(jsonSerializerSettings);
            requestSerializer.Serialize(requestWriter, requestBody);
            var content = new StringContent(requestWriter.ToString(), 
                          System.Text.Encoding.UTF8, "application/json");
            httpRequestMessage.Content = content;
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
            }
        }
        
        /// <summary>
        /// Update an existing pet
        /// UpdatePet pet
        /// </summary>
        /// <param name="requestBody">Pet object that needs to be added to the store</param>
        public async Task UpdatePetAsync(Pet requestBody, 
          Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet";
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, requestUri))
            {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create(jsonSerializerSettings);
            requestSerializer.Serialize(requestWriter, requestBody);
            var content = new StringContent
            (requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");
            httpRequestMessage.Content = content;
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
            }
        }
        
        /// <summary>
        /// Find pet by ID
        /// Returns a single pet
        /// GetPetById pet/{petId}
        /// </summary>
        /// <param name="petId">ID of pet to return</param>
        /// <returns>successful operation</returns>
        public async Task<Pet> GetPetByIdAsync
        (long petId, Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet/"+petId;
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri))
            {
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
                var stream = await responseMessage.Content.ReadAsStreamAsync();
                using (JsonReader jsonReader = new JsonTextReader
                                  (new System.IO.StreamReader(stream)))
                {
                var serializer = new JsonSerializer();
                return serializer.Deserialize<Pet>(jsonReader);
                }
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
        }
        
        /// <summary>
        /// Deletes a pet
        /// DeletePet pet/{petId}
        /// </summary>
        /// <param name="petId">Pet id to delete</param>
        public async Task DeletePetAsync
        (long petId, Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet/"+petId;
            using (var httpRequestMessage = 
                   new HttpRequestMessage(HttpMethod.Delete, requestUri))
            {
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
        }
        
        /// <summary>
        /// Finds Pets by status
        /// Multiple status values can be provided with comma separated strings
        /// FindPetsByStatus pet/findByStatus
        /// </summary>
        /// <param name="status">Status values that need to be considered for filter</param>
        /// <returns>successful operation</returns>
        public async Task<Pet[]> FindPetsByStatusAsync(PetStatus[] status, 
               Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {
            var requestUri = "pet/findByStatus?"+String.Join
                             ("&", status.Select(z => $"status={z}"));
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri))
            {
            if (handleHeaders != null)
            {
                handleHeaders(httpRequestMessage.Headers);
            }

            var responseMessage = await client.SendAsync(httpRequestMessage);
            try
            {
                responseMessage.EnsureSuccessStatusCodeEx();
                var stream = await responseMessage.Content.ReadAsStreamAsync();
                using (JsonReader jsonReader = 
                       new JsonTextReader(new System.IO.StreamReader(stream)))
                {
                var serializer = new JsonSerializer();
                return serializer.Deserialize<Pet[]>(jsonReader);
                }
            }
            finally
            {
                responseMessage.Dispose();
            }
            }
        }

正如您可能看到的,生成的代码比其他工具生成的代码看起来更简洁,但提供了相似的数据处理和错误处理能力。更重要的是,数据类型的匹配更加全面和精确。

为 Angular 5+ 生成的 TypeScript 代码示例

import { Injectable, Inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
export namespace My_Pet_Client {
    export interface ApiResponse {
        code?: number;
        type?: string;
        message?: string;
    }


    /** A representation of a cat */
    export interface Cat extends Pet {

        /** The measured skill for hunting */
        huntingSkill: CatHuntingSkill;
    }

    export enum CatHuntingSkill { clueless = 0, lazy = 1, adventurous = 2, aggressive = 3 }

    export interface Category {

        /** Category ID */
        id?: number;

        /**
         * Category name
         * Min length: 1
         */
        name?: string;

        /** Test Sub Category */
        sub?: CategorySub;
    }

    export interface CategorySub {

        /** Dumb Property */
        prop1?: string;
    }


    /** A representation of a dog */
    export interface Dog extends Pet {

        /**
         * The size of the pack the dog is from
         * Minimum: 1
         */
        packSize: number;
    }


    /** A representation of a honey bee */
    export interface HoneyBee extends Pet {

        /** Average amount of honey produced per day in ounces */
        honeyPerDay: number;
    }

    export interface Order {

        /** Order ID */
        id?: number;

        /** Pet ID */
        petId?: number;
        quantity?: number;

        /** Estimated ship date */
        shipDate?: Date;

        /** Order Status */
        status?: OrderStatus;

        /** Indicates whenever order was completed or not */
        complete?: boolean;

        /** Unique Request Id */
        requestId?: string;
    }

    export enum OrderStatus { placed = 0, approved = 1, delivered = 2 }

    export interface Pet {

        /** Pet ID */
        id?: number;

        /** Categories this pet belongs to */
        category?: Category;

        /** The name given to a pet */
        name: string;

        /**
         * The list of URL to a cute photos featuring pet
         * Maximum items: 20
         */
        photoUrls: Array<string>;
        friend?: Pet;

        /**
         * Tags attached to the pet
         * Minimum items: 1
         */
        tags?: Array<Tag>;

        /** Pet status in the store */
        status?: PetStatus;

        /** Type of a pet */
        petType?: string;
    }

    export interface Tag {

        /** Tag ID */
        id?: number;

        /**
         * Tag name
         * Min length: 1
         */
        name?: string;
    }

    export enum PetStatus { available = 0, pending = 1, sold = 2 }

    @Injectable()
    export class PetClient {
        constructor(@Inject('baseUri') private baseUri: 
        string = location.protocol + '//' + location.hostname + 
        (location.port ? ':' + location.port : '') + '/', private http: HttpClient) {
        }

        /**
         * Add a new pet to the store
         * Add new pet to the store inventory.
         * Post pet
         * @param {Pet} requestBody Pet object that needs to be added to the store
         * @return {void} 
         */
        AddPet(requestBody: Pet, headersHandler?: () => HttpHeaders): 
                                 Observable<HttpResponse<string>> {
            return this.http.post(this.baseUri + 'pet', 
                   JSON.stringify(requestBody), { headers: headersHandler ? 
                   headersHandler().append('Content-Type', 'application/json;charset=UTF-8') :
                   new HttpHeaders({ 'Content-Type': 'application/json;charset=UTF-8' }), 
                   observe: 'response', responseType: 'text' });
        }

        /**
         * Update an existing pet
         * Put pet
         * @param {Pet} requestBody Pet object that needs to be added to the store
         * @return {void} 
         */
        UpdatePet(requestBody: Pet, headersHandler?: () => HttpHeaders): 
                  Observable<HttpResponse<string>> {
            return this.http.put(this.baseUri + 'pet', JSON.stringify(requestBody), 
                   { headers: headersHandler ? headersHandler().append
                   ('Content-Type', 'application/json;charset=UTF-8') : 
                   new HttpHeaders({ 'Content-Type': 'application/json;charset=UTF-8' }), 
                   observe: 'response', responseType: 'text' });
        }

        /**
         * Find pet by ID
         * Returns a single pet
         * Get pet/{petId}
         * @param {number} petId ID of pet to return
         * @return {Pet} successful operation
         */
        GetPetById(petId: number, headersHandler?: () => HttpHeaders): Observable<Pet> {
            return this.http.get<Pet>(this.baseUri + 'pet/' + petId, 
                   { headers: headersHandler ? headersHandler() : undefined });
        }

        /**
         * Deletes a pet
         * Delete pet/{petId}
         * @param {number} petId Pet id to delete
         * @return {void} 
         */
        DeletePet(petId: number, headersHandler?: () => HttpHeaders): 
                  Observable<HttpResponse<string>> {
            return this.http.delete(this.baseUri + 'pet/' + petId, 
                   { headers: headersHandler ? headersHandler() : undefined, 
                   observe: 'response', responseType: 'text' });
        }

        /**
         * Finds Pets by status
         * Multiple status values can be provided with comma separated strings
         * Get pet/findByStatus
         * @param {Array<PetStatus>} status Status values that need to be 
         * considered for filter
         * @return {Array<Pet>} successful operation
         */
        FindPetsByStatus(status: Array<PetStatus>, headersHandler?: () => 
                         HttpHeaders): Observable<Array<Pet>> {
            return this.http.get<Array<Pet>>(this.baseUri + 'pet/findByStatus?' + 
                   status.map(z => `status=${z}`).join('&'), 
                   { headers: headersHandler ? headersHandler() : undefined });
        }

优化了什么

强类型的API和客户端API

强类型的Web API为具有复杂语义建模和工作流程的业务应用程序提供了更好的支持。客户端API通过强类型数据更好地反映了这些语义建模。与WebApiClientGen类似,OpenApiClientGen可以尽可能精确地翻译Open API定义中的组件和数据类型。更多详情,请参阅

远程过程调用

Swagger/Open API规范假定RESTful设计,而WebApiClientGen及其衍生产品OpenApiClientGen则针对RPC进行了优化。

从某种意义上说,REST是一种构建RPC的规范或受限方式。对于构建复杂的业务应用程序,REST可能对整体开发有利,但也可能过于技术化,迫使开发人员将高级业务逻辑翻译成REST,而不是直接进行业务领域建模。

“考虑一下软件项目开始采用最新的架构设计潮流,而直到后来才发现系统需求是否需要这种架构。”

参考文献

不支持的内容

不支持的内容是出于设计考虑。本文对此进行了简要说明。

HTTP请求头作为参数

HTTP请求头通常用于两个目的:

  1. Authorization
  2. 与业务模型语义无关的相关ID和其他元数据

像NSwag这样的工具会生成包含请求头作为参数的客户端API代码,如果Open API定义将请求头包含为参数的话。如果您喜欢这种方式,可以忽略OpenApiClientGen,继续使用NSwag或类似工具。

根据 mcp.yaml,NSwag生成了 这样的函数原型

        public System.Threading.Tasks.Task<PatientClaimInteractiveResponseType> McpPatientclaiminteractiveGeneralV1Async(PatientClaimInteractiveRequestType body, string authorization, string dhs_auditId, string dhs_subjectId, string dhs_messageId, string dhs_auditIdType, string dhs_correlationId, string dhs_productId, string dhs_subjectIdType)
        {

 

OpenApiClientGen生成了一个 更简洁的函数原型

        public async Task<PatientClaimInteractiveResponseType> McpPatientClaimInteractiveGeneralAsync(PatientClaimInteractiveRequestType requestBody, Action<System.Net.Http.Headers.HttpRequestHeaders> handleHeaders = null)
        {

没有将请求头作为函数参数,客户端API函数看起来更干净,客户端应用程序代码结构更清晰,您可以更专注于业务逻辑,而不是请求头的技术细节。

对于授权,通常会使用HTTP拦截。而.NET HttpClient、Angular Http服务和JQuery Ajax等都提供了对HTTP拦截的内置支持。

要处理请求头(如果Open API定义了某些请求头),您可以在生成客户端API代码时,在CodeGen.json中包含"HandleHttpRequestHeaders": true,这将在客户端API函数的参数列表末尾添加一个回调函数。

其他

请参阅 wiki

关注点

如果您正在开发ASP.NET Web API或.NET Core Web API,则不需要OpenApiClientGen,因为WebApiClientGen可以在不涉及Swagger/Open API规范的情况下为C#、Angular 2+、Aurelia、jQuery、AXIOS和Fetch API生成多组客户端API。您可能会对以下文章更感兴趣:

  1. 为ASP.NET Web API生成C# .NET客户端API
  2. 为 ASP.NET Web API 生成 TypeScript 客户端 API
  3. ASP.NET Web API、Angular2、TypeScript 和 WebApiClientGen
  4. 为 ASP.NET Core Web API 生成 C# 客户端 API
  5. WebApiClientGen vs Swashbuckle 加上 NSwag

备注

OpenApiClientGen 的开发始于 WebApiClientGen vs Swashbuckle plus NSwag 的发布之后,而WebApiClientGenOpenApiClientGen共享一些关键组件和设计理念。

该工具已经 针对1000多个Open API定义进行了测试

让生成的代码通过编译不足以证明其有效性。您不能假设为相应Web API服务生成的客户端API是正确的,除非您 **构建了与Web API服务交互的集成测试套件**。

为其他JavaScript库和框架扩展OpenApiClientGen

与其他Open API / Swagger客户端代码生成器相比,OpenApiClientGen 更易于扩展。如果您的首选JavaScript库和框架未包含在内,您可以像 ClientApiTsNG2FunctionGen ControllersTsNG2ClientApiGen 一样,通过扩展ClientApiTsFunctionGenBase ControllersTsClientApiGenBase来实现。如您所见,每个插件的代码量仅约200行。

历史

  • 2020 年 7 月 3 日:初始版本
© . All rights reserved.