James Newton-King 5 лет назад
Родитель
Сommit
2c175f7fbd
47 измененных файлов с 513 добавлено и 417 удалено
  1. 4 1
      src/DataProtection/DataProtection/test/HostingTests.cs
  2. 14 14
      src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs
  3. 3 3
      src/Hosting/Hosting/src/GenericHost/GenericWebHostServiceOptions.cs
  4. 8 55
      src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs
  5. 3 3
      src/Hosting/Hosting/src/GenericHost/HostingStartupWebHostBuilder.cs
  6. 3 3
      src/Hosting/Hosting/src/Internal/ConfigureBuilder.cs
  7. 5 2
      src/Hosting/Hosting/src/Internal/ConfigureContainerBuilder.cs
  8. 7 7
      src/Hosting/Hosting/src/Internal/ConfigureServicesBuilder.cs
  9. 43 0
      src/Hosting/Hosting/src/Internal/ErrorPageBuilder.cs
  10. 1 1
      src/Hosting/Hosting/src/Internal/HostedServiceExecutor.cs
  11. 11 11
      src/Hosting/Hosting/src/Internal/HostingApplication.cs
  12. 1 1
      src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs
  13. 5 5
      src/Hosting/Hosting/src/Internal/HostingEnvironment.cs
  14. 4 4
      src/Hosting/Hosting/src/Internal/HostingEventSource.cs
  15. 5 2
      src/Hosting/Hosting/src/Internal/HostingLoggerExtensions.cs
  16. 20 15
      src/Hosting/Hosting/src/Internal/HostingRequestFinishedLog.cs
  17. 13 13
      src/Hosting/Hosting/src/Internal/HostingRequestStartingLog.cs
  18. 1 1
      src/Hosting/Hosting/src/Internal/MethodInfoExtensions.cs
  19. 10 10
      src/Hosting/Hosting/src/Internal/StartupLoader.cs
  20. 2 2
      src/Hosting/Hosting/src/Internal/StartupMethods.cs
  21. 35 54
      src/Hosting/Hosting/src/Internal/WebHost.cs
  22. 2 2
      src/Hosting/Hosting/src/Internal/WebHostLifetime.cs
  23. 0 5
      src/Hosting/Hosting/src/Internal/WebHostOptions.cs
  24. 1 0
      src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj
  25. 8 8
      src/Hosting/Hosting/src/PublicAPI.Shipped.txt
  26. 2 2
      src/Hosting/Hosting/src/Startup/ConventionBasedStartup.cs
  27. 1 1
      src/Hosting/Hosting/src/Startup/StartupBase.cs
  28. 2 1
      src/Hosting/Hosting/src/StaticWebAssets/StaticWebAssetsFileProvider.cs
  29. 1 1
      src/Hosting/Hosting/src/StaticWebAssets/StaticWebAssetsReader.cs
  30. 1 1
      src/Hosting/Hosting/src/WebHostBuilder.cs
  31. 19 4
      src/Hosting/Hosting/test/HostingEnvironmentExtensionsTests.cs
  32. 15 8
      src/Hosting/Hosting/test/WebHostBuilderTests.cs
  33. 3 3
      src/Hosting/Hosting/test/WebHostConfigurationsTests.cs
  34. 1 5
      src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
  35. 3 23
      src/Servers/IIS/IIS/src/StartupHook.cs
  36. 71 68
      src/Shared/ErrorPage/ErrorPage.Designer.cs
  37. 18 7
      src/Shared/ErrorPage/ErrorPageModel.cs
  38. 58 0
      src/Shared/ErrorPage/ErrorPageModelBuilder.cs
  39. 7 4
      src/Shared/ErrorPage/Views/ErrorPage.cshtml
  40. 17 11
      src/Shared/RazorViews/BaseView.cs
  41. 18 4
      src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs
  42. 12 14
      src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs
  43. 17 6
      src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs
  44. 12 4
      src/Shared/StackTrace/StackFrame/StackFrameInfo.cs
  45. 6 4
      src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs
  46. 12 18
      src/Shared/StackTrace/StackFrame/StackTraceHelper.cs
  47. 8 6
      src/Shared/TypeNameHelper/TypeNameHelper.cs

+ 4 - 1
src/DataProtection/DataProtection/test/HostingTests.cs

@@ -82,12 +82,15 @@ namespace Microsoft.AspNetCore.DataProtection.Test
                 .Throws(new NotSupportedException("This mock doesn't actually work, but shouldn't kill the server"))
                 .Verifiable();
 
+            var mockServer = new Mock<IServer>();
+            mockServer.Setup(m => m.Features).Returns(new FeatureCollection());
+
             var builder = new HostBuilder()
                 .ConfigureServices(s =>
                     s.AddDataProtection()
                     .Services
                     .Replace(ServiceDescriptor.Singleton(mockKeyRing.Object))
-                    .AddSingleton(Mock.Of<IServer>()))
+                    .AddSingleton(mockServer.Object))
                     .ConfigureWebHost(b => b.UseStartup<TestStartup>());
 
             using (var host = builder.Build())

+ 14 - 14
src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs

@@ -23,11 +23,11 @@ namespace Microsoft.AspNetCore.Hosting
     {
         private readonly IHostBuilder _builder;
         private readonly IConfiguration _config;
-        private object _startupObject;
+        private object? _startupObject;
         private readonly object _startupKey = new object();
 
-        private AggregateException _hostingStartupErrors;
-        private HostingStartupWebHostBuilder _hostingStartupWebHostBuilder;
+        private AggregateException? _hostingStartupErrors;
+        private HostingStartupWebHostBuilder? _hostingStartupWebHostBuilder;
 
         public GenericWebHostBuilder(IHostBuilder builder, WebHostBuilderOptions options)
         {
@@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Hosting
 
         private void ExecuteHostingStartups()
         {
-            var webHostOptions = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name);
+            var webHostOptions = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name ?? string.Empty);
 
             if (webHostOptions.PreventHostingStartup)
             {
@@ -141,7 +141,7 @@ namespace Microsoft.AspNetCore.Hosting
 
                     foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
                     {
-                        var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
+                        var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType)!;
                         hostingStartup.Configure(_hostingStartupWebHostBuilder);
                     }
                 }
@@ -240,13 +240,13 @@ namespace Microsoft.AspNetCore.Hosting
         }
 
         [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2006:UnrecognizedReflectionPattern", Justification = "We need to call a generic method on IHostBuilder.")]
-        private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, HostBuilderContext context, IServiceCollection services, object instance = null)
+        private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, HostBuilderContext context, IServiceCollection services, object? instance = null)
         {
             var webHostBuilderContext = GetWebHostBuilderContext(context);
             var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];
 
-            ExceptionDispatchInfo startupError = null;
-            ConfigureBuilder configureBuilder = null;
+            ExceptionDispatchInfo? startupError = null;
+            ConfigureBuilder? configureBuilder = null;
 
             try
             {
@@ -281,12 +281,12 @@ namespace Microsoft.AspNetCore.Hosting
                     var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType);
 
                     // Get the private ConfigureContainer method on this type then close over the container type
-                    var configureCallback = typeof(GenericWebHostBuilder).GetMethod(nameof(ConfigureContainerImpl), BindingFlags.NonPublic | BindingFlags.Instance)
+                    var configureCallback = typeof(GenericWebHostBuilder).GetMethod(nameof(ConfigureContainerImpl), BindingFlags.NonPublic | BindingFlags.Instance)!
                                                      .MakeGenericMethod(containerType)
                                                      .CreateDelegate(actionType, this);
 
                     // _builder.ConfigureContainer<T>(ConfigureContainer);
-                    typeof(IHostBuilder).GetMethod(nameof(IHostBuilder.ConfigureContainer))
+                    typeof(IHostBuilder).GetMethod(nameof(IHostBuilder.ConfigureContainer))!
                         .MakeGenericMethod(containerType)
                         .InvokeWithoutWrappingExceptions(_builder, new object[] { configureCallback });
                 }
@@ -316,7 +316,7 @@ namespace Microsoft.AspNetCore.Hosting
             });
         }
 
-        private void ConfigureContainerImpl<TContainer>(HostBuilderContext context, TContainer container)
+        private void ConfigureContainerImpl<TContainer>(HostBuilderContext context, TContainer container) where TContainer : notnull
         {
             var instance = context.Properties[_startupKey];
             var builder = (ConfigureContainerBuilder)context.Properties[typeof(ConfigureContainerBuilder)];
@@ -347,7 +347,7 @@ namespace Microsoft.AspNetCore.Hosting
         {
             if (!context.Properties.TryGetValue(typeof(WebHostBuilderContext), out var contextVal))
             {
-                var options = new WebHostOptions(context.Configuration, Assembly.GetEntryAssembly()?.GetName().Name);
+                var options = new WebHostOptions(context.Configuration, Assembly.GetEntryAssembly()?.GetName().Name ?? string.Empty);
                 var webHostBuilderContext = new WebHostBuilderContext
                 {
                     Configuration = context.Configuration,
@@ -370,7 +370,7 @@ namespace Microsoft.AspNetCore.Hosting
             return _config[key];
         }
 
-        public IWebHostBuilder UseSetting(string key, string value)
+        public IWebHostBuilder UseSetting(string key, string? value)
         {
             _config[key] = value;
             return this;
@@ -386,7 +386,7 @@ namespace Microsoft.AspNetCore.Hosting
                 _context = context;
             }
 
-            public object GetService(Type serviceType)
+            public object? GetService(Type serviceType)
             {
                 // The implementation of the HostingEnvironment supports both interfaces
 #pragma warning disable CS0618 // Type or member is obsolete

+ 3 - 3
src/Hosting/Hosting/src/GenericHost/GenericWebHostServiceOptions.cs

@@ -8,10 +8,10 @@ namespace Microsoft.AspNetCore.Hosting
 {
     internal class GenericWebHostServiceOptions
     {
-        public Action<IApplicationBuilder> ConfigureApplication { get; set; }
+        public Action<IApplicationBuilder>? ConfigureApplication { get; set; }
 
-        public WebHostOptions WebHostOptions { get; set; }
+        public WebHostOptions WebHostOptions { get; set; } = default!; // Always set when options resolved by DI
 
-        public AggregateException HostingStartupExceptions { get; set; }
+        public AggregateException? HostingStartupExceptions { get; set; }
     }
 }

+ 8 - 55
src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs

@@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Hosting.Server.Features;
 using Microsoft.AspNetCore.Hosting.Views;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.FileProviders;
 using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
@@ -64,14 +65,14 @@ namespace Microsoft.AspNetCore.Hosting
         {
             HostingEventSource.Log.HostStart();
 
-            var serverAddressesFeature = Server.Features?.Get<IServerAddressesFeature>();
+            var serverAddressesFeature = Server.Features.Get<IServerAddressesFeature>();
             var addresses = serverAddressesFeature?.Addresses;
             if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0)
             {
                 var urls = Configuration[WebHostDefaults.ServerUrlsKey];
                 if (!string.IsNullOrEmpty(urls))
                 {
-                    serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey);
+                    serverAddressesFeature!.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey);
 
                     foreach (var value in urls.Split(';', StringSplitOptions.RemoveEmptyEntries))
                     {
@@ -80,11 +81,11 @@ namespace Microsoft.AspNetCore.Hosting
                 }
             }
 
-            RequestDelegate application = null;
+            RequestDelegate? application = null;
 
             try
             {
-                Action<IApplicationBuilder> configure = Options.ConfigureApplication;
+                var configure = Options.ConfigureApplication;
 
                 if (configure == null)
                 {
@@ -112,7 +113,9 @@ namespace Microsoft.AspNetCore.Hosting
                     throw;
                 }
 
-                application = BuildErrorPageApplication(ex);
+                var showDetailedErrors = HostingEnvironment.IsDevelopment() || Options.WebHostOptions.DetailedErrors;
+
+                application = ErrorPageBuilder.BuildErrorPageApplication(HostingEnvironment.ContentRootFileProvider, Logger, showDetailedErrors, ex);
             }
 
             var httpApplication = new HostingApplication(application, Logger, DiagnosticListener, HttpContextFactory);
@@ -144,56 +147,6 @@ namespace Microsoft.AspNetCore.Hosting
             }
         }
 
-        private RequestDelegate BuildErrorPageApplication(Exception exception)
-        {
-            if (exception is TargetInvocationException tae)
-            {
-                exception = tae.InnerException;
-            }
-
-            var showDetailedErrors = HostingEnvironment.IsDevelopment() || Options.WebHostOptions.DetailedErrors;
-
-            var model = new ErrorPageModel
-            {
-                RuntimeDisplayName = RuntimeInformation.FrameworkDescription
-            };
-            var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).Assembly;
-            var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
-            var clrVersion = assemblyVersion;
-            model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
-            var currentAssembly = typeof(ErrorPage).Assembly;
-            model.CurrentAssemblyVesion = currentAssembly
-                .GetCustomAttribute<AssemblyInformationalVersionAttribute>()
-                .InformationalVersion;
-            model.ClrVersion = clrVersion;
-            model.OperatingSystemDescription = RuntimeInformation.OSDescription;
-            model.ShowRuntimeDetails = showDetailedErrors;
-
-            if (showDetailedErrors)
-            {
-                var exceptionDetailProvider = new ExceptionDetailsProvider(
-                    HostingEnvironment.ContentRootFileProvider,
-                    Logger,
-                    sourceCodeLineCount: 6);
-
-                model.ErrorDetails = exceptionDetailProvider.GetDetails(exception);
-            }
-            else
-            {
-                model.ErrorDetails = Array.Empty<ExceptionDetails>();
-            }
-
-            var errorPage = new ErrorPage(model);
-            return context =>
-            {
-                context.Response.StatusCode = 500;
-                context.Response.Headers[HeaderNames.CacheControl] = "no-cache,no-store";
-                context.Response.Headers[HeaderNames.Pragma] = "no-cache";
-                context.Response.ContentType = "text/html; charset=utf-8";
-                return errorPage.ExecuteAsync(context);
-            };
-        }
-
         public async Task StopAsync(CancellationToken cancellationToken)
         {
             try

+ 3 - 3
src/Hosting/Hosting/src/GenericHost/HostingStartupWebHostBuilder.cs

@@ -15,8 +15,8 @@ namespace Microsoft.AspNetCore.Hosting
     internal class HostingStartupWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider
     {
         private readonly GenericWebHostBuilder _builder;
-        private Action<WebHostBuilderContext, IConfigurationBuilder> _configureConfiguration;
-        private Action<WebHostBuilderContext, IServiceCollection> _configureServices;
+        private Action<WebHostBuilderContext, IConfigurationBuilder>? _configureConfiguration;
+        private Action<WebHostBuilderContext, IServiceCollection>? _configureServices;
 
         public HostingStartupWebHostBuilder(GenericWebHostBuilder builder)
         {
@@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Hosting
 
         public string GetSetting(string key) => _builder.GetSetting(key);
 
-        public IWebHostBuilder UseSetting(string key, string value)
+        public IWebHostBuilder UseSetting(string key, string? value)
         {
             _builder.UseSetting(key, value);
             return this;

+ 3 - 3
src/Hosting/Hosting/src/Internal/ConfigureBuilder.cs

@@ -18,9 +18,9 @@ namespace Microsoft.AspNetCore.Hosting
 
         public MethodInfo MethodInfo { get; }
 
-        public Action<IApplicationBuilder> Build(object instance) => builder => Invoke(instance, builder);
+        public Action<IApplicationBuilder> Build(object? instance) => builder => Invoke(instance, builder);
 
-        private void Invoke(object instance, IApplicationBuilder builder)
+        private void Invoke(object? instance, IApplicationBuilder builder)
         {
             // Create a scope for Configure, this allows creating scoped dependencies
             // without the hassle of manually creating a scope.
@@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Hosting
                                 parameterInfo.ParameterType.FullName,
                                 parameterInfo.Name,
                                 MethodInfo.Name,
-                                MethodInfo.DeclaringType.FullName), ex);
+                                MethodInfo.DeclaringType?.FullName), ex);
                         }
                     }
                 }

