【问题标题】:.NET 5 - SWAGGER.json.NET 5 - 招摇.json
【发布时间】:2021-03-24 10:50:41
【问题描述】:

我为我的公共 api 生成了一个 swagger.json 文件。使用以下代码效果非常好:

在启动中 - 配置服务

            //Register Swagger Options as Dependency Injection
        services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
        
        services.AddSwaggerGen(options =>
        {
            // add a custom operation filter which sets default values
            //options.OperationFilter<SwaggerDefaultValues>();

            var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
            options.IncludeXmlComments(xmlPath);

        });

在启动中 - 配置

app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.InjectStylesheet("../css/customize-swagger-ui.css");
            c.InjectJavascript("../js/customize-swagger-ui.js");
            // build a swagger endpoint for each discovered API version
            foreach (var description in provider.ApiVersionDescriptions)
            {
                c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());

            }

        });

我的问题是,我想在 API-Request 后面添加方法的名称。否则,用于客户端代码生成的 NSWAG 等工具会创建许多不同的方法,称为 APIRoute1、APIRoute2、APIRoute3...

这里是swagger生成文件的json-sn-p:

"paths": {
"/api/v1/Order": {
  "post": {
    "tags": [
      "Order"
    ],
    "summary": "Creates a new Order if no order currently exists",
    "requestBody": {
      "description": "",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/CreateOrderModel"
          }
        },
        "text/json": {
          "schema": {
            "$ref": "#/components/schemas/CreateOrderModel"
          }
        },
        "application/*+json": {
          "schema": {
            "$ref": "#/components/schemas/CreateOrderModel"
          }
        }
      }
    },
    "responses": {
      "200": {
        "description": "Success"
      }
    }
  }
},
"/api/v1/Order/{orderNumber}": {
  "get": {
    "tags": [
      "Order"
    ],
    "summary": "Reads a given Order if exists",
    "parameters": [
      {
        "name": "orderNumber",
        "in": "path",
        "description": "Order Number",
        "required": true,
        "schema": {
          "type": "integer",
          "format": "int32"
        }
      }
    ],
    "responses": {
      "200": {
        "description": "Success",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OrderDTO"
            }
          }
        }
      },
      "400": {
        "description": "Bad Request",
        "content": {
          "application/json": {
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "404": {
        "description": "Not Found",
        "content": {
          "application/json": {
            "schema": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}

APIRoute 只是一个占位符作为示例。 APIRouteX 后面的方法使用相同的路由实现,但 http 变量不同,例如(POST、DELETE、PUT)。所以真的很难阅读客户端代码生成的代码。

是否可以在swagger.json文件的路由后面加上“Method-Name”?

SWAGGER 配置选项

    /// <summary>
/// Configures the Swagger Options like Titel, Contact Informations etc
/// </summary>
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
    readonly IApiVersionDescriptionProvider provider;

    /// <summary>
    /// Constructor which adds ApiVersionDescription by Dependency Injection
    /// </summary>
    /// <param name="provider">API Version Description Provider</param>
    public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider;

    /// <summary>
    /// Adds a seperate Swagger Document for each API Version
    /// </summary>
    /// <param name="options">Swagger Generic Options</param>
    public void Configure(SwaggerGenOptions options)
    {
        foreach (var description in provider.ApiVersionDescriptions)
        {
            options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description.ApiVersion.ToString(), description.IsDeprecated));
        }
    }

    /// <summary>
    /// Creates Header Informations for API
    /// </summary>
    /// <param name="description">ApiVersionDescription</param>
    /// <returns>Api Info</returns>
    static OpenApiInfo CreateInfoForApiVersion(string version, bool isDeprecated)
    {
        var info = new OpenApiInfo()
        {
            Title = "Rüstkontrolle API",
            Version = version,
            Description = "Service Dokumentation für das AddOn Rüstkontrolle im SICK Werkerportal",
            Contact = new OpenApiContact() { Name = "Lukas Adler", Email = "lukas.adler@sick.de" },
            License = new OpenApiLicense() { Name = "",}
        };

        if (isDeprecated)
        {
            info.Description += "API version is deprecated.";
        }
        return info;
    }
}

Swagger 默认值

    /// <summary>
/// Swagger Operation Filter
/// </summary>
public class SwaggerDefaultValues : IOperationFilter
{
    /// <summary>
    /// Applies Operations and OperationsFilterContext on API Descriptions
    /// </summary>
    /// <param name="operation">Operation</param>
    /// <param name="context">OperationFilterContext</param>
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {

        var apiDescription = context.ApiDescription;

        if (operation.Parameters == null)
        {
            return;
        }

        foreach (var parameter in operation.Parameters)
        {
            var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);

            if (parameter.Description == null)
            {
                parameter.Description = description.ModelMetadata?.Description;
            }

            parameter.Required |= description.IsRequired;
        }
    }
}

NSWAG 配置

    {
  "runtime": "NetCore21",
  "defaultVariables": null,
  "documentGenerator": {
    "fromDocument": "HERE IS NORMALLY MY API DESCRIPTION",
      "url": "https://localhost:44314/swagger/v1/swagger.json",
      "output": null,
      "newLineBehavior": "Auto"
    }
  },
  "codeGenerators": {
    "openApiToCSharpClient": {
      "clientBaseClass": null,
      "configurationClass": null,
      "generateClientClasses": true,
      "generateClientInterfaces": false,
      "clientBaseInterface": null,
      "injectHttpClient": true,
      "disposeHttpClient": true,
      "protectedMethods": [],
      "generateExceptionClasses": true,
      "exceptionClass": "ApiException",
      "wrapDtoExceptions": true,
      "useHttpClientCreationMethod": false,
      "httpClientType": "System.Net.Http.HttpClient",
      "useHttpRequestMessageCreationMethod": false,
      "useBaseUrl": true,
      "generateBaseUrlProperty": true,
      "generateSyncMethods": false,
      "generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
      "exposeJsonSerializerSettings": false,
      "clientClassAccessModifier": "public",
      "typeAccessModifier": "public",
      "generateContractsOutput": false,
      "contractsNamespace": null,
      "contractsOutputFilePath": null,
      "parameterDateTimeFormat": "s",
      "parameterDateFormat": "yyyy-MM-dd",
      "generateUpdateJsonSerializerSettingsMethod": true,
      "useRequestAndResponseSerializationSettings": false,
      "serializeTypeInformation": false,
      "queryNullValue": "",
      "className": "{controller}Client",
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "additionalNamespaceUsages": [],
      "additionalContractNamespaceUsages": [],
      "generateOptionalParameters": false,
      "generateJsonMethods": false,
      "enforceFlagEnums": false,
      "parameterArrayType": "System.Collections.Generic.IEnumerable",
      "parameterDictionaryType": "System.Collections.Generic.IDictionary",
      "responseArrayType": "System.Collections.Generic.ICollection",
      "responseDictionaryType": "System.Collections.Generic.IDictionary",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "namespace": "MyNamespace",
      "requiredPropertiesMustBeDefined": true,
      "dateType": "System.DateTimeOffset",
      "jsonConverters": null,
      "anyType": "object",
      "dateTimeType": "System.DateTimeOffset",
      "timeType": "System.TimeSpan",
      "timeSpanType": "System.TimeSpan",
      "arrayType": "System.Collections.Generic.ICollection",
      "arrayInstanceType": "System.Collections.ObjectModel.Collection",
      "dictionaryType": "System.Collections.Generic.IDictionary",
      "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
      "arrayBaseType": "System.Collections.ObjectModel.Collection",
      "dictionaryBaseType": "System.Collections.Generic.Dictionary",
      "classStyle": "Poco",
      "jsonLibrary": "NewtonsoftJson",
      "generateDefaultValues": true,
      "generateDataAnnotations": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateImmutableArrayProperties": false,
      "generateImmutableDictionaryProperties": false,
      "jsonSerializerSettingsTransformationMethod": null,
      "inlineNamedArrays": false,
      "inlineNamedDictionaries": false,
      "inlineNamedTuples": true,
      "inlineNamedAny": false,
      "generateDtoTypes": true,
      "generateOptionalPropertiesAsNullable": false,
      "generateNullableReferenceTypes": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": null,
      "newLineBehavior": "Auto"
    }
  }
}

