首页 > 解决方案 > 将框架升级到 .NET Core 3.1 后出现 Swagger 问题

问题描述

我们有一个框架 2.1 上的 .NET Core Web API。我将它更新到 .NET Core 3.1,当然这涉及到一些代码更改以实现兼容性。目前我可以从 VS 在本地运行它并且它工作正常 - 我可以使用 Swagger 和所有查看索引页面。但是在将其部署到开发服务器后,它不会从服务器 URL (https://[my app].[domain].com/swagger/index.html) 加载。我得到一个页面:

在此处输入图像描述

...尽管开发人员工具中没有列出任何错误。

服务器上的输出日志是这样说的:

信息:Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
用户配置文件可用。使用“C:\Windows\system32\config\systemprofile\AppData\Local\ASP.NET\DataProtection-Keys”作为密钥存储库和 Windows DPAPI 来加密静态密钥。
信息:Microsoft.Hosting.Lifetime[0]
应用程序已启动。按 Ctrl+C 关闭。
信息:Microsoft.Hosting.Lifetime[0]
托管环境:生产信息:Microsoft.Hosting.Lifetime[0] 内容根路径:E:\Websites[my app] 信息:Microsoft.AspNetCore.Hosting.Diagnostics 1
请求启动 HTTP/ 2.0 获取 https://[我的应用程序].[域].com/swagger/index.html
信息:Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] 发送文件。请求路径:'/swagger/index.html'。物理路径:'E:\Websites[my app]\wwwroot\swagger\index.html' 信息:Microsoft.AspNetCore.Hosting.Diagnostics[2] 请求在 21.5241 毫秒内完成 200 文本/html 信息:Microsoft.AspNetCore.Hosting。诊断1请求开始 HTTP/2.0 GET https://[my app].[domain].com/swagger/custom.css 信息:Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] 发送文件。请求路径:'/swagger/custom.css'。物理路径:'E:\Websites[my app]\wwwroot\swagger\custom.css' 信息:Microsoft.AspNetCore.Hosting.Diagnostics[2] 请求在 1.0042 毫秒内完成 200 文本/css 信息:Microsoft.AspNetCore.Hosting。诊断1请求开始 HTTP/2.0 GET https://[my app].[domain].com/swagger/favicon.ico 信息:Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] 发送文件。请求路径:'/swagger/favicon.ico'。物理路径:'E:\Websites[my app]\wwwroot\swagger\favicon.ico' 信息:Microsoft.AspNetCore.Hosting.Diagnostics[2] 请求在 1.8643 毫秒内完成 200 图像/x-icon 信息:Microsoft.AspNetCore。 Hosting.Diagnostics 1请求开始 HTTP/2.0 GET https://[my app].[domain].com/swagger/v3/swagger.json 信息:Microsoft.AspNetCore.Hosting.Diagnostics 1请求开始 HTTP/2.0 GET https://[my app].[domain].com/swagger/favicon-32x32.png 信息:Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] 发送文件。请求路径:'/swagger/favicon-32x32.png'。物理路径:'E:\Websites[my app]\wwwroot\swagger\favicon-32x32.png' 信息:Microsoft.AspNetCore.Hosting.Diagnostics[2] 请求在 2.0548 毫秒内完成 200 图像/png 失败:Microsoft.AspNetCore。服务器.IIS.Core.IISHttpServer[2]