+ 5 - 2
src/Hosting/Hosting/src/Internal/ConfigureContainerBuilder.cs

@@ -2,18 +2,19 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Diagnostics;
 using System.Reflection;
 
 namespace Microsoft.AspNetCore.Hosting
 {
     internal class ConfigureContainerBuilder
     {
-        public ConfigureContainerBuilder(MethodInfo configureContainerMethod)
+        public ConfigureContainerBuilder(MethodInfo? configureContainerMethod)
         {
             MethodInfo = configureContainerMethod;
         }
 
-        public MethodInfo MethodInfo { get; }
+        public MethodInfo? MethodInfo { get; }
 
         public Func<Action<object>, Action<object>> ConfigureContainerFilters { get; set; } = f => f;
 
@@ -21,6 +22,8 @@ namespace Microsoft.AspNetCore.Hosting
 
         public Type GetContainerType()
         {
+            Debug.Assert(MethodInfo != null, "Shouldn't be called when there is no Configure method.");
+
             var parameters = MethodInfo.GetParameters();
             if (parameters.Length != 1)
             {

+ 7 - 7
src/Hosting/Hosting/src/Internal/ConfigureServicesBuilder.cs

@@ -10,25 +10,25 @@ namespace Microsoft.AspNetCore.Hosting
 {
     internal class ConfigureServicesBuilder
     {
-        public ConfigureServicesBuilder(MethodInfo configureServices)
+        public ConfigureServicesBuilder(MethodInfo? configureServices)
         {
             MethodInfo = configureServices;
         }
 
-        public MethodInfo MethodInfo { get; }
+        public MethodInfo? MethodInfo { get; }
 
-        public Func<Func<IServiceCollection, IServiceProvider>, Func<IServiceCollection, IServiceProvider>> StartupServiceFilters { get; set; } = f => f;
+        public Func<Func<IServiceCollection, IServiceProvider?>, Func<IServiceCollection, IServiceProvider?>> StartupServiceFilters { get; set; } = f => f;
 
-        public Func<IServiceCollection, IServiceProvider> Build(object instance) => services => Invoke(instance, services);
+        public Func<IServiceCollection, IServiceProvider?> Build(object instance) => services => Invoke(instance, services);
 
-        private IServiceProvider Invoke(object instance, IServiceCollection services)
+        private IServiceProvider? Invoke(object instance, IServiceCollection services)
         {
             return StartupServiceFilters(Startup)(services);
 
-            IServiceProvider Startup(IServiceCollection serviceCollection) => InvokeCore(instance, serviceCollection);
+            IServiceProvider? Startup(IServiceCollection serviceCollection) => InvokeCore(instance, serviceCollection);
         }
 
-        private IServiceProvider InvokeCore(object instance, IServiceCollection services)
+        private IServiceProvider? InvokeCore(object instance, IServiceCollection services)
         {
             if (MethodInfo == null)
             {

+ 43 - 0
src/Hosting/Hosting/src/Internal/ErrorPageBuilder.cs

@@ -0,0 +1,43 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Microsoft.AspNetCore.Hosting.Views;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.StackTrace.Sources;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.Hosting
+{
+    internal static class ErrorPageBuilder
+    {
+        public static RequestDelegate BuildErrorPageApplication(
+            IFileProvider contentRootFileProvider,
+            ILogger logger,
+            bool showDetailedErrors,
+            Exception exception)
+        {
+            if (exception is TargetInvocationException tae)
+            {
+                exception = tae.InnerException!;
+            }
+
+            var model = ErrorPageModelBuilder.CreateErrorPageModel(contentRootFileProvider, logger, showDetailedErrors, exception);
+
+            var errorPage = new ErrorPage(model);
+            return context =>
+            {
+                context.Response.StatusCode = 500;
+                context.Response.Headers[HeaderNames.CacheControl] = "no-cache,no-store";
+                context.Response.Headers[HeaderNames.Pragma] = "no-cache";
+                context.Response.ContentType = "text/html; charset=utf-8";
+                return errorPage.ExecuteAsync(context);
+            };
+        }
+    }
+}

+ 1 - 1
src/Hosting/Hosting/src/Internal/HostedServiceExecutor.cs

@@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Hosting
 
         private async Task ExecuteAsync(Func<IHostedService, Task> callback, bool throwOnFirstFailure = true)
         {
-            List<Exception> exceptions = null;
+            List<Exception>? exceptions = null;
 
             foreach (var service in _services)
             {

+ 11 - 11
src/Hosting/Hosting/src/Internal/HostingApplication.cs

@@ -15,8 +15,8 @@ namespace Microsoft.AspNetCore.Hosting
     internal class HostingApplication : IHttpApplication<HostingApplication.Context>
     {
         private readonly RequestDelegate _application;
-        private readonly IHttpContextFactory _httpContextFactory;
-        private readonly DefaultHttpContextFactory _defaultHttpContextFactory;
+        private readonly IHttpContextFactory? _httpContextFactory;
+        private readonly DefaultHttpContextFactory? _defaultHttpContextFactory;
         private HostingApplicationDiagnostics _diagnostics;
 
         public HostingApplication(
@@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Hosting
             HttpContext httpContext;
             if (_defaultHttpContextFactory != null)
             {
-                var defaultHttpContext = (DefaultHttpContext)hostContext.HttpContext;
+                var defaultHttpContext = (DefaultHttpContext?)hostContext.HttpContext;
                 if (defaultHttpContext is null)
                 {
                     httpContext = _defaultHttpContextFactory.Create(contextFeatures);
@@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Hosting
             }
             else
             {
-                httpContext = _httpContextFactory.Create(contextFeatures);
+                httpContext = _httpContextFactory!.Create(contextFeatures);
                 hostContext.HttpContext = httpContext;
             }
 
@@ -84,13 +84,13 @@ namespace Microsoft.AspNetCore.Hosting
         // Execute the request
         public Task ProcessRequestAsync(Context context)
         {
-            return _application(context.HttpContext);
+            return _application(context.HttpContext!);
         }
 
         // Clean up the request
         public void DisposeContext(Context context, Exception exception)
         {
-            var httpContext = context.HttpContext;
+            var httpContext = context.HttpContext!;
             _diagnostics.RequestEnd(httpContext, exception, context);
 
             if (_defaultHttpContextFactory != null)
@@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Hosting
             }
             else
             {
-                _httpContextFactory.Dispose(httpContext);
+                _httpContextFactory!.Dispose(httpContext);
             }
 
             _diagnostics.ContextDisposed(context);
@@ -119,10 +119,10 @@ namespace Microsoft.AspNetCore.Hosting
 
         internal class Context
         {
-            public HttpContext HttpContext { get; set; }
-            public IDisposable Scope { get; set; }
-            public Activity Activity { get; set; }
-            internal HostingRequestStartingLog StartLog { get; set; }
+            public HttpContext? HttpContext { get; set; }
+            public IDisposable? Scope { get; set; }
+            public Activity? Activity { get; set; }
+            internal HostingRequestStartingLog? StartLog { get; set; }
 
             public long StartTimestamp { get; set; }
             internal bool HasDiagnosticListener { get; set; }

+ 1 - 1
src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs

@@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Hosting
         private void LogRequestStarting(HostingApplication.Context context)
         {
             // IsEnabled is checked in the caller, so if we are here just log
-            var startLog = new HostingRequestStartingLog(context.HttpContext);
+            var startLog = new HostingRequestStartingLog(context.HttpContext!);
             context.StartLog = startLog;
 
             _logger.Log(

+ 5 - 5
src/Hosting/Hosting/src/Internal/HostingEnvironment.cs

@@ -11,14 +11,14 @@ namespace Microsoft.AspNetCore.Hosting
     {
         public string EnvironmentName { get; set; } = Extensions.Hosting.Environments.Production;
 
-        public string ApplicationName { get; set; }
+        public string ApplicationName { get; set; } = default!;
 
-        public string WebRootPath { get; set; }
+        public string WebRootPath { get; set; } = default!;
 
-        public IFileProvider WebRootFileProvider { get; set; }
+        public IFileProvider WebRootFileProvider { get; set; } = default!;
 
-        public string ContentRootPath { get; set; }
+        public string ContentRootPath { get; set; } = default!;
 
-        public IFileProvider ContentRootFileProvider { get; set; }
+        public IFileProvider ContentRootFileProvider { get; set; } = default!;
     }
 }

+ 4 - 4
src/Hosting/Hosting/src/Internal/HostingEventSource.cs

@@ -12,10 +12,10 @@ namespace Microsoft.AspNetCore.Hosting
     {
         public static readonly HostingEventSource Log = new HostingEventSource();
 
-        private IncrementingPollingCounter _requestsPerSecondCounter;
-        private PollingCounter _totalRequestsCounter;
-        private PollingCounter _failedRequestsCounter;
-        private PollingCounter _currentRequestsCounter;
+        private IncrementingPollingCounter? _requestsPerSecondCounter;
+        private PollingCounter? _totalRequestsCounter;
+        private PollingCounter? _failedRequestsCounter;
+        private PollingCounter? _currentRequestsCounter;
 
         private long _totalRequests;
         private long _currentRequests;

+ 5 - 2
src/Hosting/Hosting/src/Internal/HostingLoggerExtensions.cs

@@ -41,7 +41,10 @@ namespace Microsoft.AspNetCore.Hosting
             {
                 foreach (var ex in reflectionTypeLoadException.LoaderExceptions)
                 {
-                    message = message + Environment.NewLine + ex.Message;
+                    if (ex != null)
+                    {
+                        message = message + Environment.NewLine + ex.Message;
+                    }
                 }
             }
 
@@ -97,7 +100,7 @@ namespace Microsoft.AspNetCore.Hosting
             private readonly string _path;
             private readonly string _traceIdentifier;
 
-            private string _cachedToString;
+            private string? _cachedToString;
 
             public int Count
             {

+ 20 - 15
src/Hosting/Hosting/src/Internal/HostingRequestFinishedLog.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
 using Microsoft.AspNetCore.Http;
 
@@ -11,37 +12,39 @@ namespace Microsoft.AspNetCore.Hosting
 {
     using static HostingRequestStartingLog;
 
-    internal class HostingRequestFinishedLog : IReadOnlyList<KeyValuePair<string, object>>
+    internal class HostingRequestFinishedLog : IReadOnlyList<KeyValuePair<string, object?>>
     {
         internal static readonly Func<object, Exception, string> Callback = (state, exception) => ((HostingRequestFinishedLog)state).ToString();
 
         private readonly HostingApplication.Context _context;
 
-        private string _cachedToString;
+        private string? _cachedToString;
         public TimeSpan Elapsed { get; }
 
         public int Count => 11;
 
-        public KeyValuePair<string, object> this[int index]
+        public KeyValuePair<string, object?> this[int index]
         {
             get
             {
+                Debug.Assert(_context.HttpContext != null);
+
                 var request = _context.HttpContext.Request;
                 var response = _context.HttpContext.Response;
 
                 return index switch
                 {
-                    0 => new KeyValuePair<string, object>("ElapsedMilliseconds", Elapsed.TotalMilliseconds),
-                    1 => new KeyValuePair<string, object>(nameof(response.StatusCode), response.StatusCode),
-                    2 => new KeyValuePair<string, object>(nameof(response.ContentType), response.ContentType),
-                    3 => new KeyValuePair<string, object>(nameof(response.ContentLength), response.ContentLength),
-                    4 => new KeyValuePair<string, object>(nameof(request.Protocol), request.Protocol),
-                    5 => new KeyValuePair<string, object>(nameof(request.Method), request.Method),
-                    6 => new KeyValuePair<string, object>(nameof(request.Scheme), request.Scheme),
-                    7 => new KeyValuePair<string, object>(nameof(request.Host), request.Host.Value),
-                    8 => new KeyValuePair<string, object>(nameof(request.PathBase), request.PathBase.Value),
-                    9 => new KeyValuePair<string, object>(nameof(request.Path), request.Path.Value),
-                    10 => new KeyValuePair<string, object>(nameof(request.QueryString), request.QueryString.Value),
+                    0 => new KeyValuePair<string, object?>("ElapsedMilliseconds", Elapsed.TotalMilliseconds),
+                    1 => new KeyValuePair<string, object?>(nameof(response.StatusCode), response.StatusCode),
+                    2 => new KeyValuePair<string, object?>(nameof(response.ContentType), response.ContentType),
+                    3 => new KeyValuePair<string, object?>(nameof(response.ContentLength), response.ContentLength),
+                    4 => new KeyValuePair<string, object?>(nameof(request.Protocol), request.Protocol),
+                    5 => new KeyValuePair<string, object?>(nameof(request.Method), request.Method),
+                    6 => new KeyValuePair<string, object?>(nameof(request.Scheme), request.Scheme),
+                    7 => new KeyValuePair<string, object?>(nameof(request.Host), request.Host.Value),
+                    8 => new KeyValuePair<string, object?>(nameof(request.PathBase), request.PathBase.Value),
+                    9 => new KeyValuePair<string, object?>(nameof(request.Path), request.Path.Value),
+                    10 => new KeyValuePair<string, object?>(nameof(request.QueryString), request.QueryString.Value),
                     _ => throw new IndexOutOfRangeException(nameof(index)),
                 };
             }
@@ -57,6 +60,8 @@ namespace Microsoft.AspNetCore.Hosting
         {
             if (_cachedToString == null)
             {
+                Debug.Assert(_context.HttpContext != null && _context.StartLog != null);
+
                 var response = _context.HttpContext.Response;
                 _cachedToString = $"Request finished {_context.StartLog.ToStringWithoutPreamble()} - {response.StatusCode.ToString(CultureInfo.InvariantCulture)} {ValueOrEmptyMarker(response.ContentLength)} {EscapedValueOrEmptyMarker(response.ContentType)} {Elapsed.TotalMilliseconds.ToString("0.0000", CultureInfo.InvariantCulture)}ms";
             }
@@ -64,7 +69,7 @@ namespace Microsoft.AspNetCore.Hosting
             return _cachedToString;
         }
 
-        public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
+        public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
         {
             for (var i = 0; i < Count; i++)
             {

+ 13 - 13
src/Hosting/Hosting/src/Internal/HostingRequestStartingLog.cs

@@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Http;
 
 namespace Microsoft.AspNetCore.Hosting
 {
-    internal class HostingRequestStartingLog : IReadOnlyList<KeyValuePair<string, object>>
+    internal class HostingRequestStartingLog : IReadOnlyList<KeyValuePair<string, object?>>
     {
         private const string LogPreamble = "Request starting ";
         private const string EmptyEntry = "-";
@@ -19,21 +19,21 @@ namespace Microsoft.AspNetCore.Hosting
 
         private readonly HttpRequest _request;
 
-        private string _cachedToString;
+        private string? _cachedToString;
 
         public int Count => 9;
 
-        public KeyValuePair<string, object> this[int index] => index switch
+        public KeyValuePair<string, object?> this[int index] => index switch
         {
-            0 => new KeyValuePair<string, object>(nameof(_request.Protocol), _request.Protocol),
-            1 => new KeyValuePair<string, object>(nameof(_request.Method), _request.Method),
-            2 => new KeyValuePair<string, object>(nameof(_request.ContentType), _request.ContentType),
-            3 => new KeyValuePair<string, object>(nameof(_request.ContentLength), _request.ContentLength),
-            4 => new KeyValuePair<string, object>(nameof(_request.Scheme), _request.Scheme),
-            5 => new KeyValuePair<string, object>(nameof(_request.Host), _request.Host.Value),
-            6 => new KeyValuePair<string, object>(nameof(_request.PathBase), _request.PathBase.Value),
-            7 => new KeyValuePair<string, object>(nameof(_request.Path), _request.Path.Value),
-            8 => new KeyValuePair<string, object>(nameof(_request.QueryString), _request.QueryString.Value),
+            0 => new KeyValuePair<string, object?>(nameof(_request.Protocol), _request.Protocol),
+            1 => new KeyValuePair<string, object?>(nameof(_request.Method), _request.Method),
+            2 => new KeyValuePair<string, object?>(nameof(_request.ContentType), _request.ContentType),
+            3 => new KeyValuePair<string, object?>(nameof(_request.ContentLength), _request.ContentLength),
+            4 => new KeyValuePair<string, object?>(nameof(_request.Scheme), _request.Scheme),
+            5 => new KeyValuePair<string, object?>(nameof(_request.Host), _request.Host.Value),
+            6 => new KeyValuePair<string, object?>(nameof(_request.PathBase), _request.PathBase.Value),
+            7 => new KeyValuePair<string, object?>(nameof(_request.Path), _request.Path.Value),
+            8 => new KeyValuePair<string, object?>(nameof(_request.QueryString), _request.QueryString.Value),
             _ => throw new IndexOutOfRangeException(nameof(index)),
         };
 
@@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Hosting
             return _cachedToString;
         }
 
-        public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
+        public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
         {
             for (var i = 0; i < Count; i++)
             {

+ 1 - 1
src/Hosting/Hosting/src/Internal/MethodInfoExtensions.cs

@@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Hosting
     internal static class MethodInfoExtensions
     {
         // This version of MethodInfo.Invoke removes TargetInvocationExceptions
-        public static object InvokeWithoutWrappingExceptions(this MethodInfo methodInfo, object obj, object[] parameters)
+        public static object? InvokeWithoutWrappingExceptions(this MethodInfo methodInfo, object? obj, object?[] parameters)
         {
             // These are the default arguments passed when methodInfo.Invoke(obj, parameters) are called. We do the same
             // here but specify BindingFlags.DoNotWrapExceptions to avoid getting TAE (TargetInvocationException)

+ 10 - 10
src/Hosting/Hosting/src/Internal/StartupLoader.cs

@@ -39,14 +39,14 @@ namespace Microsoft.AspNetCore.Hosting
         //
         // If the Startup class ConfigureServices returns an <see cref="IServiceProvider"/> and there is at least an <see cref="IStartupConfigureServicesFilter"/> registered we
         // throw as the filters can't be applied.
-        public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, [DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName, object instance = null)
+        public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, [DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName, object? instance = null)
         {
             var configureMethod = FindConfigureDelegate(startupType, environmentName);
 
             var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName);
             var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName);
 
-            if (instance == null && (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)))
+            if (instance == null && (!configureMethod.MethodInfo.IsStatic || (servicesMethod?.MethodInfo != null && !servicesMethod.MethodInfo.IsStatic)))
             {
                 instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType);
             }
@@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Hosting
                 hostingServiceProvider,
                 servicesMethod,
                 configureContainerMethod,
-                instance);
+                instance)!;
 
             return new StartupMethods(instance, configureMethod.Build(instance), builder.Build());
         }
@@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Hosting
             public abstract Func<IServiceCollection, IServiceProvider> Build();
         }
 
-        private class ConfigureServicesDelegateBuilder<TContainerBuilder> : ConfigureServicesDelegateBuilder
+        private class ConfigureServicesDelegateBuilder<TContainerBuilder> : ConfigureServicesDelegateBuilder where TContainerBuilder : notnull
         {
             public ConfigureServicesDelegateBuilder(
                 IServiceProvider hostingServiceProvider,
@@ -117,7 +117,7 @@ namespace Microsoft.AspNetCore.Hosting
             }
 
             Func<IServiceCollection, IServiceProvider> ConfigureServices(
-                Func<IServiceCollection, IServiceProvider> configureServicesCallback,
+                Func<IServiceCollection, IServiceProvider?> configureServicesCallback,
                 Action<object> configureContainerCallback)
             {
                 return ConfigureServicesWithContainerConfiguration;
@@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Hosting
                 IServiceProvider ConfigureServicesWithContainerConfiguration(IServiceCollection services)
                 {
                     // Call ConfigureServices, if that returned an IServiceProvider, we're done
-                    IServiceProvider applicationServiceProvider = configureServicesCallback.Invoke(services);
+                    var applicationServiceProvider = configureServicesCallback.Invoke(services);
 
                     if (applicationServiceProvider != null)
                     {
@@ -152,11 +152,11 @@ namespace Microsoft.AspNetCore.Hosting
                 }
             }
 
-            private Func<IServiceCollection, IServiceProvider> BuildStartupServicesFilterPipeline(Func<IServiceCollection, IServiceProvider> startup)
+            private Func<IServiceCollection, IServiceProvider?> BuildStartupServicesFilterPipeline(Func<IServiceCollection, IServiceProvider?> startup)
             {
                 return RunPipeline;
 
-                IServiceProvider RunPipeline(IServiceCollection services)
+                IServiceProvider? RunPipeline(IServiceCollection services)
                 {
 #pragma warning disable CS0612 // Type or member is obsolete
                     var filters = HostingServiceProvider.GetRequiredService<IEnumerable<IStartupConfigureServicesFilter>>().Reverse().ToArray();
@@ -281,7 +281,7 @@ namespace Microsoft.AspNetCore.Hosting
 
         internal static ConfigureBuilder FindConfigureDelegate([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string environmentName)
         {
-            var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true);
+            var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true)!;
             return new ConfigureBuilder(configureMethod);
         }
 
@@ -303,7 +303,7 @@ namespace Microsoft.AspNetCore.Hosting
             return new ConfigureServicesBuilder(servicesMethod);
         }
 
-        private static MethodInfo FindMethod([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true)
+        private static MethodInfo? FindMethod([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, string methodName, string environmentName, Type? returnType = null, bool required = true)
         {
             var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName);
             var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, "");

+ 2 - 2
src/Hosting/Hosting/src/Internal/StartupMethods.cs

@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Hosting
 {
     internal class StartupMethods
     {
-        public StartupMethods(object instance, Action<IApplicationBuilder> configure, Func<IServiceCollection, IServiceProvider> configureServices)
+        public StartupMethods(object? instance, Action<IApplicationBuilder> configure, Func<IServiceCollection, IServiceProvider> configureServices)
         {
             Debug.Assert(configure != null);
             Debug.Assert(configureServices != null);
@@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Hosting
             ConfigureServicesDelegate = configureServices;
         }
 
-        public object StartupInstance { get; }
+        public object? StartupInstance { get; }
         public Func<IServiceCollection, IServiceProvider> ConfigureServicesDelegate { get; }
         public Action<IApplicationBuilder> ConfigureDelegate { get; }
 

+ 35 - 54
src/Hosting/Hosting/src/Internal/WebHost.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reflection;
 using System.Runtime.ExceptionServices;
@@ -32,17 +33,17 @@ namespace Microsoft.AspNetCore.Hosting
         private static readonly string DeprecatedServerUrlsKey = "server.urls";
 
         private readonly IServiceCollection _applicationServiceCollection;
-        private IStartup _startup;
-        private ApplicationLifetime _applicationLifetime;
-        private HostedServiceExecutor _hostedServiceExecutor;
+        private IStartup? _startup;
+        private ApplicationLifetime? _applicationLifetime;
+        private HostedServiceExecutor? _hostedServiceExecutor;
 
         private readonly IServiceProvider _hostingServiceProvider;
         private readonly WebHostOptions _options;
         private readonly IConfiguration _config;
-        private readonly AggregateException _hostingStartupErrors;
+        private readonly AggregateException? _hostingStartupErrors;
 
-        private IServiceProvider _applicationServices;
-        private ExceptionDispatchInfo _applicationServicesException;
+        private IServiceProvider? _applicationServices;
+        private ExceptionDispatchInfo? _applicationServicesException;
         private ILogger _logger =  NullLogger.Instance;
 
         private bool _stopped;
@@ -51,14 +52,14 @@ namespace Microsoft.AspNetCore.Hosting
         // Used for testing only
         internal WebHostOptions Options => _options;
 
-        private IServer Server { get; set; }
+        private IServer? Server { get; set; }
 
         public WebHost(
             IServiceCollection appServices,
             IServiceProvider hostingServiceProvider,
             WebHostOptions options,
             IConfiguration config,
-            AggregateException hostingStartupErrors)
+            AggregateException? hostingStartupErrors)
         {
             if (appServices == null)
             {
@@ -82,6 +83,7 @@ namespace Microsoft.AspNetCore.Hosting
             _hostingServiceProvider = hostingServiceProvider;
             _applicationServiceCollection.AddSingleton<ApplicationLifetime>();
             // There's no way to to register multiple service types per definition. See https://github.com/aspnet/DependencyInjection/issues/360
+#pragma warning disable CS8634 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'class' constraint.
             _applicationServiceCollection.AddSingleton(services
                 => services.GetService<ApplicationLifetime>() as IHostApplicationLifetime);
 #pragma warning disable CS0618 // Type or member is obsolete
@@ -90,6 +92,7 @@ namespace Microsoft.AspNetCore.Hosting
             _applicationServiceCollection.AddSingleton(services
                 => services.GetService<ApplicationLifetime>() as Extensions.Hosting.IApplicationLifetime);
 #pragma warning restore CS0618 // Type or member is obsolete
+#pragma warning restore CS8634 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'class' constraint.
             _applicationServiceCollection.AddSingleton<HostedServiceExecutor>();
         }
 
@@ -97,6 +100,7 @@ namespace Microsoft.AspNetCore.Hosting
         {
             get
             {
+                Debug.Assert(_applicationServices != null, "Initialize must be called before accessing services.");
                 return _applicationServices;
             }
         }
@@ -106,7 +110,7 @@ namespace Microsoft.AspNetCore.Hosting
             get
             {
                 EnsureServer();
-                return Server?.Features;
+                return Server.Features;
             }
         }
 
@@ -141,6 +145,8 @@ namespace Microsoft.AspNetCore.Hosting
 
         public virtual async Task StartAsync(CancellationToken cancellationToken = default)
         {
+            Debug.Assert(_applicationServices != null, "Initialize must be called first.");
+
             HostingEventSource.Log.HostStart();
             _logger = _applicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("Microsoft.AspNetCore.Hosting.Diagnostics");
             _logger.Starting();
@@ -192,6 +198,7 @@ namespace Microsoft.AspNetCore.Hosting
             }
         }
 
+        [MemberNotNull(nameof(_startup))]
         private void EnsureStartup()
         {
             if (_startup != null)
@@ -199,16 +206,21 @@ namespace Microsoft.AspNetCore.Hosting
                 return;
             }
 
-            _startup = _hostingServiceProvider.GetService<IStartup>();
+            var startup = _hostingServiceProvider.GetService<IStartup>();
 
-            if (_startup == null)
+            if (startup == null)
             {
                 throw new InvalidOperationException($"No application configured. Please specify startup via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, injecting {nameof(IStartup)} or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
             }
+
+            _startup = startup;
         }
 
+        [MemberNotNull(nameof(Server))]
         private RequestDelegate BuildApplication()
         {
+            Debug.Assert(_applicationServices != null, "Initialize must be called first.");
+
             try
             {
                 _applicationServicesException?.Throw();
@@ -219,10 +231,13 @@ namespace Microsoft.AspNetCore.Hosting
                 builder.ApplicationServices = _applicationServices;
 
                 var startupFilters = _applicationServices.GetService<IEnumerable<IStartupFilter>>();
-                Action<IApplicationBuilder> configure = _startup.Configure;
-                foreach (var filter in startupFilters.Reverse())
+                Action<IApplicationBuilder> configure = _startup!.Configure;
+                if (startupFilters != null)
                 {
-                    configure = filter.Configure(configure);
+                    foreach (var filter in startupFilters.Reverse())
+                    {
+                        configure = filter.Configure(configure);
+                    }
                 }
 
                 configure(builder);
@@ -250,49 +265,15 @@ namespace Microsoft.AspNetCore.Hosting
                 var hostingEnv = _applicationServices.GetRequiredService<IHostEnvironment>();
                 var showDetailedErrors = hostingEnv.IsDevelopment() || _options.DetailedErrors;
 
-                var model = new ErrorPageModel
-                {
-                    RuntimeDisplayName = RuntimeInformation.FrameworkDescription
-                };
-                var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).Assembly;
-                var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
-                var clrVersion = assemblyVersion;
-                model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
-                var currentAssembly = typeof(ErrorPage).Assembly;
-                model.CurrentAssemblyVesion = currentAssembly
-                    .GetCustomAttribute<AssemblyInformationalVersionAttribute>()
-                    .InformationalVersion;
-                model.ClrVersion = clrVersion;
-                model.OperatingSystemDescription = RuntimeInformation.OSDescription;
-                model.ShowRuntimeDetails = showDetailedErrors;
-
-                if (showDetailedErrors)
-                {
-                    var exceptionDetailProvider = new ExceptionDetailsProvider(
-                        hostingEnv.ContentRootFileProvider,
-                        logger,
-                        sourceCodeLineCount: 6);
-
-                    model.ErrorDetails = exceptionDetailProvider.GetDetails(ex);
-                }
-                else
-                {
-                    model.ErrorDetails = new ExceptionDetails[0];
-                }
-
-                var errorPage = new ErrorPage(model);
-                return context =>
-                {
-                    context.Response.StatusCode = 500;
-                    context.Response.Headers[HeaderNames.CacheControl] = "no-cache,no-store";
-                    context.Response.Headers[HeaderNames.Pragma] = "no-cache";
-                    return errorPage.ExecuteAsync(context);
-                };
+                return ErrorPageBuilder.BuildErrorPageApplication(hostingEnv.ContentRootFileProvider, logger, showDetailedErrors, ex);
             }
         }
 
+        [MemberNotNull(nameof(Server))]
         private void EnsureServer()
         {
+            Debug.Assert(_applicationServices != null, "Initialize must be called first.");
+
             if (Server == null)
             {
                 Server = _applicationServices.GetRequiredService<IServer>();
@@ -304,7 +285,7 @@ namespace Microsoft.AspNetCore.Hosting
                     var urls = _config[WebHostDefaults.ServerUrlsKey] ?? _config[DeprecatedServerUrlsKey];
                     if (!string.IsNullOrEmpty(urls))
                     {
-                        serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(_config, WebHostDefaults.PreferHostingUrlsKey);
+                        serverAddressesFeature!.PreferHostingUrls = WebHostUtilities.ParseBool(_config, WebHostDefaults.PreferHostingUrlsKey);
 
                         foreach (var value in urls.Split(';', StringSplitOptions.RemoveEmptyEntries))
                         {
@@ -379,7 +360,7 @@ namespace Microsoft.AspNetCore.Hosting
             await DisposeServiceProviderAsync(_hostingServiceProvider).ConfigureAwait(false);
         }
 
-        private async ValueTask DisposeServiceProviderAsync(IServiceProvider serviceProvider)
+        private async ValueTask DisposeServiceProviderAsync(IServiceProvider? serviceProvider)
         {
             switch (serviceProvider)
             {

+ 2 - 2
src/Hosting/Hosting/src/Internal/WebHostLifetime.cs

@@ -42,14 +42,14 @@ namespace Microsoft.AspNetCore.Hosting
             Console.CancelKeyPress -= CancelKeyPress;
         }
 
-        private void CancelKeyPress(object sender, ConsoleCancelEventArgs eventArgs)
+        private void CancelKeyPress(object? sender, ConsoleCancelEventArgs eventArgs)
         {
             Shutdown();
             // Don't terminate the process immediately, wait for the Main thread to exit gracefully.
             eventArgs.Cancel = true;
         }
 
-        private void ProcessExit(object sender, EventArgs eventArgs)
+        private void ProcessExit(object? sender, EventArgs eventArgs)
         {
             Shutdown();
             if (_exitedGracefully)

+ 0 - 5
src/Hosting/Hosting/src/Internal/WebHostOptions.cs

@@ -11,11 +11,6 @@ namespace Microsoft.AspNetCore.Hosting
 {
     internal class WebHostOptions
     {
-        public WebHostOptions() { }
-
-        public WebHostOptions(IConfiguration configuration)
-            : this(configuration, string.Empty) { }
-
         public WebHostOptions(IConfiguration configuration, string applicationNameFallback)
         {
             if (configuration == null)

+ 1 - 0
src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj

@@ -7,6 +7,7 @@
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <PackageTags>aspnetcore;hosting</PackageTags>
     <IsPackable>false</IsPackable>
+    <Nullable>enable</Nullable>
   </PropertyGroup>
 
   <ItemGroup>

+ 8 - 8
src/Hosting/Hosting/src/PublicAPI.Shipped.txt

@@ -53,11 +53,11 @@ static Microsoft.AspNetCore.Hosting.WebHostExtensions.StopAsync(this Microsoft.A
 static Microsoft.AspNetCore.Hosting.WebHostExtensions.WaitForShutdown(this Microsoft.AspNetCore.Hosting.IWebHost! host) -> void
 static Microsoft.AspNetCore.Hosting.WebHostExtensions.WaitForShutdownAsync(this Microsoft.AspNetCore.Hosting.IWebHost! host, System.Threading.CancellationToken token = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task!
 virtual Microsoft.AspNetCore.Hosting.StartupBase<TBuilder>.ConfigureContainer(TBuilder builder) -> void
-~Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory.CreateBuilder(Microsoft.AspNetCore.Http.Features.IFeatureCollection serverFeatures) -> Microsoft.AspNetCore.Builder.IApplicationBuilder
-~Microsoft.AspNetCore.Hosting.StartupBase<TBuilder>.StartupBase(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory<TBuilder> factory) -> void
-~abstract Microsoft.AspNetCore.Hosting.StartupBase.Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder app) -> void
-~override Microsoft.AspNetCore.Hosting.StartupBase<TBuilder>.CreateServiceProvider(Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> System.IServiceProvider
-~static Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions.ConfigureWebHost(this Microsoft.Extensions.Hosting.IHostBuilder builder, System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder> configure) -> Microsoft.Extensions.Hosting.IHostBuilder
-~static Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions.ConfigureWebHost(this Microsoft.Extensions.Hosting.IHostBuilder builder, System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder> configure, System.Action<Microsoft.Extensions.Hosting.WebHostBuilderOptions> configureWebHostBuilder) -> Microsoft.Extensions.Hosting.IHostBuilder
-~virtual Microsoft.AspNetCore.Hosting.StartupBase.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> void
-~virtual Microsoft.AspNetCore.Hosting.StartupBase.CreateServiceProvider(Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> System.IServiceProvider
+Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory.CreateBuilder(Microsoft.AspNetCore.Http.Features.IFeatureCollection! serverFeatures) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
+Microsoft.AspNetCore.Hosting.StartupBase<TBuilder>.StartupBase(Microsoft.Extensions.DependencyInjection.IServiceProviderFactory<TBuilder>! factory) -> void
+abstract Microsoft.AspNetCore.Hosting.StartupBase.Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder! app) -> void
+override Microsoft.AspNetCore.Hosting.StartupBase<TBuilder>.CreateServiceProvider(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> System.IServiceProvider!
+static Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions.ConfigureWebHost(this Microsoft.Extensions.Hosting.IHostBuilder! builder, System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder!>! configure) -> Microsoft.Extensions.Hosting.IHostBuilder!
+static Microsoft.Extensions.Hosting.GenericHostWebHostBuilderExtensions.ConfigureWebHost(this Microsoft.Extensions.Hosting.IHostBuilder! builder, System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder!>! configure, System.Action<Microsoft.Extensions.Hosting.WebHostBuilderOptions!>! configureWebHostBuilder) -> Microsoft.Extensions.Hosting.IHostBuilder!
+virtual Microsoft.AspNetCore.Hosting.StartupBase.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void
+virtual Microsoft.AspNetCore.Hosting.StartupBase.CreateServiceProvider(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> System.IServiceProvider!

+ 2 - 2
src/Hosting/Hosting/src/Startup/ConventionBasedStartup.cs

@@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Hosting
             {
                 if (ex is TargetInvocationException)
                 {
-                    ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
+                    ExceptionDispatchInfo.Capture(ex.InnerException!).Throw();
                 }
 
                 throw;
@@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Hosting
             {
                 if (ex is TargetInvocationException)
                 {
-                    ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
+                    ExceptionDispatchInfo.Capture(ex.InnerException!).Throw();
                 }
 
                 throw;

+ 1 - 1
src/Hosting/Hosting/src/Startup/StartupBase.cs

@@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Hosting
     /// Base class for initializing services and middlewares used for configuring a <typeparamref name="TBuilder"/>.
     /// </summary>
     /// <typeparam name="TBuilder">The type of builder associated with the startup configuration.</typeparam>
-    public abstract class StartupBase<TBuilder> : StartupBase
+    public abstract class StartupBase<TBuilder> : StartupBase where TBuilder : notnull
     {
         private readonly IServiceProviderFactory<TBuilder> _factory;
 

+ 2 - 1
src/Hosting/Hosting/src/StaticWebAssets/StaticWebAssetsFileProvider.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Runtime.InteropServices;
@@ -111,7 +112,7 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
             public StaticWebAssetsDirectoryRoot(PathString remainingPath)
             {
                 // We MUST use the Value property here because it is unescaped.
-                _nextSegment = remainingPath.Value.Split("/", StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
+                _nextSegment = remainingPath.Value?.Split("/", StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ?? string.Empty;
             }
 
             public bool Exists => true;

+ 1 - 1
src/Hosting/Hosting/src/StaticWebAssets/StaticWebAssetsReader.cs

@@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets
         internal static IEnumerable<ContentRootMapping> Parse(Stream manifest)
         {
             var document = XDocument.Load(manifest);
-            if (!string.Equals(document.Root.Name.LocalName, ManifestRootElementName, StringComparison.OrdinalIgnoreCase))
+            if (!string.Equals(document.Root!.Name.LocalName, ManifestRootElementName, StringComparison.OrdinalIgnoreCase))
             {
                 throw new InvalidOperationException($"Invalid manifest format. Manifest root must be '{ManifestRootElementName}'");
             }

+ 1 - 1
src/Hosting/Hosting/src/WebHostBuilder.cs

@@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.Hosting
         {
             hostingStartupErrors = null;
 
-            _options = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name);
+            _options = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name ?? string.Empty);
 
             if (!_options.PreventHostingStartup)
             {

+ 19 - 4
src/Hosting/Hosting/test/HostingEnvironmentExtensionsTests.cs

@@ -2,7 +2,9 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.IO;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.FileProviders;
+using Moq;
 using Xunit;
 
 namespace Microsoft.AspNetCore.Hosting.Tests
@@ -14,7 +16,10 @@ namespace Microsoft.AspNetCore.Hosting.Tests
         {
             IWebHostEnvironment env = new HostingEnvironment();
 
-            env.Initialize(Path.GetFullPath("."), new WebHostOptions() { WebRoot = "testroot" });
+            var webHostOptions = CreateWebHostOptions();
+            webHostOptions.WebRoot = "testroot";
+
+            env.Initialize(Path.GetFullPath("."), webHostOptions);
 
             Assert.Equal(Path.GetFullPath("."), env.ContentRootPath);
             Assert.Equal(Path.GetFullPath("testroot"), env.WebRootPath);
@@ -27,7 +32,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests
         {
             IWebHostEnvironment env = new HostingEnvironment();
 
-            env.Initialize(Path.GetFullPath("testroot"), new WebHostOptions());
+            env.Initialize(Path.GetFullPath("testroot"), CreateWebHostOptions());
 
             Assert.Equal(Path.GetFullPath("testroot"), env.ContentRootPath);
             Assert.Equal(Path.GetFullPath(Path.Combine("testroot", "wwwroot")), env.WebRootPath);
@@ -40,7 +45,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests
         {
             IWebHostEnvironment env = new HostingEnvironment();
 
-            env.Initialize(Path.GetFullPath(Path.Combine("testroot", "wwwroot")), new WebHostOptions());
+            env.Initialize(Path.GetFullPath(Path.Combine("testroot", "wwwroot")), CreateWebHostOptions());
 
             Assert.Equal(Path.GetFullPath(Path.Combine("testroot", "wwwroot")), env.ContentRootPath);
             Assert.Null(env.WebRootPath);
@@ -54,9 +59,19 @@ namespace Microsoft.AspNetCore.Hosting.Tests
             IWebHostEnvironment env = new HostingEnvironment();
             env.EnvironmentName = "SomeName";
 
-            env.Initialize(Path.GetFullPath("."), new WebHostOptions() { Environment = "NewName" });
+            var webHostOptions = CreateWebHostOptions();
+            webHostOptions.Environment = "NewName";
+
+            env.Initialize(Path.GetFullPath("."), webHostOptions);
 
             Assert.Equal("NewName", env.EnvironmentName);
         }
+
+        private WebHostOptions CreateWebHostOptions(IConfiguration configuration = null, string applicationNameFallback = null)
+        {
+            return new WebHostOptions(
+                configuration ?? Mock.Of<IConfiguration>(),
+                applicationNameFallback: applicationNameFallback);
+        }
     }
 }

+ 15 - 8
src/Hosting/Hosting/test/WebHostBuilderTests.cs

@@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Hosting
 
             using (var host = builder.UseStartup("MyStartupAssembly").Build())
             {
-                var options = new WebHostOptions(host.Services.GetRequiredService<IConfiguration>());
+                var options = CreateWebHostOptions(host.Services.GetRequiredService<IConfiguration>());
                 Assert.Equal("MyStartupAssembly", options.ApplicationName);
                 Assert.Equal("MyStartupAssembly", options.StartupAssembly);
             }
@@ -502,7 +502,7 @@ namespace Microsoft.AspNetCore.Hosting
 
             using (var host = hostBuilder.Build())
             {
-                var options = new WebHostOptions(host.Services.GetRequiredService<IConfiguration>());
+                var options = CreateWebHostOptions(host.Services.GetRequiredService<IConfiguration>());
                 Assert.Equal("EnvB", options.Environment);
             }
         }
@@ -528,7 +528,7 @@ namespace Microsoft.AspNetCore.Hosting
 
             using (var host = hostBuilder.Build())
             {
-                var options = new WebHostOptions(host.Services.GetRequiredService<IConfiguration>());
+                var options = CreateWebHostOptions(host.Services.GetRequiredService<IConfiguration>());
                 Assert.Equal("EnvB", options.Environment);
             }
         }
@@ -554,7 +554,7 @@ namespace Microsoft.AspNetCore.Hosting
 
             using (var host = hostBuilder.Build())
             {
-                var options = new WebHostOptions(host.Services.GetRequiredService<IConfiguration>());
+                var options = CreateWebHostOptions(host.Services.GetRequiredService<IConfiguration>());
                 Assert.Equal("EnvB", options.Environment);
             }
         }
@@ -589,7 +589,7 @@ namespace Microsoft.AspNetCore.Hosting
 
             using (var host = hostBuilder.Build())
             {
-                var options = new WebHostOptions(host.Services.GetRequiredService<IConfiguration>());
+                var options = CreateWebHostOptions(host.Services.GetRequiredService<IConfiguration>());
                 Assert.Equal("EnvB", options.Environment);
             }
         }
@@ -1265,7 +1265,7 @@ namespace Microsoft.AspNetCore.Hosting
 
             using (var host = builder.Build())
             {
-                var options = new WebHostOptions(host.Services.GetRequiredService<IConfiguration>());
+                var options = CreateWebHostOptions(host.Services.GetRequiredService<IConfiguration>());
                 Assert.Equal(TimeSpan.FromSeconds(102), options.ShutdownTimeout);
             }
         }
@@ -1391,6 +1391,13 @@ namespace Microsoft.AspNetCore.Hosting
             await host.StopAsync();
         }
 
+        private WebHostOptions CreateWebHostOptions(IConfiguration configuration, string applicationNameFallback = null)
+        {
+            return new WebHostOptions(
+                configuration,
+                applicationNameFallback: applicationNameFallback);
+        }
+
         private class StartOrder
         {
             public int Order { get; set; }
@@ -1427,7 +1434,7 @@ namespace Microsoft.AspNetCore.Hosting
 
             public StartOrder Ordering { get; }
 
-            public IFeatureCollection Features => null;
+            public IFeatureCollection Features { get; } = new FeatureCollection();
 
             public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
             {
@@ -1543,7 +1550,7 @@ namespace Microsoft.AspNetCore.Hosting
 
         private class TestServer : IServer
         {
-            IFeatureCollection IServer.Features { get; }
+            IFeatureCollection IServer.Features { get; } = new FeatureCollection();
             public RequestDelegate RequestDelegate { get; private set; }
 
             public void Dispose() { }

+ 3 - 3
src/Hosting/Hosting/test/WebHostConfigurationsTests.cs

@@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests
                 { WebHostDefaults.SuppressStatusMessagesKey, "true" }
             };
 
-            var config = new WebHostOptions(new ConfigurationBuilder().AddInMemoryCollection(parameters).Build());
+            var config = new WebHostOptions(new ConfigurationBuilder().AddInMemoryCollection(parameters).Build(), applicationNameFallback: null);
 
             Assert.Equal("wwwroot", config.WebRoot);
             Assert.Equal("MyProjectReference", config.ApplicationName);
@@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests
         public void ReadsOldEnvKey()
         {
             var parameters = new Dictionary<string, string>() { { "ENVIRONMENT", Environments.Development } };
-            var config = new WebHostOptions(new ConfigurationBuilder().AddInMemoryCollection(parameters).Build());
+            var config = new WebHostOptions(new ConfigurationBuilder().AddInMemoryCollection(parameters).Build(), applicationNameFallback: null);
 
             Assert.Equal(Environments.Development, config.Environment);
         }
@@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Hosting.Tests
         public void AllowsNumberForDetailedErrors(string value, bool expected)
         {
             var parameters = new Dictionary<string, string>() { { "detailedErrors", value } };
-            var config = new WebHostOptions(new ConfigurationBuilder().AddInMemoryCollection(parameters).Build());
+            var config = new WebHostOptions(new ConfigurationBuilder().AddInMemoryCollection(parameters).Build(), applicationNameFallback: null);
 
             Assert.Equal(expected, config.DetailedErrors);
         }

+ 1 - 5
src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs

@@ -188,11 +188,7 @@ namespace Microsoft.AspNetCore.Diagnostics
                 }
 
                 var stackFrames = new List<StackFrameSourceCodeInfo>();
-                var exceptionDetails = new ExceptionDetails
-                {
-                    StackFrames = stackFrames,
-                    ErrorMessage = compilationFailure.FailureSummary,
-                };
+                var exceptionDetails = new ExceptionDetails(compilationFailure.FailureSummary!, stackFrames);
                 model.ErrorDetails.Add(exceptionDetails);
                 model.CompiledContent.Add(compilationFailure.CompiledContent);
 

+ 3 - 23
src/Servers/IIS/IIS/src/StartupHook.cs

@@ -63,31 +63,11 @@ internal class StartupHook
             var iisConfigData = NativeMethods.HttpGetApplicationProperties();
             var contentRoot = iisConfigData.pwzFullApplicationPath.TrimEnd(Path.DirectorySeparatorChar);
 
-            var model = new ErrorPageModel
-            {
-                RuntimeDisplayName = RuntimeInformation.FrameworkDescription
-            };
-
-            var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).Assembly;
-            var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
-            var clrVersion = assemblyVersion;
-            model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
-            var currentAssembly = typeof(ErrorPage).Assembly;
-            model.CurrentAssemblyVesion = currentAssembly
-                .GetCustomAttribute<AssemblyInformationalVersionAttribute>()
-                .InformationalVersion;
-            model.ClrVersion = clrVersion;
-            model.OperatingSystemDescription = RuntimeInformation.OSDescription;
-
-            var exceptionDetailProvider = new ExceptionDetailsProvider(
+            var model = ErrorPageModelBuilder.CreateErrorPageModel(
                 new PhysicalFileProvider(contentRoot),
                 logger: null,
-                sourceCodeLineCount: 6);
-
-            // The startup hook is only present when detailed errors are allowed, so
-            // we can turn on all the details.
-            model.ErrorDetails = exceptionDetailProvider.GetDetails(exception);
-            model.ShowRuntimeDetails = true;
+                showDetailedErrors: true,
+                exception);
 
             var errorPage = new ErrorPage(model);
 

+ 71 - 68
src/Shared/ErrorPage/ErrorPage.Designer.cs

@@ -296,7 +296,7 @@ a {
             WriteLiteral("            <div class=\"titleerror\">");
 #nullable restore
 #line 234 "Views/ErrorPage.cshtml"
-                               Write(errorDetail.Error.GetType().Name);
+                               Write(errorDetail.Error!.GetType().Name);
 
 #line default
 #line hidden
@@ -304,7 +304,7 @@ a {
             WriteLiteral(": ");
 #nullable restore
 #line 234 "Views/ErrorPage.cshtml"
-                                                                          Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); 
+                                                                           Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error!.Message)); 
 
 #line default
 #line hidden
@@ -333,10 +333,10 @@ a {
 #line hidden
 #nullable disable
             WriteLiteral(" in <code");
-            BeginWriteAttribute("title", " title=\"", 4862, "\"", 4886, 1);
+            BeginWriteAttribute("title", " title=\"", 4864, "\"", 4888, 1);
 #nullable restore
 #line 243 "Views/ErrorPage.cshtml"
-WriteAttributeValue("", 4870, firstFrame.File, 4870, 16, false);
+WriteAttributeValue("", 4872, firstFrame.File, 4872, 16, false);
 
 #line default
 #line hidden
@@ -405,21 +405,24 @@ WriteAttributeValue("", 4870, firstFrame.File, 4870, 16, false);
 #line 261 "Views/ErrorPage.cshtml"
                          foreach (var ex in reflectionTypeLoadException.LoaderExceptions)
                         {
+                            if (ex != null)
+                            {
 
 #line default
 #line hidden
 #nullable disable
-            WriteLiteral("                            <li>");
+            WriteLiteral("                                <li>");
 #nullable restore
-#line 263 "Views/ErrorPage.cshtml"
-                           Write(ex.Message);
+#line 265 "Views/ErrorPage.cshtml"
+                               Write(ex.Message);
 
 #line default
 #line hidden
 #nullable disable
             WriteLiteral("</li>\r\n");
 #nullable restore
-#line 264 "Views/ErrorPage.cshtml"
+#line 266 "Views/ErrorPage.cshtml"
+                            }
                         }
 
 #line default
@@ -427,7 +430,7 @@ WriteAttributeValue("", 4870, firstFrame.File, 4870, 16, false);
 #nullable disable
             WriteLiteral("                    </ul>\r\n");
 #nullable restore
-#line 266 "Views/ErrorPage.cshtml"
+#line 269 "Views/ErrorPage.cshtml"
                 }
             }
         }
@@ -437,7 +440,7 @@ WriteAttributeValue("", 4870, firstFrame.File, 4870, 16, false);
 #nullable disable
             WriteLiteral("        <div id=\"stackpage\" class=\"page\">\r\n            <ul>\r\n");
 #nullable restore
-#line 271 "Views/ErrorPage.cshtml"
+#line 274 "Views/ErrorPage.cshtml"
                   
                     var exceptionCount = 0;
                     var stackFrameCount = 0;
@@ -449,7 +452,7 @@ WriteAttributeValue("", 4870, firstFrame.File, 4870, 16, false);
 #line hidden
 #nullable disable
 #nullable restore
-#line 277 "Views/ErrorPage.cshtml"
+#line 280 "Views/ErrorPage.cshtml"
                  foreach (var errorDetail in Model.ErrorDetails)
                 {
                     exceptionCount++;
@@ -460,23 +463,23 @@ WriteAttributeValue("", 4870, firstFrame.File, 4870, 16, false);
 #nullable disable
             WriteLiteral("                    <li>\r\n                        <h2 class=\"stackerror\">");
 #nullable restore
-#line 282 "Views/ErrorPage.cshtml"
-                                          Write(errorDetail.Error.GetType().Name);
+#line 285 "Views/ErrorPage.cshtml"
+                                          Write(errorDetail.Error!.GetType().Name);
 
 #line default
 #line hidden
 #nullable disable
             WriteLiteral(": ");
 #nullable restore
-#line 282 "Views/ErrorPage.cshtml"
-                                                                             Write(errorDetail.Error.Message);
+#line 285 "Views/ErrorPage.cshtml"
+                                                                              Write(errorDetail.Error!.Message);
 
 #line default
 #line hidden
 #nullable disable
             WriteLiteral("</h2>\r\n                        <ul>\r\n");
 #nullable restore
-#line 284 "Views/ErrorPage.cshtml"
+#line 287 "Views/ErrorPage.cshtml"
                          foreach (var frame in errorDetail.StackFrames)
                         {
                             stackFrameCount++;
@@ -486,10 +489,10 @@ WriteAttributeValue("", 4870, firstFrame.File, 4870, 16, false);
 #line hidden
 #nullable disable
             WriteLiteral("                            <li class=\"frame\"");
-            BeginWriteAttribute("id", " id=\"", 6742, "\"", 6755, 1);
+            BeginWriteAttribute("id", " id=\"", 6857, "\"", 6870, 1);
 #nullable restore
-#line 288 "Views/ErrorPage.cshtml"
-WriteAttributeValue("", 6747, frameId, 6747, 8, false);
+#line 291 "Views/ErrorPage.cshtml"
+WriteAttributeValue("", 6862, frameId, 6862, 8, false);
 
 #line default
 #line hidden
@@ -497,7 +500,7 @@ WriteAttributeValue("", 6747, frameId, 6747, 8, false);
             EndWriteAttribute();
             WriteLiteral(">\r\n");
 #nullable restore
-#line 289 "Views/ErrorPage.cshtml"
+#line 292 "Views/ErrorPage.cshtml"
                                  if (string.IsNullOrEmpty(frame.File))
                                 {
 
@@ -506,7 +509,7 @@ WriteAttributeValue("", 6747, frameId, 6747, 8, false);
 #nullable disable
             WriteLiteral("                                    <h3>");
 #nullable restore
-#line 291 "Views/ErrorPage.cshtml"
+#line 294 "Views/ErrorPage.cshtml"
                                    Write(frame.Function);
 
 #line default
@@ -514,7 +517,7 @@ WriteAttributeValue("", 6747, frameId, 6747, 8, false);
 #nullable disable
             WriteLiteral("</h3>\r\n");
 #nullable restore
-#line 292 "Views/ErrorPage.cshtml"
+#line 295 "Views/ErrorPage.cshtml"
                                 }
                                 else
                                 {
@@ -524,17 +527,17 @@ WriteAttributeValue("", 6747, frameId, 6747, 8, false);
 #nullable disable
             WriteLiteral("                                    <h3>");
 #nullable restore
-#line 295 "Views/ErrorPage.cshtml"
+#line 298 "Views/ErrorPage.cshtml"
                                    Write(frame.Function);
 
 #line default
 #line hidden
 #nullable disable
             WriteLiteral(" in <code");
-            BeginWriteAttribute("title", " title=\"", 7100, "\"", 7119, 1);
+            BeginWriteAttribute("title", " title=\"", 7215, "\"", 7234, 1);
 #nullable restore
-#line 295 "Views/ErrorPage.cshtml"
-WriteAttributeValue("", 7108, frame.File, 7108, 11, false);
+#line 298 "Views/ErrorPage.cshtml"
+WriteAttributeValue("", 7223, frame.File, 7223, 11, false);
 
 #line default
 #line hidden
@@ -542,7 +545,7 @@ WriteAttributeValue("", 7108, frame.File, 7108, 11, false);
             EndWriteAttribute();
             WriteLiteral(">");
 #nullable restore
-#line 295 "Views/ErrorPage.cshtml"
+#line 298 "Views/ErrorPage.cshtml"
                                                                                 Write(System.IO.Path.GetFileName(frame.File));
 
 #line default
@@ -550,7 +553,7 @@ WriteAttributeValue("", 7108, frame.File, 7108, 11, false);
 #nullable disable
             WriteLiteral("</code></h3>\r\n");
 #nullable restore
-#line 296 "Views/ErrorPage.cshtml"
+#line 299 "Views/ErrorPage.cshtml"
                                 }
 
 #line default
@@ -558,7 +561,7 @@ WriteAttributeValue("", 7108, frame.File, 7108, 11, false);
 #nullable disable
             WriteLiteral("\r\n");
 #nullable restore
-#line 298 "Views/ErrorPage.cshtml"
+#line 301 "Views/ErrorPage.cshtml"
                                  if (frame.Line != 0 && frame.ContextCode.Any())
                                 {
 
@@ -567,7 +570,7 @@ WriteAttributeValue("", 7108, frame.File, 7108, 11, false);
 #nullable disable
             WriteLiteral("                                    <button class=\"expandCollapseButton\" data-frameId=\"");
 #nullable restore
-#line 300 "Views/ErrorPage.cshtml"
+#line 303 "Views/ErrorPage.cshtml"
                                                                                   Write(frameId);
 
 #line default
@@ -575,7 +578,7 @@ WriteAttributeValue("", 7108, frame.File, 7108, 11, false);
 #nullable disable
             WriteLiteral("\">+</button>\r\n                                    <div class=\"source\">\r\n");
 #nullable restore
-#line 302 "Views/ErrorPage.cshtml"
+#line 305 "Views/ErrorPage.cshtml"
                                          if (frame.PreContextCode.Any())
                                         {
 
@@ -583,10 +586,10 @@ WriteAttributeValue("", 7108, frame.File, 7108, 11, false);
 #line hidden
 #nullable disable
             WriteLiteral("                                            <ol");
-            BeginWriteAttribute("start", " start=\"", 7659, "\"", 7688, 1);
+            BeginWriteAttribute("start", " start=\"", 7774, "\"", 7803, 1);
 #nullable restore
-#line 304 "Views/ErrorPage.cshtml"
-WriteAttributeValue("", 7667, frame.PreContextLine, 7667, 21, false);
+#line 307 "Views/ErrorPage.cshtml"
+WriteAttributeValue("", 7782, frame.PreContextLine, 7782, 21, false);
 
 #line default
 #line hidden
@@ -594,7 +597,7 @@ WriteAttributeValue("", 7667, frame.PreContextLine, 7667, 21, false);
             EndWriteAttribute();
             WriteLiteral(" class=\"collapsible\">\r\n");
 #nullable restore
-#line 305 "Views/ErrorPage.cshtml"
+#line 308 "Views/ErrorPage.cshtml"
                                                  foreach (var line in frame.PreContextCode)
                                                 {
 
@@ -603,7 +606,7 @@ WriteAttributeValue("", 7667, frame.PreContextLine, 7667, 21, false);
 #nullable disable
             WriteLiteral("                                                    <li><span>");
 #nullable restore
-#line 307 "Views/ErrorPage.cshtml"
+#line 310 "Views/ErrorPage.cshtml"
                                                          Write(line);
 
 #line default
@@ -611,7 +614,7 @@ WriteAttributeValue("", 7667, frame.PreContextLine, 7667, 21, false);
 #nullable disable
             WriteLiteral("</span></li>\r\n");
 #nullable restore
-#line 308 "Views/ErrorPage.cshtml"
+#line 311 "Views/ErrorPage.cshtml"
                                                 }
 
 #line default
@@ -619,17 +622,17 @@ WriteAttributeValue("", 7667, frame.PreContextLine, 7667, 21, false);
 #nullable disable
             WriteLiteral("                                            </ol>\r\n");
 #nullable restore
-#line 310 "Views/ErrorPage.cshtml"
+#line 313 "Views/ErrorPage.cshtml"
                                         }
 
 #line default
 #line hidden
 #nullable disable
             WriteLiteral("\r\n                                        <ol");
-            BeginWriteAttribute("start", " start=\"", 8127, "\"", 8146, 1);
+            BeginWriteAttribute("start", " start=\"", 8242, "\"", 8261, 1);
 #nullable restore
-#line 312 "Views/ErrorPage.cshtml"
-WriteAttributeValue("", 8135, frame.Line, 8135, 11, false);
+#line 315 "Views/ErrorPage.cshtml"
+WriteAttributeValue("", 8250, frame.Line, 8250, 11, false);
 
 #line default
 #line hidden
@@ -637,7 +640,7 @@ WriteAttributeValue("", 8135, frame.Line, 8135, 11, false);
             EndWriteAttribute();
             WriteLiteral(" class=\"highlight\">\r\n");
 #nullable restore
-#line 313 "Views/ErrorPage.cshtml"
+#line 316 "Views/ErrorPage.cshtml"
                                              foreach (var line in frame.ContextCode)
                                             {
 
@@ -646,7 +649,7 @@ WriteAttributeValue("", 8135, frame.Line, 8135, 11, false);
 #nullable disable
             WriteLiteral("                                                <li><span>");
 #nullable restore
-#line 315 "Views/ErrorPage.cshtml"
+#line 318 "Views/ErrorPage.cshtml"
                                                      Write(line);
 
 #line default
@@ -654,7 +657,7 @@ WriteAttributeValue("", 8135, frame.Line, 8135, 11, false);
 #nullable disable
             WriteLiteral("</span></li>\r\n");
 #nullable restore
-#line 316 "Views/ErrorPage.cshtml"
+#line 319 "Views/ErrorPage.cshtml"
                                             }
 
 #line default
@@ -662,7 +665,7 @@ WriteAttributeValue("", 8135, frame.Line, 8135, 11, false);
 #nullable disable
             WriteLiteral("                                        </ol>\r\n\r\n");
 #nullable restore
-#line 319 "Views/ErrorPage.cshtml"
+#line 322 "Views/ErrorPage.cshtml"
                                          if (frame.PostContextCode.Any())
                                         {
 
@@ -670,10 +673,10 @@ WriteAttributeValue("", 8135, frame.Line, 8135, 11, false);
 #line hidden
 #nullable disable
             WriteLiteral("                                            <ol");
-            BeginWriteAttribute("start", " start=\'", 8639, "\'", 8664, 1);
+            BeginWriteAttribute("start", " start=\'", 8754, "\'", 8779, 1);
 #nullable restore
-#line 321 "Views/ErrorPage.cshtml"
-WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
+#line 324 "Views/ErrorPage.cshtml"
+WriteAttributeValue("", 8762, frame.Line + 1, 8762, 17, false);
 
 #line default
 #line hidden
@@ -681,7 +684,7 @@ WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
             EndWriteAttribute();
             WriteLiteral(" class=\"collapsible\">\r\n");
 #nullable restore
-#line 322 "Views/ErrorPage.cshtml"
+#line 325 "Views/ErrorPage.cshtml"
                                                  foreach (var line in frame.PostContextCode)
                                                 {
 
@@ -690,7 +693,7 @@ WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
 #nullable disable
             WriteLiteral("                                                    <li><span>");
 #nullable restore
-#line 324 "Views/ErrorPage.cshtml"
+#line 327 "Views/ErrorPage.cshtml"
                                                          Write(line);
 
 #line default
@@ -698,7 +701,7 @@ WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
 #nullable disable
             WriteLiteral("</span></li>\r\n");
 #nullable restore
-#line 325 "Views/ErrorPage.cshtml"
+#line 328 "Views/ErrorPage.cshtml"
                                                 }
 
 #line default
@@ -706,7 +709,7 @@ WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
 #nullable disable
             WriteLiteral("                                            </ol>\r\n");
 #nullable restore
-#line 327 "Views/ErrorPage.cshtml"
+#line 330 "Views/ErrorPage.cshtml"
                                         }
 
 #line default
@@ -714,7 +717,7 @@ WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
 #nullable disable
             WriteLiteral("                                    </div>\r\n");
 #nullable restore
-#line 329 "Views/ErrorPage.cshtml"
+#line 332 "Views/ErrorPage.cshtml"
                                 }
 
 #line default
@@ -722,7 +725,7 @@ WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
 #nullable disable
             WriteLiteral("                            </li>\r\n");
 #nullable restore
-#line 331 "Views/ErrorPage.cshtml"
+#line 334 "Views/ErrorPage.cshtml"
                         }
 
 #line default
@@ -736,17 +739,17 @@ WriteAttributeValue("", 8647, frame.Line + 1, 8647, 17, false);
                             <div class=""showRawExceptionContainer"">
                                 <button class=""showRawException"" data-exceptionDetailId=""");
 #nullable restore
-#line 338 "Views/ErrorPage.cshtml"
+#line 341 "Views/ErrorPage.cshtml"
                                                                                     Write(exceptionDetailId);
 
 #line default
 #line hidden
 #nullable disable
             WriteLiteral("\">Show raw exception details</button>\r\n                            </div>\r\n                            <div");
-            BeginWriteAttribute("id", " id=\"", 9655, "\"", 9678, 1);
+            BeginWriteAttribute("id", " id=\"", 9770, "\"", 9793, 1);
 #nullable restore
-#line 340 "Views/ErrorPage.cshtml"
-WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
+#line 343 "Views/ErrorPage.cshtml"
+WriteAttributeValue("", 9775, exceptionDetailId, 9775, 18, false);
 
 #line default
 #line hidden
@@ -754,15 +757,15 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
             EndWriteAttribute();
             WriteLiteral(" class=\"rawExceptionDetails\">\r\n                                <pre class=\"rawExceptionStackTrace\">");
 #nullable restore
-#line 341 "Views/ErrorPage.cshtml"
-                                                               Write(errorDetail.Error.ToString());
+#line 344 "Views/ErrorPage.cshtml"
+                                                               Write(errorDetail.Error!.ToString());
 
 #line default
 #line hidden
 #nullable disable
             WriteLiteral("</pre>\r\n                            </div>\r\n                        </div>\r\n                    </li>\r\n");
 #nullable restore
-#line 345 "Views/ErrorPage.cshtml"
+#line 348 "Views/ErrorPage.cshtml"
                 }
 
 #line default
@@ -770,7 +773,7 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
 #nullable disable
             WriteLiteral("            </ul>\r\n        </div>\r\n");
 #nullable restore
-#line 348 "Views/ErrorPage.cshtml"
+#line 351 "Views/ErrorPage.cshtml"
          if (Model.ShowRuntimeDetails) {
 
 #line default
@@ -778,7 +781,7 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
 #nullable disable
             WriteLiteral("            <footer>\r\n                ");
 #nullable restore
-#line 350 "Views/ErrorPage.cshtml"
+#line 353 "Views/ErrorPage.cshtml"
            Write(Model.RuntimeDisplayName);
 
 #line default
@@ -786,7 +789,7 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
 #nullable disable
             WriteLiteral(" ");
 #nullable restore
-#line 350 "Views/ErrorPage.cshtml"
+#line 353 "Views/ErrorPage.cshtml"
                                      Write(Model.RuntimeArchitecture);
 
 #line default
@@ -794,7 +797,7 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
 #nullable disable
             WriteLiteral(" v");
 #nullable restore
-#line 350 "Views/ErrorPage.cshtml"
+#line 353 "Views/ErrorPage.cshtml"
                                                                   Write(Model.ClrVersion);
 
 #line default
@@ -802,7 +805,7 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
 #nullable disable
             WriteLiteral(" &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;Microsoft.AspNetCore.Hosting version ");
 #nullable restore
-#line 350 "Views/ErrorPage.cshtml"
+#line 353 "Views/ErrorPage.cshtml"
                                                                                                                                                                Write(Model.CurrentAssemblyVesion);
 
 #line default
@@ -810,7 +813,7 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
 #nullable disable
             WriteLiteral(" &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp; ");
 #nullable restore
-#line 350 "Views/ErrorPage.cshtml"
+#line 353 "Views/ErrorPage.cshtml"
                                                                                                                                                                                                                                   Write(Model.OperatingSystemDescription);
 
 #line default
@@ -818,7 +821,7 @@ WriteAttributeValue("", 9660, exceptionDetailId, 9660, 18, false);
 #nullable disable
             WriteLiteral(" &nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href=\"http://go.microsoft.com/fwlink/?LinkId=517394\">Need help?</a>\r\n            </footer>\r\n");
 #nullable restore
-#line 352 "Views/ErrorPage.cshtml"
+#line 355 "Views/ErrorPage.cshtml"
         }
 
 #line default

+ 18 - 7
src/Shared/ErrorPage/ErrorPageModel.cs

@@ -11,21 +11,32 @@ namespace Microsoft.AspNetCore.Hosting.Views
     /// </summary>
     internal class ErrorPageModel
     {
+        public ErrorPageModel(IEnumerable<ExceptionDetails> errorDetails, bool showRuntimeDetails, string runtimeDisplayName, string runtimeArchitecture, string clrVersion, string currentAssemblyVesion, string operatingSystemDescription)
+        {
+            ErrorDetails = errorDetails;
+            ShowRuntimeDetails = showRuntimeDetails;
+            RuntimeDisplayName = runtimeDisplayName;
+            RuntimeArchitecture = runtimeArchitecture;
+            ClrVersion = clrVersion;
+            CurrentAssemblyVesion = currentAssemblyVesion;
+            OperatingSystemDescription = operatingSystemDescription;
+        }
+
         /// <summary>
         /// Detailed information about each exception in the stack.
         /// </summary>
-        public IEnumerable<ExceptionDetails> ErrorDetails { get; set; }
+        public IEnumerable<ExceptionDetails> ErrorDetails { get; }
 
-        public bool ShowRuntimeDetails { get; set; }
+        public bool ShowRuntimeDetails { get; }
 
-        public string RuntimeDisplayName { get; set; }
+        public string RuntimeDisplayName { get; }
 
-        public string RuntimeArchitecture { get; set; }
+        public string RuntimeArchitecture { get; }
 
-        public string ClrVersion { get; set; }
+        public string ClrVersion { get; }
 
-        public string CurrentAssemblyVesion { get; set; }
+        public string CurrentAssemblyVesion { get; }
 
-        public string OperatingSystemDescription { get; set; }
+        public string OperatingSystemDescription { get; }
     }
 }

+ 58 - 0
src/Shared/ErrorPage/ErrorPageModelBuilder.cs

@@ -0,0 +1,58 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.StackTrace.Sources;
+
+#nullable enable
+
+namespace Microsoft.AspNetCore.Hosting.Views
+{
+    internal static class ErrorPageModelBuilder
+    {
+        public static ErrorPageModel CreateErrorPageModel(
+            IFileProvider contentRootFileProvider,
+            ILogger? logger,
+            bool showDetailedErrors,
+            Exception exception)
+        {
+            var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).Assembly;
+            var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName!).Version?.ToString() ?? string.Empty;
+            var clrVersion = assemblyVersion;
+            var currentAssembly = typeof(ErrorPage).Assembly;
+            var currentAssemblyVesion = currentAssembly
+                .GetCustomAttribute<AssemblyInformationalVersionAttribute>()!
+                .InformationalVersion;
+
+            IEnumerable<ExceptionDetails> errorDetails;
+            if (showDetailedErrors)
+            {
+                var exceptionDetailProvider = new ExceptionDetailsProvider(
+                    contentRootFileProvider,
+                    logger,
+                    sourceCodeLineCount: 6);
+
+                errorDetails = exceptionDetailProvider.GetDetails(exception);
+            }
+            else
+            {
+                errorDetails = Array.Empty<ExceptionDetails>();
+            }
+
+            var model = new ErrorPageModel(
+                errorDetails,
+                showDetailedErrors,
+                RuntimeInformation.FrameworkDescription,
+                RuntimeInformation.ProcessArchitecture.ToString(),
+                clrVersion,
+                currentAssemblyVesion,
+                RuntimeInformation.OSDescription);
+            return model;
+        }
+    }
+}

+ 7 - 4
src/Shared/ErrorPage/Views/ErrorPage.cshtml

@@ -30,7 +30,7 @@
         <h1>An error occurred while starting the application.</h1>
         @foreach (var errorDetail in Model.ErrorDetails)
         {
-            <div class="titleerror">@errorDetail.Error.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); }</div>
+            <div class="titleerror">@errorDetail.Error!.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error!.Message)); }</div>
 
             var firstFrame = errorDetail.StackFrames.FirstOrDefault();
             if (firstFrame != null)
@@ -59,7 +59,10 @@
                     <ul>
                         @foreach (var ex in reflectionTypeLoadException.LoaderExceptions)
                         {
-                            <li>@ex.Message</li>
+                            if (ex != null)
+                            {
+                                <li>@ex.Message</li>
+                            }
                         }
                     </ul>
                 }
@@ -78,7 +81,7 @@
                     exceptionCount++;
                     exceptionDetailId = "exceptionDetail" + exceptionCount;
                     <li>
-                        <h2 class="stackerror">@errorDetail.Error.GetType().Name: @errorDetail.Error.Message</h2>
+                        <h2 class="stackerror">@errorDetail.Error!.GetType().Name: @errorDetail.Error!.Message</h2>
                         <ul>
                         @foreach (var frame in errorDetail.StackFrames)
                         {
@@ -137,7 +140,7 @@
                                 <button class="showRawException" data-exceptionDetailId="@exceptionDetailId">Show raw exception details</button>
                             </div>
                             <div id="@exceptionDetailId" class="rawExceptionDetails">
-                                <pre class="rawExceptionStackTrace">@errorDetail.Error.ToString()</pre>
+                                <pre class="rawExceptionStackTrace">@errorDetail.Error!.ToString()</pre>
                             </div>
                         </div>
                     </li>

+ 17 - 11
src/Shared/RazorViews/BaseView.cs

@@ -12,6 +12,8 @@ using System.Text.Encodings.Web;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Http;
 
+#nullable enable
+
 namespace Microsoft.Extensions.RazorViews
 {
     /// <summary>
@@ -26,22 +28,22 @@ namespace Microsoft.Extensions.RazorViews
         /// <summary>
         /// The request context
         /// </summary>
-        protected HttpContext Context { get; private set; }
+        protected HttpContext Context { get; private set; } = default!;
 
         /// <summary>
         /// The request
         /// </summary>
-        protected HttpRequest Request { get; private set; }
+        protected HttpRequest Request { get; private set; } = default!;
 
         /// <summary>
         /// The response
         /// </summary>
-        protected HttpResponse Response { get; private set; }
+        protected HttpResponse Response { get; private set; } = default!;
 
         /// <summary>
         /// The output stream
         /// </summary>
-        protected TextWriter Output { get; private set; }
+        protected TextWriter Output { get; private set; } = default!;
 
         /// <summary>
         /// Html encoder used to encode content.
@@ -128,7 +130,7 @@ namespace Microsoft.Extensions.RazorViews
         /// Write the given value without HTML encoding directly to <see cref="Output"/>.
         /// </summary>
         /// <param name="value">The <see cref="string"/> to write.</param>
-        protected void WriteLiteral(string value)
+        protected void WriteLiteral(string? value)
         {
             if (!string.IsNullOrEmpty(value))
             {
@@ -136,7 +138,7 @@ namespace Microsoft.Extensions.RazorViews
             }
         }
 
-        private List<string> AttributeValues { get; set; }
+        private List<string>? AttributeValues { get; set; }
 
         protected void WriteAttributeValue(string thingy, int startPostion, object value, int endValue, int dealyo, bool yesno)
         {
@@ -145,10 +147,10 @@ namespace Microsoft.Extensions.RazorViews
                 AttributeValues = new List<string>();
             }
 
-            AttributeValues.Add(value.ToString());
+            AttributeValues.Add(value.ToString()!);
         }
 
-        private string AttributeEnding { get; set; }
+        private string? AttributeEnding { get; set; }
 
         protected void BeginWriteAttribute(string name, string beginning, int startPosition, string ending, int endPosition, int thingy)
         {
@@ -160,6 +162,7 @@ namespace Microsoft.Extensions.RazorViews
 
         protected void EndWriteAttribute()
         {
+            Debug.Assert(AttributeValues != null);
             Debug.Assert(!string.IsNullOrEmpty(AttributeEnding));
 
             var attributes = string.Join(" ", AttributeValues);
@@ -207,7 +210,7 @@ namespace Microsoft.Extensions.RazorViews
                 // value might be a bool. If the value is the bool 'true' we want to write the attribute name
                 // instead of the string 'true'. If the value is the bool 'false' we don't want to write anything.
                 // Otherwise the value is another object (perhaps an HtmlString) and we'll ask it to format itself.
-                string stringValue;
+                string? stringValue;
                 if (value.Value is bool)
                 {
                     if ((bool)value.Value)
@@ -279,9 +282,12 @@ namespace Microsoft.Extensions.RazorViews
         /// Writes the specified <paramref name="value"/> with HTML encoding to <see cref="Output"/>.
         /// </summary>
         /// <param name="value">The <see cref="string"/> to write.</param>
-        protected void Write(string value)
+        protected void Write(string? value)
         {
-            WriteLiteral(HtmlEncoder.Encode(value));
+            if (!string.IsNullOrEmpty(value))
+            {
+                WriteLiteral(HtmlEncoder.Encode(value));
+            }
         }
 
         protected string HtmlEncodeAndReplaceLineBreaks(string input)

+ 18 - 4
src/Shared/StackTrace/ExceptionDetails/ExceptionDetails.cs

@@ -1,9 +1,11 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
 using System.Collections.Generic;
 
+#nullable enable
+
 namespace Microsoft.Extensions.StackTrace.Sources
 {
     /// <summary>
@@ -11,19 +13,31 @@ namespace Microsoft.Extensions.StackTrace.Sources
     /// </summary>
     internal class ExceptionDetails
     {
+        public ExceptionDetails(Exception error, IEnumerable<StackFrameSourceCodeInfo> stackFrames)
+        {
+            Error = error;
+            StackFrames = stackFrames;
+        }
+
+        public ExceptionDetails(string errorMessage, IEnumerable<StackFrameSourceCodeInfo> stackFrames)
+        {
+            ErrorMessage = errorMessage;
+            StackFrames = stackFrames;
+        }
+
         /// <summary>
         /// An individual exception
         /// </summary>
-        public Exception Error { get; set; }
+        public Exception? Error { get; }
 
         /// <summary>
         /// The generated stack frames
         /// </summary>
-        public IEnumerable<StackFrameSourceCodeInfo> StackFrames { get; set; }
+        public IEnumerable<StackFrameSourceCodeInfo> StackFrames { get; }
 
         /// <summary>
         /// Gets or sets the summary message.
         /// </summary>
-        public string ErrorMessage { get; set; }
+        public string? ErrorMessage { get; set; }
     }
 }

+ 12 - 14
src/Shared/StackTrace/ExceptionDetails/ExceptionDetailsProvider.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
@@ -9,15 +9,17 @@ using System.Reflection;
 using Microsoft.Extensions.FileProviders;
 using Microsoft.Extensions.Logging;
 
+#nullable enable
+
 namespace Microsoft.Extensions.StackTrace.Sources
 {
     internal class ExceptionDetailsProvider
     {
         private readonly IFileProvider _fileProvider;
-        private readonly ILogger _logger;
+        private readonly ILogger? _logger;
         private readonly int _sourceCodeLineCount;
 
-        public ExceptionDetailsProvider(IFileProvider fileProvider, ILogger logger, int sourceCodeLineCount)
+        public ExceptionDetailsProvider(IFileProvider fileProvider, ILogger? logger, int sourceCodeLineCount)
         {
             _fileProvider = fileProvider;
             _logger = logger;
@@ -30,11 +32,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
 
             foreach (var ex in exceptions)
             {
-                yield return new ExceptionDetails
-                {
-                    Error = ex,
-                    StackFrames = GetStackFrames(ex),
-                };
+                yield return new ExceptionDetails(ex, GetStackFrames(ex));
             }
         }
 
@@ -42,7 +40,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
         {
             var stackFrames = StackTraceHelper.GetFrames(original, out var exception)
                 .Select(frame => GetStackFrameSourceCodeInfo(
-                    frame.MethodDisplayInfo.ToString(),
+                    frame.MethodDisplayInfo?.ToString(),
                     frame.FilePath,
                     frame.LineNumber));
 
@@ -54,7 +52,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
             return stackFrames;
         }
 
-        private static IEnumerable<Exception> FlattenAndReverseExceptionTree(Exception ex)
+        private static IEnumerable<Exception> FlattenAndReverseExceptionTree(Exception? ex)
         {
             // ReflectionTypeLoadException is special because the details are in
             // the LoaderExceptions property
@@ -67,7 +65,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
                     typeLoadExceptions.AddRange(FlattenAndReverseExceptionTree(loadException));
                 }
 
-                typeLoadExceptions.Add(ex);
+                typeLoadExceptions.Add(typeLoadException);
                 return typeLoadExceptions;
             }
 
@@ -95,7 +93,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
         }
 
         // make it internal to enable unit testing
-        internal StackFrameSourceCodeInfo GetStackFrameSourceCodeInfo(string method, string filePath, int lineNumber)
+        internal StackFrameSourceCodeInfo GetStackFrameSourceCodeInfo(string? method, string? filePath, int lineNumber)
         {
             var stackFrame = new StackFrameSourceCodeInfo
             {
@@ -109,7 +107,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
                 return stackFrame;
             }
 
-            IEnumerable<string> lines = null;
+            IEnumerable<string>? lines = null;
             if (File.Exists(stackFrame.File))
             {
                 lines = File.ReadLines(stackFrame.File);
@@ -174,7 +172,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
         {
             using (var reader = new StreamReader(fileInfo.CreateReadStream()))
             {
-                string line;
+                string? line;
                 while ((line = reader.ReadLine()) != null)
                 {
                     yield return line;

+ 17 - 6
src/Shared/StackTrace/StackFrame/MethodDisplayInfo.cs

@@ -1,23 +1,34 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 
+#nullable enable
+
 namespace Microsoft.Extensions.StackTrace.Sources
 {
     internal class MethodDisplayInfo
     {
-        public string DeclaringTypeName { get; set; }
+        public MethodDisplayInfo(string? declaringTypeName, string name, string? genericArguments, string? subMethod, IEnumerable<ParameterDisplayInfo> parameters)
+        {
+            DeclaringTypeName = declaringTypeName;
+            Name = name;
+            GenericArguments = genericArguments;
+            SubMethod = subMethod;
+            Parameters = parameters;
+        }
+
+        public string? DeclaringTypeName { get; }
 
-        public string Name { get; set; }
+        public string Name { get; }
 
-        public string GenericArguments { get; set; }
+        public string? GenericArguments { get; }
 
-        public string SubMethod { get; set; }
+        public string? SubMethod { get; }
 
-        public IEnumerable<ParameterDisplayInfo> Parameters { get; set; }
+        public IEnumerable<ParameterDisplayInfo> Parameters { get; }
 
         public override string ToString()
         {

+ 12 - 4
src/Shared/StackTrace/StackFrame/StackFrameInfo.cs

@@ -8,12 +8,20 @@ namespace Microsoft.Extensions.StackTrace.Sources
 {
     internal class StackFrameInfo
     {
-        public int LineNumber { get; set; }
+        public StackFrameInfo(int lineNumber, string? filePath, StackFrame? stackFrame, MethodDisplayInfo? methodDisplayInfo)
+        {
+            LineNumber = lineNumber;
+            FilePath = filePath;
+            StackFrame = stackFrame;
+            MethodDisplayInfo = methodDisplayInfo;
+        }
 
-        public string? FilePath { get; set; }
+        public int LineNumber { get; }
 
-        public StackFrame? StackFrame { get; set; }
+        public string? FilePath { get; }
 
-        public MethodDisplayInfo? MethodDisplayInfo { get; set; }
+        public StackFrame? StackFrame { get; }
+
+        public MethodDisplayInfo? MethodDisplayInfo { get; }
     }
 }

+ 6 - 4
src/Shared/StackTrace/StackFrame/StackFrameSourceCodeInfo.cs

@@ -1,9 +1,11 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.Collections.Generic;
 using System.Linq;
 
+#nullable enable
+
 namespace Microsoft.Extensions.StackTrace.Sources
 {
     /// <summary>
@@ -14,12 +16,12 @@ namespace Microsoft.Extensions.StackTrace.Sources
         /// <summary>
         /// Function containing instruction
         /// </summary>
-        public string Function { get; set; }
+        public string? Function { get; set; }
 
         /// <summary>
         /// File containing the instruction
         /// </summary>
-        public string File { get; set; }
+        public string? File { get; set; }
 
         /// <summary>
         /// The line number of the instruction
@@ -49,6 +51,6 @@ namespace Microsoft.Extensions.StackTrace.Sources
         /// <summary>
         /// Specific error details for this stack frame.
         /// </summary>
-        public string ErrorDetails { get; set; }
+        public string? ErrorDetails { get; set; }
     }
 }

+ 12 - 18
src/Shared/StackTrace/StackFrame/StackTraceHelper.cs

@@ -49,14 +49,7 @@ namespace Microsoft.Extensions.StackTrace.Sources
                     continue;
                 }
 
-                var stackFrame = new StackFrameInfo
-                {
-                    StackFrame = frame,
-                    FilePath = frame.GetFileName(),
-                    LineNumber = frame.GetFileLineNumber(),
-                    MethodDisplayInfo = GetMethodDisplayString(frame.GetMethod()),
-                };
-
+                var stackFrame = new StackFrameInfo(frame.GetFileLineNumber(), frame.GetFileName(), frame, GetMethodDisplayString(frame.GetMethod()));
                 frames.Add(stackFrame);
             }
 
@@ -78,39 +71,38 @@ namespace Microsoft.Extensions.StackTrace.Sources
                 return null;
             }
 
-            var methodDisplayInfo = new MethodDisplayInfo();
-
             // Type name
             var type = method.DeclaringType;
 
             var methodName = method.Name;
 
+            string? subMethod = null;
             if (type != null && type.IsDefined(typeof(CompilerGeneratedAttribute)) &&
                 (typeof(IAsyncStateMachine).IsAssignableFrom(type) || typeof(IEnumerator).IsAssignableFrom(type)))
             {
                 // Convert StateMachine methods to correct overload +MoveNext()
                 if (TryResolveStateMachineMethod(ref method, out type))
                 {
-                    methodDisplayInfo.SubMethod = methodName;
+                    subMethod = methodName;
                 }
             }
+
+            string? declaringTypeName = null;
             // ResolveStateMachineMethod may have set declaringType to null
             if (type != null)
             {
-                methodDisplayInfo.DeclaringTypeName = TypeNameHelper.GetTypeDisplayName(type, includeGenericParameterNames: true);
+                declaringTypeName = TypeNameHelper.GetTypeDisplayName(type, includeGenericParameterNames: true);
             }
 
-            // Method name
-            methodDisplayInfo.Name = method.Name;
+            string? genericArguments = null;
             if (method.IsGenericMethod)
             {
-                var genericArguments = string.Join(", ", method.GetGenericArguments()
-                    .Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true)));
-                methodDisplayInfo.GenericArguments += "<" + genericArguments + ">";
+                genericArguments = "<" + string.Join(", ", method.GetGenericArguments()
+                    .Select(arg => TypeNameHelper.GetTypeDisplayName(arg, fullName: false, includeGenericParameterNames: true))) + ">";
             }
 
             // Method parameters
-            methodDisplayInfo.Parameters = method.GetParameters().Select(parameter =>
+            var parameters = method.GetParameters().Select(parameter =>
             {
                 var parameterType = parameter.ParameterType;
 
@@ -143,6 +135,8 @@ namespace Microsoft.Extensions.StackTrace.Sources
                 };
             });
 
+            var methodDisplayInfo = new MethodDisplayInfo(declaringTypeName, method.Name, genericArguments, subMethod, parameters);
+
             return methodDisplayInfo;
         }
 

+ 8 - 6
src/Shared/TypeNameHelper/TypeNameHelper.cs

@@ -5,6 +5,8 @@ using System;
 using System.Text;
 using System.Collections.Generic;
 
+#nullable enable
+
 namespace Microsoft.Extensions.Internal
 {
     internal static class TypeNameHelper
@@ -31,7 +33,7 @@ namespace Microsoft.Extensions.Internal
             { typeof(ushort), "ushort" }
         };
 
-        public static string GetTypeDisplayName(object item, bool fullName = true)
+        public static string? GetTypeDisplayName(object item, bool fullName = true)
         {
             return item == null ? null : GetTypeDisplayName(item.GetType(), fullName);
         }
@@ -76,7 +78,7 @@ namespace Microsoft.Extensions.Internal
             }
             else
             {
-                var name = options.FullName ? type.FullName : type.Name;
+                var name = options.FullName ? type.FullName! : type.Name;
                 builder.Append(name);
 
                 if (options.NestedTypeDelimiter != DefaultNestedTypeDelimiter)
@@ -91,7 +93,7 @@ namespace Microsoft.Extensions.Internal
             var innerType = type;
             while (innerType.IsArray)
             {
-                innerType = innerType.GetElementType();
+                innerType = innerType.GetElementType()!;
             }
 
             ProcessType(builder, innerType, options);
@@ -101,7 +103,7 @@ namespace Microsoft.Extensions.Internal
                 builder.Append('[');
                 builder.Append(',', type.GetArrayRank() - 1);
                 builder.Append(']');
-                type = type.GetElementType();
+                type = type.GetElementType()!;
             }
         }
 
@@ -110,14 +112,14 @@ namespace Microsoft.Extensions.Internal
             var offset = 0;
             if (type.IsNested)
             {
-                offset = type.DeclaringType.GetGenericArguments().Length;
+                offset = type.DeclaringType!.GetGenericArguments().Length;
             }
 
             if (options.FullName)
             {
                 if (type.IsNested)
                 {
-                    ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options);
+                    ProcessGenericType(builder, type.DeclaringType!, genericArguments, offset, options);
                     builder.Append(options.NestedTypeDelimiter);
                 }
                 else if (!string.IsNullOrEmpty(type.Namespace))