使用身份验证托管的 Blazor Wasm 上的 Azure 500 错误

时间:2021-01-05 17:34:57

标签: azure blazor serilog .net-5

我在这里完全不知所措。我有一个运行 .net5 的 Blazor WASM 托管,它已部署到 Azure 应用服务。当没有数据库时,部署到 Azure 没有问题。它按预期加载并运行。我已经安装了 Identity 和 DBContext。一切都在本地构建并与本地 SQL 实例一起正常运行。

在 Azure 上,我创建了一个新的 SQLServer 和一个 SQL 数据库。在 SQL 数据库防火墙设置中,我有“允许 Azure 服务和资源访问此服务器”以及我的客户端 IP(不是 Azure 应用程序的 IP)的规则。

对于配置中的应用服务,我有一个名为 DefaultConnection(与 appsettings.json 中相同)的 ConnectionString,其连接字符串与 SQLDatabase 提供的连接字符串相同,源 AppConfig,类型 SqlAzure

我使用 VS2019 Publish on the Server 项目(启动项目)发布到 Azure。我选择了目标为 Azure -> Azure 应用服务 (Windows) 和我的实例名称。配置为 Release,Target Framework net5.0,DeploymentMode Framework-dependent,Target runtime Portable。

服务依赖项设置为 AzureSqlDatabase,它使用 ConnectionName DefaultConnection,用户名和密码是在 Azure 上创建的 SQL Server 的管理员用户密码设置,SaveConnectionStringValue 是 Azure 应用程序设置。 (这会自动填充上述应用服务配置连接字符串。

当我点击发布时,我在输出中看到所有发布都正确:

Publish Succeeded.
Web App was published successfully http://bbqfriend.azurewebsites.net/
========== Build: 1 succeeded, 0 failed, 6 up-to-date, 0 skipped ==========
========== Publish: 1 succeeded, 0 failed, 0 skipped ==========
Installation of Web App Site extension Microsoft.AspNetCore.AzureAppServices.SiteExtension is in progress...
Restarting the Web App...
Successfully installed Web App extension Microsoft.AspNetCore.AzureAppServices.SiteExtension
Successfully restarted Web App.

但是当页面启动时,它显示 500 错误。

如果我返回发布并编辑设置 - 数据库 - DefaultConnection 并检查在运行时使用此连接字符串选择在 ServiceDependencies 以及 EntityFrameworkMigrations DataContext 中配置的连接字符串,则在发布时应用此迁移。当我发布该配置文件时,它将执行迁移以及我在 DataContext OnModelCreating 覆盖中定义的种子

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            #region Identity Seed
            modelBuilder.ApplyConfiguration(new ApplicationUserConfiguration());
            modelBuilder.ApplyConfiguration(new IdentityRoleConfiguration());
            modelBuilder.ApplyConfiguration(new IdentityUserRoleConfiguration());
            #endregion

            //modelBuilder.ApplyConfiguration(new CountryConfiguration());

            
        }

所以我知道连接字符串是正确的,并且有一个具有正确模型和种子数据的数据库。为什么我得到了 500?!?

这是我在服务器项目中的 appsettings.json

{
    "ConnectionStrings": {
        "DefaultConnection": "Server=.;Database={DatabaseName};Trusted_Connection=True;MultipleActiveResultSets=true"
    },
    "IdentityServer": {
        "Clients": {
            "XXXX.Client": {
                "Profile": "IdentityServerSPA"
            }
        }
    },
    "Serilog": {
        "Using": [ "Serilog.Sinks.MSSqlServer" ],
        "MinimumLevel": {
            "Default": "Information",
            "Override": {
                "Microsoft": "Warning",
                "Microsoft.AspNetCore": "Warning",
                "Microsoft.AspNetCore.Authorization.DefaultAuthorizationService": "Warning",
                "Microsoft.EntityFrameworkCore": "Warning",
                "System": "Warning",
                "System.Net.Http.HttpClient*": "Warning",
                "IdentityServer4": "Warning",
                "Serilog.AspNetCore": "Warning"
            }
        },
        "WriteTo": [
            {
                "Name": "MSSqlServer",
                "Args": {
                    "connectionString": "DefaultConnection",
                    "sinkOptionsSection": {
                        "tableName": "Logs"
                    },
                    "columnOptionsSection": {
                        "additionalColumns": [
                            {
                                "ColumnName": "InstanceId"
                            },
                            {
                                "ColumnName": "Origin"
                            },
                            {
                                "ColumnName": "SourceContext"
                            },
                            {
                                "ColumnName": "UserId"
                            },
                            {
                                "ColumnName": "Username"
                            }
                        ],
                        "excludeAdditionalProperties": true
                    }
                }
            }
        ]
    },
    "AllowedHosts": "*"
}