连接 ID “17437937763084179141”,请求 ID “800096c6-0001-f200-b63f-84710c7967bb”:应用程序引发了未处理的异常。System.UriFormatException:无效的 URI:无法确定 URI 的格式。在 System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind) 在 System.Uri..ctor(String uriString) 在 ICE.API.Startup.<>c__DisplayClass4_0.b__5(SwaggerGenOptions swagger) 在 D:\ADO\ADOAgent1 \Work\206\s[repo][project]\Startup.cs:line 119 at Microsoft.Extensions.Options.ConfigureNamedOptions 1.Configure(String name, TOptions options) at Microsoft.Extensions.Options.OptionsFactory1.Create(String name) at Microsoft.Extensions.Options.OptionsManager`1.<>c__DisplayClass5_0.b__0 ()....

引用的包包括(但不限于):

这是 Startup.cs 的内容,其中第 119 行(在上面的错误中指定)是swagger.SwaggerDoc("v3", new OpenApiInfo

    public void ConfigureServices(IServiceCollection services)
    {
        string[] allowedOrigins = Configuration.GetSection("appsettings:AllowedPortalUrlCsv").Value.Trim().Split(',');

        services.AddOptions();
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => 
                builder
                    .WithOrigins(allowedOrigins)
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials()
           );
        });
        services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
        services.AddSignalR();
        services.AddControllers();

        //Add DB Context:
        services.AddDbContext<ActiveDBContext>(options => options.UseOracle(Configuration["appsettings:connection:activedb"]));

        services.AddDistributedMemoryCache();

        int timeout; 
        var success = int.TryParse(Configuration["appsettings:sessionidletimeout"], out timeout); 
        timeout = (success ? timeout : 10);

        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromMinutes(timeout);
            options.Cookie.HttpOnly = true;
            options.Cookie.IsEssential = true;
            options.Cookie.SameSite = SameSiteMode.None;
        });

            //Higher Level Dependency Mappings:
                services.AddTransient<IValidateRequest, ValidateRequest>();
                services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
                services.AddSingleton<IFileProvider>(sp => sp.GetRequiredService<IWebHostEnvironment>().ContentRootFileProvider);

            //Add Service Dependencies:
        services.AddScoped<[interface], [service]>();

        services.AddAuthentication(IISDefaults.AuthenticationScheme);

        services.Configure<IISOptions>(options =>
        {
            options.AutomaticAuthentication = true;
        });

        services.AddSwaggerGen(swagger =>
        {
            swagger.SwaggerDoc("v3", new OpenApiInfo
            {
                Version = Configuration.GetSection("appsettings:info:version").Value,
                Title = Configuration.GetSection("appsettings:info:title").Value,
                Description = Configuration.GetSection("appsettings:info:description").Value,
                TermsOfService = new Uri(Configuration.GetSection("appsettings:info:termsofservice").Value),
                Contact = new OpenApiContact
                {
                    Name = Configuration.GetSection("appsettings:info:contact:name").Value,
                    Email = Configuration.GetSection("appsettings:info:contact:email").Value,
                    Url = new Uri(Configuration.GetSection("appsettings:info:contact:url").Value),
                },
                License = new OpenApiLicense
                {
                    Name = Configuration.GetSection("appsettings:info:license:name").Value,
                    Url = new Uri(Configuration.GetSection("appsettings:info:license:url").Value),
                }
            });

            var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "[project].xml");

            swagger.IncludeXmlComments(filePath);
            swagger.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
            {
                Description = $"ApiKey for authorization across API (Example: {Configuration.GetSection("appsettings:apikey").Value})",
                Name = "ApiKey",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.ApiKey
            });
            swagger.AddSecurityRequirement(new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "ApiKey"
                        }
                    },
                    new string[] { }
                }
            });
        });
        services.AddSwaggerGenNewtonsoftSupport();

        services.AddSingleton(Configuration);
        services.AddMvc().AddJsonOptions(o =>
        {
            o.JsonSerializerOptions.PropertyNamingPolicy = null;
            o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
        });        
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
    {
        //UseStaticFiles will allow the site to check wwwroot for overriding files to load, such as icons, images, styles, etc...
        app.UseStaticFiles();

        //For .NET Core 3.1, IHostingEnvironment changed to IWebHostEnvironment...but IsDevelopment is in the Microsoft.Extensions.Hosting namespace, so that was added up top
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        //Needed for setting if no TNSNames and Local Client is installed:
        OracleConfiguration.OracleDataSources.Add(Configuration.GetSection("appsettings:connection:tnsname").Value, Configuration.GetSection("appsettings:connection:descriptor").Value);
               
        loggerFactory.AddDBLogger(new DBLoggerConfiguration()
        {
            DBConnection = Configuration.GetSection("appsettings:connection:activedb").Value,
            LogLevelAsString = Configuration.GetSection("appsettings:internalloglevel").Value.ToLower(),
            EnableFallBackLogger = true
        });

        //Swagger API Documentation:
        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("./swagger/v3/swagger.json", $"{Configuration.GetSection("appsettings:info:title").Value} {Configuration.GetSection("appsettings:info:version").Value}");
        });

        app.UseMiddleware<ApiKeyValidatorsMiddleware>();

        app.UseCors("CorsPolicy");

        app.UseSession();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<Domain.Helpers.NotifyHub>("/notify");
            endpoints.MapControllers();
        });
    }
}

这是 index.html 的内容:

<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>[my website]</title>
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui/dist/swagger-ui.css">
    <link rel="stylesheet" type="text/css" href="./custom.css">
    <link rel="icon" type="image/png" href="./favicon.ico">
    <style>

        html {
            box-sizing: border-box;
            overflow: -moz-scrollbars-vertical;
            overflow-y: scroll;
        }

        *,
        *:before,
        *:after {
            box-sizing: inherit;
        }

        body {
            margin: 0;
            background: #fafafa;
        }
    </style>
    <link href='/swagger/custom.css' rel='stylesheet' media='screen' type='text/css' />

</head>

<body>

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
  <defs>
    <symbol viewBox="0 0 20 20" id="unlocked">
          <path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
    </symbol>

    <symbol viewBox="0 0 20 20" id="locked">
      <path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
    </symbol>

    <symbol viewBox="0 0 20 20" id="close">
      <path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
    </symbol>

    <symbol viewBox="0 0 20 20" id="large-arrow">
      <path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
    </symbol>

    <symbol viewBox="0 0 20 20" id="large-arrow-down">
      <path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
    </symbol>


    <symbol viewBox="0 0 24 24" id="jump-to">
      <path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
    </symbol>

    <symbol viewBox="0 0 24 24" id="expand">
      <path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
    </symbol>

  </defs>
</svg>

<div id="swagger-ui"></div>

<!-- Workaround for https://github.com/swagger-api/swagger-editor/issues/1371 -->
<script>
  if (window.navigator.userAgent.indexOf("Edge") > -1) {
    console.log("Removing native Edge fetch in favor of swagger-ui's polyfill")
    window.fetch = undefined;
  }
</script>

<script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js" charset="utf-8"></script>
<script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-standalone-preset.js" charset="utf-8"></script>
<script>
  window.onload = function () {
    var configObject = JSON.parse('{"urls":[{"url":"/swagger/v3/swagger.json","name":"[my website] v3"}],"validatorUrl":null}');
    var oauthConfigObject = JSON.parse('{}');

    /*Apply mandatory parameters*/
    configObject.dom_id = "#swagger-ui";
    configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset];
    configObject.layout = "StandaloneLayout";

    /*If oauth2RedirectUrl isn't specified, use the built-in default*/
    if (!configObject.hasOwnProperty("oauth2RedirectUrl"))
      configObject.oauth2RedirectUrl = window.location.href.replace("index.html", "oauth2-redirect.html");

    /*Build a system*/
    const ui = SwaggerUIBundle(configObject);

    /*Apply OAuth config*/
    ui.initOAuth(oauthConfigObject);
  }
</script>
</body>

</html>

一定和AddSwaggerGenStartup里面的内容有关。我尝试用谷歌搜索,但看不到任何特定问题,所以我不确定我还需要做什么。请注意 .NET Core 2.1 上的版本在 URL 中加载 swagger 就好了。

标签: c#asp.net-coreswaggerswagger-uiswashbuckle.aspnetcore

解决方案


推荐阅读