【问题讨论】:

  • 通常,NSwag 使用路由来生成方法的名称。在您的情况下,两种方法 Order 具有不同的参数。你能分享你的 NSwag 配置吗?
  • @vernou 我已经添加了所需的信息。在实现中,两种排序方法具有不同的名称,例如“createOrder”和“readOrder”
  • 我不清楚。你能分享一下 NSwag(客户端代码生成)设置吗?
  • @vernou 请再看看:)

标签: .net swagger .net-5


【解决方案1】:

您可以使用SwaggerGenOptions.CustomOperationIds 指定您自己的自定义操作ID。例如。我使用以下设置:

services.AddSwaggerGen(c =>
{
    c.CustomOperationIds(x => ${x.ActionDescriptor.RouteValues["controller"]}_{x.ActionDescriptor.RouteValues["action"]}");
});

这会创建像 Client_GetDetail 这样的操作 ID,您可以将 NSWAG 配置为创建像 clientApi.getDetail() 这样的客户端 API。

PS你在你的项目中使用Swashbuckle,如果你使用NSwag那么它应该默认设置它。

【讨论】:

    【解决方案2】:

    在 NSWag 设置中,operationGenerationMode 设置为 MultipleClientsFromOperationId

    从第一个操作标签和操作ID(操作名称=操作ID,客户端名称=第一个操作标签)。

    但 OpenAPI 合同(在您的情况下为 swagger.json 文件)没有指定操作的 id。

    我看到了两个解决方案,选择另一个值 operationGenerationMode 或指定操作的 id。

    其他operationGenerationModeare

    public enum OperationGenerationMode
    {
        /// <summary>Multiple clients from the Swagger operation ID in the form '{controller}_{action}'.</summary>
        MultipleClientsFromOperationId,
    
        /// <summary>From path segments (operation name = last segment, client name = second to last segment).</summary>
        MultipleClientsFromPathSegments,
    
        /// <summary>From the first operation tag and path segments (operation name = last segment, client name = first operation tag).</summary>
        MultipleClientsFromFirstTagAndPathSegments,
    
        /// <summary>From the first operation tag and operation ID (operation name = operation ID, client name = first operation tag).</summary>
        MultipleClientsFromFirstTagAndOperationId,
    
        /// <summary>From the Swagger operation ID.</summary>
        SingleClientFromOperationId,
    
        /// <summary>From path segments suffixed by HTTP operation name</summary>
        SingleClientFromPathSegments,
    }
    

    如果您喜欢指定操作的 id,您可以在Http[Get|Put|...]Attribute 中设置名称:

    [HttpGet(Name = "SpecifiedOperationId")]
    public IActionResult Get()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-08
      • 2021-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-23
      相关资源
      最近更新 更多