这是服务器项目的 Startup.cs

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

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            //Register the Datacontext and Connection String
            services.AddDbContext<DataContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

            services.AddDatabaseDeveloperPageExceptionFilter();

            //Sets up the default Asp.net core Identity Screens - Use Identity Scaffolding to override defaults
            services.AddDefaultIdentity<ApplicationUser>( options =>
                    {
                        options.SignIn.RequireConfirmedAccount = true;
                        options.Password.RequireDigit = true;
                        options.Password.RequireLowercase = true;
                        options.Password.RequireUppercase = true;
                        options.Password.RequiredUniqueChars = 0;
                        options.Password.RequireNonAlphanumeric = false;
                        options.Password.RequiredLength = 8;
                        options.User.RequireUniqueEmail = true;
                    })
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<DataContext>();

            //Associates the User to Context with Identity
            services.AddIdentityServer()
                .AddApiAuthorization<ApplicationUser, DataContext>( options =>
            {
                options.IdentityResources["openid"].UserClaims.Add(JwtClaimTypes.Role);
                options.ApiResources.Single().UserClaims.Add(JwtClaimTypes.Role);
            });
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove(JwtClaimTypes.Role);

            //Adds authentication handler
            services.AddAuthentication().AddIdentityServerJwt();

            //Register Repositories for Dependency Injection
            services.AddScoped<ICountryRepository, CountryRepository>();

            services.AddControllersWithViews();
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseMigrationsEndPoint();
                app.UseWebAssemblyDebugging();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            //AutoMigrates data
            //dataContext.Database.Migrate();

            app.UseHttpsRedirection();
            app.UseBlazorFrameworkFiles();
            app.UseStaticFiles();

            app.UseSerilogIngestion();
            app.UseSerilogRequestLogging();

            app.UseRouting();

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

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapControllers();
                endpoints.MapFallbackToFile("index.html");
            });
        }
    }

这是服务器项目的 Program.cs

public class Program
    {
        public static void Main(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();

            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(configuration)
                .Enrich.WithProperty("InstanceId", Guid.NewGuid())
                .Enrich.WithProperty("Origin", "Server")
                .CreateLogger();

            try
            {
                Log.Information("Starting up");
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Application start-up failed");
            }
            finally
            {
                Log.CloseAndFlush();
            }

        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }

这是客户端项目的 Program.cs

public static async Task Main(string[] args)
        {
            //Serilog 
            var levelSwitch = new LoggingLevelSwitch();
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.ControlledBy(levelSwitch)
                .Enrich.WithProperty("InstanceId", Guid.NewGuid())
                .Enrich.FromLogContext()
                .WriteTo.BrowserHttp(controlLevelSwitch: levelSwitch)
                .CreateLogger();            

            Log.ForContext<Program>().Information("Client has started");

            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");

            builder.Services.AddLogging(logging =>
           {
               logging.ClearProviders();
               logging.AddSerilog(dispose: true);
           });

            builder.Services.AddHttpClient("XXX.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
                .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

            // Supply HttpClient instances that include access tokens when making requests to the server project
            builder.Services.AddTransient(sp => 
                sp.GetRequiredService<IHttpClientFactory>()
                .CreateClient("XXXX.ServerAPI"));

            builder.Services.AddApiAuthorization()
                .AddAccountClaimsPrincipalFactory<RolesClaimsPrincipalFactory>();

            //Register Services
            var baseAddress = new Uri($"{builder.HostEnvironment.BaseAddress}api/");
            void RegisterTypedClient<TClient, TImplementation>(Uri apiBaseUrl)
                where TClient : class where TImplementation : class, TClient
            {
                builder.Services.AddHttpClient<TClient, TImplementation>(client => client.BaseAddress = apiBaseUrl)
                    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
            }

            RegisterTypedClient<ICountryService, CountryService>(baseAddress);


            await builder.Build().RunAsync();
        }

我确实配置了 Serilog,它看起来也能正常工作。这是我在服务器启动期间看到的错误消息

System.InvalidOperationException: Startup assembly Microsoft.ApplicationInsights.StartupBootstrapper failed to execute. See the inner exception for more details.
 ---> System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.ApplicationInsights.StartupBootstrapper, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
File name: 'Microsoft.ApplicationInsights.StartupBootstrapper, Culture=neutral, PublicKeyToken=null'
   at System.Reflection.RuntimeAssembly.InternalLoad(ObjectHandleOnStack assemblyName, ObjectHandleOnStack requestingAssembly, StackCrawlMarkHandle stackMark, Boolean throwOnFileNotFound, ObjectHandleOnStack assemblyLoadContext, ObjectHandleOnStack retAssembly)
   at System.Reflection.RuntimeAssembly.InternalLoad(AssemblyName assemblyName, RuntimeAssembly requestingAssembly, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, AssemblyLoadContext assemblyLoadContext)
   at System.Reflection.Assembly.Load(AssemblyName assemblyRef)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.ExecuteHostingStartups()
   --- End of inner exception stack trace ---

 System.InvalidOperationException: Startup assembly DiagnosticServices.HostingStartup failed to execute. See the inner exception for more details.
 ---> System.IO.FileNotFoundException: Could not load file or assembly 'DiagnosticServices.HostingStartup, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
File name: 'DiagnosticServices.HostingStartup, Culture=neutral, PublicKeyToken=null'
   at System.Reflection.RuntimeAssembly.InternalLoad(ObjectHandleOnStack assemblyName, ObjectHandleOnStack requestingAssembly, StackCrawlMarkHandle stackMark, Boolean throwOnFileNotFound, ObjectHandleOnStack assemblyLoadContext, ObjectHandleOnStack retAssembly)
   at System.Reflection.RuntimeAssembly.InternalLoad(AssemblyName assemblyName, RuntimeAssembly requestingAssembly, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, AssemblyLoadContext assemblyLoadContext)
   at System.Reflection.Assembly.Load(AssemblyName assemblyRef)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.ExecuteHostingStartups()
   --- End of inner exception stack trace ---

更新 我能够复制上述错误消息,并且它们通过 Serilog 登录到数据库中。

enter image description here

所以我们可以从 Server Program.cs Main 方法(上面)看到“启动”,下一个条目来自 EntityFramework 模型验证。然后是错误。我可以将命名空间视为 Microsoft.AspNetCore.Hosting.Diagnostics 作为异常来源。

  1. 我尝试添加 Nuget 引用,但没有任何作用

  2. 我尝试添加对已添加服务的引用。AddApplicationInsightsTelemetry();到服务器 Startup.cs ConfigureServices 和 ApplicationInsights InstrumentationKey 到 appsettings.json(已作为变量存在于 Azure 中),但没有任何作用

  3. 我添加了一个引用并添加了

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseSerilog()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>().UseAzureAppServices();
                });

