运行 NSwag 而不引导整个站点

问题描述 投票:0回答:3

我们使用 NSwag 来使用如下命令生成 swagger 文件:

node_modules/.bin/nswag run foobar.nswag /runtime:NetCore31

这似乎实际上启动了应用程序,并且我们的一些引导代码将失败,因为生成 swagger 文档的进程无法访问连接字符串等。

有没有一种方法可以在不引导/启动整个应用程序的情况下生成文档?

有没有办法在启动代码中检测“swagger 生成模式”,以便我们可以跳过该场景的步骤?

使用

NSwag.AspNetCore
nuget 包版本
13.11.3

foobar.nswag
文件:

{
  "runtime": "NetCore31",
  "defaultVariables": null,
  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      "project": "Foobar.csproj",
      "msBuildProjectExtensionsPath": null,
      "configuration": null,
      "runtime": null,
      "targetFramework": null,
      "noBuild": false,
      "msBuildOutputPath": null,
      "verbose": true,
      "workingDirectory": null,
      "requireParametersWithoutDefault": false,
      "apiGroupNames": [
        "BACKEND"
      ],
      "defaultPropertyNameHandling": "Default",
      "defaultReferenceTypeNullHandling": "Null",
      "defaultDictionaryValueReferenceTypeNullHandling": "NotNull",
      "defaultResponseReferenceTypeNullHandling": "NotNull",
      "generateOriginalParameterNames": true,
      "defaultEnumHandling": "Integer",
      "flattenInheritanceHierarchy": false,
      "generateKnownTypes": true,
      "generateEnumMappingDescription": false,
      "generateXmlObjects": false,
      "generateAbstractProperties": false,
      "generateAbstractSchemas": true,
      "ignoreObsoleteProperties": false,
      "allowReferencesWithProperties": false,
      "excludedTypeNames": [],
      "serviceHost": null,
      "serviceBasePath": null,
      "serviceSchemes": [],
      "infoTitle": "My Title",
      "infoDescription": null,
      "infoVersion": "1.0.0",
      "documentTemplate": null,
      "documentProcessorTypes": [],
      "operationProcessorTypes": [],
      "typeNameGeneratorType": null,
      "schemaNameGeneratorType": null,
      "contractResolverType": null,
      "serializerSettingsType": null,
      "useDocumentProvider": false,
      "documentName": "v1",
      "aspNetCoreEnvironment": "Development",
      "createWebHostBuilderMethod": null,
      "startupType": null,
      "allowNullableBodyParameters": true,
      "output": "foobar-swagger.json",
      "outputType": "Swagger2",
      "newLineBehavior": "Auto",
      "assemblyPaths": [],
      "assemblyConfig": null,
      "referencePaths": [],
      "useNuGetCache": false
    }
  },
  "codeGenerators": {
    "openApiToTypeScriptClient": {
      "className": "{controller}Client",
      "moduleName": "",
      "namespace": "",
      "typeScriptVersion": 2.7,
      "template": "Fetch",
      "promiseType": "Promise",
      "httpClass": "HttpClient",
      "withCredentials": false,
      "useSingletonProvider": false,
      "injectionTokenType": "OpaqueToken",
      "rxJsVersion": 6.0,
      "dateTimeType": "Date",
      "nullValue": "Undefined",
      "generateClientClasses": false,
      "generateClientInterfaces": false,
      "generateOptionalParameters": false,
      "exportTypes": true,
      "wrapDtoExceptions": false,
      "exceptionClass": "ApiException",
      "clientBaseClass": null,
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "protectedMethods": [],
      "configurationClass": null,
      "useTransformOptionsMethod": false,
      "useTransformResultMethod": false,
      "generateDtoTypes": true,
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "markOptionalProperties": true,
      "generateCloneMethod": false,
      "typeStyle": "Class",
      "enumStyle": "Enum",
      "useLeafType": false,
      "classTypes": [],
      "extendedClasses": [],
      "extensionCode": null,
      "generateDefaultValues": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateConstructorInterface": true,
      "convertConstructorInterfaceData": false,
      "importRequiredTypes": true,
      "useGetBaseUrlMethod": false,
      "baseUrlTokenName": "API_BASE_URL",
      "queryNullValue": "",
      "useAbortSignal": false,
      "inlineNamedDictionaries": false,
      "inlineNamedAny": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "checksumCacheEnabled": false,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": null,
      "newLineBehavior": "Auto"
    }
  }
}
asp.net-core nswag
3个回答
1
投票

我在你的nswag.json中没有看到任何超级奇怪的东西;也许您仍然需要在配置 OpenApi/Swagger 的地方发布 Startup.cs。

您是否可能在

ConfigureServices()
中做一些奇怪的事情,例如运行需要特定设置(如连接字符串)的特定代码。例如?我认为这是一个不好的做法。我也相信 nswag 确实会运行你的项目/运行
ConfigureServices()
(但我只是凭记忆猜测),所以这可能会导致问题,是的。

否则,请尝试以下操作:

  • 我个人将
    targetFramework
    设置为
    net5.0
    (您使用netcore 3.1,因此需要不同的值),将其留空
  • 这可能是多种因素的结合:
    • 我将
      nobuild
      设置为
      false
      ,因为我在运行
      nswag
      之前构建了我的项目,因为当 nswag 尝试执行此操作或其他问题时,它会随机导致许多问题,项目无法正确构建。
    • 我不使用node_module来构建nswag.json。我使用
      the nswag.msbuild
      包,并将其全部放在单独的
      Api.Client
      组件中,并使用 推荐的设置
    • 我还在我的 nswag.json 中使用
      "configuration": "$(Configuration)"

我刚刚想出了一个非常丑陋的解决方案,你可能会使用它,但我确实相信这将是一个丑陋的解决方法,因为我认为你的问题在于在ConfigureServices()中使用了不好的做法。

解决方案是在 API 启动时将另一个变量传递给它,或者将环境更改为

Nswag
或类似的东西,这样在你的代码中你可以做类似
if(environment != "nswag") {}
的事情,但同样,丑陋..


1
投票

我知道有点晚了,但这可能对其他人有帮助。我面临着同样的问题(此处也有描述:https://github.com/RicoSuter/NSwag/issues/3922

我尝试了使用 nswag.json 中的

createWebHostBuilderMethod
startupType
配置的解决方案(此处描述:https://bartwullems.blogspot.com/2021/05/nswag-error-systeminvalidoperationexcep.html),但是对我来说不起作用,因为这些配置现在似乎已经贬值了。

对我有用的是:

  1. 在我的解决方案中创建一个名为
    NSwag
    的自定义配置,仅由 MyApi 项目使用。
  2. 使用
    Program.cs
    命名空间和围绕这两个类的
    Startup.cs
    预编译器指令在 MyApi 项目中创建自定义 NSwag
    NSwag
    NSwag
    Startup
    类仅包含控制器和 Swagger 所需的引导,并删除所有其他不相关的引导,特别是那些依赖于运行时应用程序设置的引导。
  3. 通过使用预编译器指令更新主 MyApi
    Program.cs
    以使用正常或 NSwag 引导。
  4. 使用nswag.json中的
    NSwag
    配置,即将
    configuration
    设置为
    NSwag

MyApi NSwag 程序.cs

#if NSWAG
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace MyApi.NSwag;

public class Program
{
    public static void RunHost(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        host.Run();
    }

    private static IWebHostBuilder CreateWebHostBuilder(string[] args)
    {
        var builder = WebHost.CreateDefaultBuilder()
            .UseStartup<Startup>(); // MyApi.NSwag.Startup

        return builder;
    }
}
#endif

MyApi NSwag Startup.cs

#if NSWAG
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace MyApi.NSwag;

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        // i.e. whatever you normally have to bootstrap your controllers and Swagger
        services.AddControllers();

        services.AddVersionedApiExplorer(o =>
            {
                o.GroupNameFormat = "'v'VVV";
                o.SubstituteApiVersionInUrl = true;
            })
            .AddApiVersioning(o =>
            {
                o.AssumeDefaultVersionWhenUnspecified = true;
                o.ReportApiVersions = true;
                o.DefaultApiVersion = new ApiVersion(1, 0);
                o.ApiVersionReader = new UrlSegmentApiVersionReader();
                o.UseApiBehavior = false;
            });

        services.AddSwaggerDocument(config =>
        {
            config.DocumentName = "allVersions";
            config.Title = "My API";
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();

        app.UseOpenApi();
        app.UseSwaggerUi3();

        app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
    }
}
#endif

MyApi 主程序.cs

using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace MyApi;

public class Program
{
    public static void Main(string[] args)
    {
#if NSWAG
        NSwag.Program.RunHost(args);
#else
        RunHost(args);
#endif
    }

    private static void RunHost(string[] args)
    {
        // i.e. whatever you normally have in Main
        var host = CreateWebHostBuilder(args).Build();
        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args)
    {
        var builder = WebHost.CreateDefaultBuilder()
            .UseStartup<Startup>(); // MyApi.Startup

        return builder;
    }
}

MyApi nswag.json

{
    "runtime": "Net60",
    "defaultVariables": null,
    "documentGenerator": {
      "aspNetCoreToOpenApi": {
        "project": "src/MyApi/MyApi.csproj",
        "msBuildProjectExtensionsPath": null,
        "configuration": "NSwag",
        ...
    },
    ...
}

0
投票

因此,如果您使用

Nswag.MsBuild
nuget 包,您可以在 csproj 中执行构建步骤,例如:

<Target Name="NSwag" AfterTargets="PostBuildEvent" Condition=" '$(Configuration)' == 'Debug' ">
  <Exec WorkingDirectory="$(ProjectDir)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Nswag" Command="$(NSwagExe_Net50) run nswag.json /variables:Configuration=$(Configuration)" />
</Target>

注意

EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Nswag"
属性。这允许您检查“Nswag”环境变量是否存在并相应地更改您的启动逻辑:

var nswag = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

if (!string.IsNullOrWhiteSpace(nswag)) {
  // just for nswag, do not encrypt as this breaks the build and release pipeline
  configurationBuilder.AddJsonFile(source => {
    source.ReloadOnChange = true;
    source.Path = protectedJsonFile;
  });
  return;
}

而且您也不需要使用

ASPNETCORE_ENVIRONMENT
作为变量名称,它可以是任何名称。

© www.soinside.com 2019 - 2024. All rights reserved.