根据此处找到的解决方法 https://github.com/dotnet/extensions/issues/2566 没有帮助

第 2 天更新

添加更多信息,因为我仍然遇到相同的异常。我很好奇这是否是版本之间的兼容性问题。我的应用程序是 .net5 并使用 .net5 早期访问在 Azure 上运行。

这是我用于服务器项目的 Nuget 包

<ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="5.0.1" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="5.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.ApiAuthorization.IdentityServer" Version="5.0.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.1" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.1">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="5.0.1" />
    <PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
    <PackageReference Include="Serilog.AspNetCore.Ingestion" Version="1.0.0-dev-00012" />
    <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
    <PackageReference Include="Serilog.Sinks.MSSqlServer" Version="5.6.0" />
  </ItemGroup>

这是客户端项目的 Nuget 包

<ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.1" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.1" />
    <PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
    <PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
    <PackageReference Include="Serilog.Sinks.BrowserHttp" Version="1.0.0-dev-00012" />
    <PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
  </ItemGroup>

我尝试从客户端和服务器项目中删除 Serilog。我还是收到了 500。

移除 Serilog 后,我尝试使用来自 https://github.com/dotnet/extensions/issues/2566 的 .UseAzureAppServices() 也没有成功。

我确实注意到一个额外的错误消息

2021-01-06 19:00:38.322 +00:00 [Error] Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware: An unhandled exception has occurred while executing the request.
System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.<>c.<AddSigningCredentials>b__10_2(IServiceProvider sp)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at ... (removed for post size) 

当我将我的网站部署为 Blazor WebAssembly 托管解决方案时,我已经能够将其隔离为一个问题。我能够获得我网站的一个版本,该版本已升级到 .net5 PRE,从 Blazor WebAssembly 切换到 Blazor WebAssembly Hosted。 .net5 版本能够毫无问题地部署到 Azure。部署 WebAssembly 托管版本时,我遇到了 500 个错误。因此,这与将 Blazor WebAssembly 托管解决方案部署到 Azure 相关。

我还尝试创建了一个开箱即用的 Blazor WebAssembly 托管解决方案,无需身份验证,并将其部署到 Azure。这没有问题。但是,当我创建一个开箱即用的 Blazor WebAssembly Hosted WITH Authentication(存储在应用程序中的个人用户帐户)并将其部署到 Azure 时,它​​失败了 500!

1 个答案:

答案 0 :(得分:0)

最简单的方法:

<块引用>

将环境变量 ASPNETCORE_ENVIRONMENT 添加/设置为 Development

enter image description here

...您的 Hosted Blazor WASM with Identity 最终将在 Azure 应用服务中运行


如果您不想要上面的简单方法,请改为执行以下操作:

  1. 按照本文生成自签名证书:

(在生成自签名证书部分) https://gavilan.blog/2020/08/18/blazor-using-a-self-signed-certificate-for-identityserver4-in-azure-app-service/

  1. 记住您用于生成证书的密码。
  2. 将证书放在您的项目中(例如在服务器项目中)
  3. 将这些附加到 appsettings.json 文件中:

enter image description here

  1. 再次发布应用。