Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules4
-rw-r--r--build/CodeSign.props2
-rw-r--r--build/artifacts.props3
-rw-r--r--build/buildorder.props1
-rw-r--r--build/dependencies.props4
-rw-r--r--build/external-dependencies.props2
-rw-r--r--build/submodules.props1
-rw-r--r--eng/Baseline.props56
-rw-r--r--eng/Dependencies.props2
-rw-r--r--eng/ProjectReferences.props6
-rw-r--r--eng/dependencies.temp.props4
-rw-r--r--eng/tools/BaselineGenerator/baseline.xml6
m---------modules/Diagnostics18
-rw-r--r--src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj3
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/CompilationFailure.cs83
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/DiagnosticMessage.cs64
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/ICompilationException.cs22
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerFeature.cs12
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerPathFeature.cs17
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/IStatusCodePagesFeature.cs16
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/IStatusCodeReExecuteFeature.cs14
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/Microsoft.AspNetCore.Diagnostics.Abstractions.csproj11
-rw-r--r--src/Middleware/Diagnostics.Abstractions/src/baseline.netcore.json356
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageExtensions.cs62
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageMiddleware.cs253
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageOptions.cs21
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Internal/DiagnosticsEntityFrameworkCoreLoggerExtensions.cs153
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj21
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointExtensions.cs51
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointMiddleware.cs159
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointOptions.cs26
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/AssemblyInfo.cs8
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/Strings.Designer.cs408
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Strings.resx201
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.Designer.cs424
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.cshtml135
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPageModel.cs35
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/ErrorPage.css78
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/src/baseline.netcore.json795
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs526
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Diagnostics.EFCore.FunctionalTests.csproj16
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/PlatformHelper.cs18
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/StringHelpers.cs19
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/TestLoggerProvider.cs61
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/MigrationsEndPointMiddlewareTest.cs224
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs60
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/Blog.cs13
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContext.cs18
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithMigrations.cs71
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithPendingModelChanges.cs33
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithSnapshotThatThrows.cs39
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageOptionsTest.cs28
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageTest.cs199
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/AssertHelpers.cs41
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/StringHelpers.cs19
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.csproj13
-rw-r--r--src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/TestHelperExtensions.cs21
-rw-r--r--src/Middleware/Diagnostics/src/.csslintrc7
-rw-r--r--src/Middleware/Diagnostics/src/.jshintrc3
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageExtensions.cs53
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs210
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageOptions.cs37
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorModel.cs30
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.Designer.cs751
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.cshtml116
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.Designer.cs1109
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.cshtml258
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.css196
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.js192
-rw-r--r--src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPageModel.cs42
-rw-r--r--src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerExtensions.cs98
-rw-r--r--src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerFeature.cs14
-rw-r--r--src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs115
-rw-r--r--src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs14
-rw-r--r--src/Middleware/Diagnostics/src/Internal/DiagnosticsLoggerExtensions.cs54
-rw-r--r--src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj30
-rw-r--r--src/Middleware/Diagnostics/src/Properties/AssemblyInfo.cs9
-rw-r--r--src/Middleware/Diagnostics/src/Properties/Resources.Designer.cs618
-rw-r--r--src/Middleware/Diagnostics/src/Resources.resx253
-rw-r--r--src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeContext.cs24
-rw-r--r--src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesExtensions.cs206
-rw-r--r--src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesFeature.cs13
-rw-r--r--src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs55
-rw-r--r--src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesOptions.cs48
-rw-r--r--src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeReExecuteFeature.cs14
-rw-r--r--src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cs373
-rw-r--r--src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cshtml196
-rw-r--r--src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.Designer.cs366
-rw-r--r--src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.cshtml196
-rw-r--r--src/Middleware/Diagnostics/src/WelcomePage/WelcomePageExtensions.cs90
-rw-r--r--src/Middleware/Diagnostics/src/WelcomePage/WelcomePageMiddleware.cs60
-rw-r--r--src/Middleware/Diagnostics/src/WelcomePage/WelcomePageOptions.cs19
-rw-r--r--src/Middleware/Diagnostics/src/_gruntfile.js32
-rw-r--r--src/Middleware/Diagnostics/src/_gruntfile.readme1
-rw-r--r--src/Middleware/Diagnostics/src/_package.json12
-rw-r--r--src/Middleware/Diagnostics/src/baseline.netcore.json1543
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.cs38
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs36
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/Diagnostics.FunctionalTests.csproj23
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/ExceptionHandlerSampleTest.cs35
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/MiddlewareAnalysisSampleTest.cs34
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/StatusCodeSampleTest.cs76
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/TestFixture.cs41
-rw-r--r--src/Middleware/Diagnostics/test/FunctionalTests/WelcomePageSampleTest.cs36
-rw-r--r--src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs144
-rw-r--r--src/Middleware/Diagnostics/test/UnitTests/ExceptionDetailsProviderTest.cs392
-rw-r--r--src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs520
-rw-r--r--src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj24
-rw-r--r--src/Middleware/Diagnostics/test/UnitTests/Resources/TestFiles/EmbeddedSourceFile.txt30
-rw-r--r--src/Middleware/Diagnostics/test/UnitTests/TestDiagnosticListener.cs83
-rw-r--r--src/Middleware/Diagnostics/test/UnitTests/TestFiles/SourceFile.txt30
-rw-r--r--src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ClassLibraryWithPortablePdbs.csproj8
-rw-r--r--src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ExceptionType.cs20
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/DatabaseErrorPageSample.csproj18
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Startup.cs57
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/web.config9
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/DeveloperExceptionPageSample.csproj16
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Startup.cs33
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/web.config9
-rw-r--r--src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/ExceptionHandlerSample.csproj15
-rw-r--r--src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Startup.cs71
-rw-r--r--src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/web.config9
-rw-r--r--src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/wwwroot/error.html12
-rw-r--r--src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Startup.cs116
-rw-r--r--src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/StatusCodePagesSample.csproj13
-rw-r--r--src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/web.config9
-rw-r--r--src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Startup.cs25
-rw-r--r--src/Middleware/Diagnostics/test/testassets/WelcomePageSample/WelcomePageSample.csproj13
-rw-r--r--src/Middleware/Diagnostics/test/testassets/WelcomePageSample/web.config9
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheck.cs56
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheckOptions.cs15
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/src/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensions.cs79
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj20
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/src/Properties/AssemblyInfo.cs3
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/test/DbContextHealthCheckTest.cs119
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/test/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensionsTest.cs47
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests.csproj14
-rw-r--r--src/Middleware/HealthChecks.EntityFrameworkCore/test/TestDbContext.cs24
-rw-r--r--src/Middleware/HealthChecks/src/Builder/HealthCheckApplicationBuilderExtensions.cs254
-rw-r--r--src/Middleware/HealthChecks/src/HealthCheckMiddleware.cs125
-rw-r--r--src/Middleware/HealthChecks/src/HealthCheckOptions.cs63
-rw-r--r--src/Middleware/HealthChecks/src/HealthCheckResponseWriters.cs18
-rw-r--r--src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj25
-rw-r--r--src/Middleware/HealthChecks/src/baseline.netcore.json5
-rw-r--r--src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareSampleTest.cs75
-rw-r--r--src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareTests.cs688
-rw-r--r--src/Middleware/HealthChecks/test/UnitTests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj18
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/BasicStartup.cs35
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/CustomWriterStartup.cs58
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DBHealthStartup.cs46
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbConnectionHealthCheck.cs61
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbContextHealthStartup.cs85
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/GCInfoHealthCheck.cs86
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/HealthChecksSample.csproj22
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/LivenessProbeStartup.cs68
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/ManagementPortStartup.cs47
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/MyContext.cs20
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Program.cs62
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Properties/launchSettings.json15
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SlowDependencyHealthCheck.cs32
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SqlConnectionHealthCheck.cs25
-rw-r--r--src/Middleware/HealthChecks/test/testassets/HealthChecksSample/appsettings.json13
-rw-r--r--src/Middleware/Middleware.sln345
-rw-r--r--src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.csproj16
-rw-r--r--src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/Startup.cs120
-rw-r--r--src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/web.config9
-rw-r--r--src/Middleware/MiddlewareAnalysis/src/AnalysisBuilder.cs65
-rw-r--r--src/Middleware/MiddlewareAnalysis/src/AnalysisMiddleware.cs85
-rw-r--r--src/Middleware/MiddlewareAnalysis/src/AnalysisServiceCollectionExtensions.cs34
-rw-r--r--src/Middleware/MiddlewareAnalysis/src/AnalysisStartupFilter.cs27
-rw-r--r--src/Middleware/MiddlewareAnalysis/src/Microsoft.AspNetCore.MiddlewareAnalysis.csproj17
-rw-r--r--src/Middleware/MiddlewareAnalysis/src/baseline.netcore.json218
-rw-r--r--src/Middleware/MiddlewareAnalysis/test/Microsoft.AspNetCore.MiddlewareAnalysis.Tests.csproj15
-rw-r--r--src/Middleware/MiddlewareAnalysis/test/MiddlewareAnalysisTests.cs53
-rw-r--r--src/Middleware/MiddlewareAnalysis/test/TestDiagnosticListener.cs35
-rw-r--r--src/Shared/Diagnostics/AttributeValue.cs39
-rw-r--r--src/Shared/Diagnostics/BaseView.cs312
-rw-r--r--src/Shared/Diagnostics/Directory.Build.props5
-rw-r--r--src/Shared/Diagnostics/HelperResult.cs35
179 files changed, 18139 insertions, 31 deletions
diff --git a/.gitmodules b/.gitmodules
index 0c8408d947..d7b9844cc8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,10 +10,6 @@
path = modules/CORS
url = https://github.com/aspnet/CORS.git
branch = master
-[submodule "modules/Diagnostics"]
- path = modules/Diagnostics
- url = https://github.com/aspnet/Diagnostics.git
- branch = master
[submodule "modules/HttpSysServer"]
path = modules/HttpSysServer
url = https://github.com/aspnet/HttpSysServer.git
diff --git a/build/CodeSign.props b/build/CodeSign.props
index ba375f0307..7b1497dae3 100644
--- a/build/CodeSign.props
+++ b/build/CodeSign.props
@@ -26,6 +26,8 @@
<FilesToSign Include="Microsoft.Extensions.DependencyInjection.Abstractions.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
<FilesToSign Include="Microsoft.Extensions.DependencyInjection.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
<FilesToSign Include="Microsoft.Extensions.DiagnosticAdapter.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
+ <FilesToSign Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
+ <FilesToSign Include="Microsoft.Extensions.Diagnostics.HealthChecks.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
<FilesToSign Include="Microsoft.Extensions.FileProviders.Abstractions.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
<FilesToSign Include="Microsoft.Extensions.FileProviders.Composite.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
<FilesToSign Include="Microsoft.Extensions.FileProviders.Embedded.dll" Certificate="$(AssemblySigningCertName)" Container="Microsoft.AspNetCore.App" />
diff --git a/build/artifacts.props b/build/artifacts.props
index 419c353ee0..f493f576d5 100644
--- a/build/artifacts.props
+++ b/build/artifacts.props
@@ -64,9 +64,7 @@
<PackageArtifact Include="Microsoft.DotNet.Web.ProjectTemplates.3.0" Category="ship" />
<PackageArtifact Include="Microsoft.DotNet.Web.Spa.ProjectTemplates" Category="ship" />
<PackageArtifact Include="Microsoft.Extensions.ApiDescription.Design" Category="ship" />
- <PackageArtifact Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Category="ship" />
<PackageArtifact Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Category="ship" />
- <PackageArtifact Include="Microsoft.Extensions.Diagnostics.HealthChecks" Category="ship" />
<PackageArtifact Include="Microsoft.Extensions.Identity.Core" Category="ship" />
<PackageArtifact Include="Microsoft.Extensions.Identity.Stores" Category="ship" />
<PackageArtifact Include="Microsoft.Extensions.Localization.Abstractions" Category="ship" />
@@ -105,7 +103,6 @@
<PackageArtifact Include="Microsoft.AspNetCore.DataProtection" Category="noship" />
<PackageArtifact Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" Category="shipoob" />
<PackageArtifact Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Category="noship" />
- <PackageArtifact Include="Microsoft.AspNetCore.Diagnostics.Elm" Category="noship" />
<PackageArtifact Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Category="noship" />
<PackageArtifact Include="Microsoft.AspNetCore.Diagnostics" Category="noship" />
<PackageArtifact Include="Microsoft.AspNetCore.HostFiltering" Category="noship" />
diff --git a/build/buildorder.props b/build/buildorder.props
index e4b51dbe38..405dcbf9ca 100644
--- a/build/buildorder.props
+++ b/build/buildorder.props
@@ -18,7 +18,6 @@
<RepositoryBuildOrder Include="ServerTests" Order="11" RootPath="$(RepositoryRoot)src\ServerTests\" />
<RepositoryBuildOrder Include="CORS" Order="12" />
<RepositoryBuildOrder Include="Routing" Order="12" />
- <RepositoryBuildOrder Include="Diagnostics" Order="12" />
<RepositoryBuildOrder Include="Localization" Order="13" />
<RepositoryBuildOrder Include="Security" Order="13" />
<RepositoryBuildOrder Include="MetaPackages" Order="13" RootPath="$(RepositoryRoot)src\MetaPackages\" />
diff --git a/build/dependencies.props b/build/dependencies.props
index f68e6eeff1..9f5930da17 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -65,6 +65,8 @@
<MicrosoftExtensionsDependencyInjectionPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion>
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
+ <MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>3.0.0-alpha1-10773</MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>
+ <MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>3.0.0-alpha1-10773</MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
@@ -99,8 +101,8 @@
<MicrosoftExtensionsStackTraceSourcesPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsStackTraceSourcesPackageVersion>
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
- <MicrosoftExtensionsWebEncodersSourcesPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsWebEncodersPackageVersion>
+ <MicrosoftExtensionsWebEncodersSourcesPackageVersion>3.0.0-preview.18569.14</MicrosoftExtensionsWebEncodersSourcesPackageVersion>
<!-- Packages from aspnet/EntityFrameworkCore -->
<MicrosoftEntityFrameworkCoreAbstractionsPackageVersion>3.0.0-preview.18569.2</MicrosoftEntityFrameworkCoreAbstractionsPackageVersion>
diff --git a/build/external-dependencies.props b/build/external-dependencies.props
index d7066c9824..fb658b606b 100644
--- a/build/external-dependencies.props
+++ b/build/external-dependencies.props
@@ -39,6 +39,8 @@
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection.Specification.Tests" Version="$(MicrosoftExtensionsDependencyInjectionSpecificationTestsPackageVersion)" />
<ExternalDependency Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
<ExternalDependency Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
+ <ExternalDependency Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion)" />
+ <ExternalDependency Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="$(MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion)" />
<ExternalDependency Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
<ExternalDependency Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositePackageVersion)" />
<ExternalDependency Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
diff --git a/build/submodules.props b/build/submodules.props
index 70d42cd041..7296c15006 100644
--- a/build/submodules.props
+++ b/build/submodules.props
@@ -41,7 +41,6 @@
<Repository Include="AzureIntegration" RootPath="$(RepositoryRoot)src\AzureIntegration\" />
<Repository Include="BasicMiddleware" />
<Repository Include="CORS" />
- <Repository Include="Diagnostics" />
<Repository Include="HttpSysServer" />
<Repository Include="Identity" />
<Repository Include="IISIntegration" RootPath="$(RepositoryRoot)src\IISIntegration\" />
diff --git a/eng/Baseline.props b/eng/Baseline.props
index 1d647402db..262c43c3ba 100644
--- a/eng/Baseline.props
+++ b/eng/Baseline.props
@@ -114,6 +114,44 @@
<BaselinePackageReference Include="System.Security.Cryptography.Xml" Version="[4.5.0, )" />
<BaselinePackageReference Include="System.Security.Principal.Windows" Version="[4.5.0, )" />
</ItemGroup>
+ <!-- Package: Microsoft.AspNetCore.Diagnostics.Abstractions-->
+ <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.Abstractions' ">
+ <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+ </PropertyGroup>
+ <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.Abstractions' AND '$(TargetFramework)' == 'netstandard2.0' " />
+ <!-- Package: Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore-->
+ <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore' ">
+ <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+ </PropertyGroup>
+ <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore' AND '$(TargetFramework)' == 'netstandard2.0' ">
+ <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[2.2.0, )" />
+ </ItemGroup>
+ <!-- Package: Microsoft.AspNetCore.Diagnostics.HealthChecks-->
+ <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.HealthChecks' ">
+ <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+ </PropertyGroup>
+ <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics.HealthChecks' AND '$(TargetFramework)' == 'netstandard2.0' ">
+ <BaselinePackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.Net.Http.Headers" Version="[2.2.0, )" />
+ </ItemGroup>
+ <!-- Package: Microsoft.AspNetCore.Diagnostics-->
+ <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics' ">
+ <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+ </PropertyGroup>
+ <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Diagnostics' AND '$(TargetFramework)' == 'netstandard2.0' ">
+ <BaselinePackageReference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.Extensions.Options" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="System.Diagnostics.DiagnosticSource" Version="[4.5.0, )" />
+ <BaselinePackageReference Include="System.Reflection.Metadata" Version="[1.6.0, )" />
+ </ItemGroup>
<!-- Package: Microsoft.AspNetCore.Hosting.Abstractions-->
<PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Hosting.Abstractions' ">
<BaselinePackageVersion>2.2.0</BaselinePackageVersion>
@@ -212,6 +250,15 @@
<BaselinePackageReference Include="Microsoft.CSharp" Version="[4.5.0, )" />
<BaselinePackageReference Include="Newtonsoft.Json" Version="[11.0.2, )" />
</ItemGroup>
+ <!-- Package: Microsoft.AspNetCore.MiddlewareAnalysis-->
+ <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.MiddlewareAnalysis' ">
+ <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+ </PropertyGroup>
+ <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.MiddlewareAnalysis' AND '$(TargetFramework)' == 'netstandard2.0' ">
+ <BaselinePackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="System.Diagnostics.DiagnosticSource" Version="[4.5.0, )" />
+ </ItemGroup>
<!-- Package: Microsoft.AspNetCore.Owin-->
<PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.AspNetCore.Owin' ">
<BaselinePackageVersion>2.2.0</BaselinePackageVersion>
@@ -333,6 +380,15 @@
<BaselinePackageReference Include="Microsoft.Net.Http.Headers" Version="[2.2.0, )" />
<BaselinePackageReference Include="System.Text.Encodings.Web" Version="[4.5.0, )" />
</ItemGroup>
+ <!-- Package: Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore-->
+ <PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore' ">
+ <BaselinePackageVersion>2.2.0</BaselinePackageVersion>
+ </PropertyGroup>
+ <ItemGroup Condition=" '$(PackageId)' == 'Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore' AND '$(TargetFramework)' == 'netstandard2.0' ">
+ <BaselinePackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="[2.2.0, )" />
+ <BaselinePackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[2.2.0, )" />
+ </ItemGroup>
<!-- Package: Microsoft.Net.Http.Headers-->
<PropertyGroup Condition=" '$(PackageId)' == 'Microsoft.Net.Http.Headers' ">
<BaselinePackageVersion>2.2.0</BaselinePackageVersion>
diff --git a/eng/Dependencies.props b/eng/Dependencies.props
index 53312853da..6005d9f3f7 100644
--- a/eng/Dependencies.props
+++ b/eng/Dependencies.props
@@ -14,6 +14,8 @@
<LatestPackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
<LatestPackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="$(MicrosoftEntityFrameworkCoreInMemoryPackageVersion)" />
+ <LatestPackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(MicrosoftEntityFrameworkCoreRelationalPackageVersion)" />
+ <LatestPackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="$(MicrosoftEntityFrameworkCoreSqlServerPackageVersion)" />
<LatestPackageReference Include="Microsoft.EntityFrameworkCore" Version="$(MicrosoftEntityFrameworkCorePackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.ActivatorUtilities.Sources" Version="$(MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props
index 164c258a97..92c4598018 100644
--- a/eng/ProjectReferences.props
+++ b/eng/ProjectReferences.props
@@ -42,6 +42,12 @@
<ProjectReferenceProvider Include="dotnet-user-secrets" ProjectPath="$(RepositoryRoot)src\Tools\dotnet-user-secrets\src\dotnet-user-secrets.csproj" />
<ProjectReferenceProvider Include="dotnet-watch" ProjectPath="$(RepositoryRoot)src\Tools\dotnet-watch\src\dotnet-watch.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" ProjectPath="$(RepositoryRoot)src\Tools\FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.Abstractions" ProjectPath="$(RepositoryRoot)src\Middleware\Diagnostics.Abstractions\src\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" ProjectPath="$(RepositoryRoot)src\Middleware\Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics" ProjectPath="$(RepositoryRoot)src\Middleware\Diagnostics\src\Microsoft.AspNetCore.Diagnostics.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" ProjectPath="$(RepositoryRoot)src\Middleware\HealthChecks.EntityFrameworkCore\src\Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" ProjectPath="$(RepositoryRoot)src\Middleware\HealthChecks\src\Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.AspNetCore.MiddlewareAnalysis" ProjectPath="$(RepositoryRoot)src\Middleware\MiddlewareAnalysis\src\Microsoft.AspNetCore.MiddlewareAnalysis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.WebSockets" ProjectPath="$(RepositoryRoot)src\Middleware\WebSockets\src\Microsoft.AspNetCore.WebSockets.csproj" />
</ItemGroup>
</Project>
diff --git a/eng/dependencies.temp.props b/eng/dependencies.temp.props
index ac05d10042..ed709e590f 100644
--- a/eng/dependencies.temp.props
+++ b/eng/dependencies.temp.props
@@ -1,11 +1,9 @@
<!--
-This file is temporary until aspnet/Diagnostics and StaticFiles are merged into this repo.
+This file is temporary until aspnet/StaticFiles are merged into this repo.
This is required to provide dependencies for samples and tests.
-->
<Project>
<ItemGroup>
- <LatestPackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="3.0.0-alpha1-10727" />
- <LatestPackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="3.0.0-alpha1-10727" />
<LatestPackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="3.0.0-alpha1-10727" />
</ItemGroup>
</Project>
diff --git a/eng/tools/BaselineGenerator/baseline.xml b/eng/tools/BaselineGenerator/baseline.xml
index c37bf56d4f..e15e9099cb 100644
--- a/eng/tools/BaselineGenerator/baseline.xml
+++ b/eng/tools/BaselineGenerator/baseline.xml
@@ -16,6 +16,10 @@
<Package Id="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.DataProtection.SystemWeb" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.DataProtection" Version="2.2.0" />
+ <Package Id="Microsoft.AspNetCore.Diagnostics.Abstractions" Version="2.2.0" />
+ <Package Id="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="2.2.0" />
+ <Package Id="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
+ <Package Id="Microsoft.AspNetCore.Diagnostics" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.Hosting.WindowsServices" Version="2.2.0" />
@@ -26,6 +30,7 @@
<Package Id="Microsoft.AspNetCore.Http.Features" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.Http" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.JsonPatch" Version="2.2.0" />
+ <Package Id="Microsoft.AspNetCore.MiddlewareAnalysis" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.Owin" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.Server.Kestrel.Core" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.Server.Kestrel.Https" Version="2.2.0" />
@@ -36,5 +41,6 @@
<Package Id="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.WebSockets" Version="2.2.0" />
<Package Id="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
+ <Package Id="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="2.2.0" />
<Package Id="Microsoft.Net.Http.Headers" Version="2.2.0" />
</Baseline>
diff --git a/modules/Diagnostics b/modules/Diagnostics
deleted file mode 160000
-Subproject 082d505977d72c75b68935c93c21feded6928e5
diff --git a/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj b/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj
index 46854acfb4..50970c8d86 100644
--- a/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj
+++ b/src/Hosting/Server.IntegrationTesting/src/Microsoft.AspNetCore.Server.IntegrationTesting.csproj
@@ -11,6 +11,9 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;testing</PackageTags>
<EnableApiCheck>false</EnableApiCheck>
+ <UseLatestPackageReferences>true</UseLatestPackageReferences>
+ <UseProjectReferences>true</UseProjectReferences>
+ <IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
diff --git a/src/Middleware/Diagnostics.Abstractions/src/CompilationFailure.cs b/src/Middleware/Diagnostics.Abstractions/src/CompilationFailure.cs
new file mode 100644
index 0000000000..4a100e3dc0
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/CompilationFailure.cs
@@ -0,0 +1,83 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// Describes a failure compiling a specific file.
+ /// </summary>
+ public class CompilationFailure
+ {
+ /// <summary>
+ /// Initializes a new instance of <see cref="CompilationFailure"/>.
+ /// </summary>
+ /// <param name="sourceFilePath">Path for the file that produced the compilation failure.</param>
+ /// <param name="sourceFileContent">Contents of the file being compiled.</param>
+ /// <param name="compiledContent">For templated languages (such as Asp.Net Core Razor), the generated content.
+ /// </param>
+ /// <param name="messages">One or or more <see cref="DiagnosticMessage"/> instances.</param>
+ public CompilationFailure(
+ string sourceFilePath,
+ string sourceFileContent,
+ string compiledContent,
+ IEnumerable<DiagnosticMessage> messages)
+ : this(sourceFilePath, sourceFileContent, compiledContent, messages, failureSummary: null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="CompilationFailure"/>.
+ /// </summary>
+ /// <param name="sourceFilePath">Path for the file that produced the compilation failure.</param>
+ /// <param name="sourceFileContent">Contents of the file being compiled.</param>
+ /// <param name="compiledContent">For templated languages (such as Asp.Net Core Razor), the generated content.
+ /// </param>
+ /// <param name="messages">One or or more <see cref="DiagnosticMessage"/> instances.</param>
+ /// <param name="failureSummary">Summary message or instructions to fix the failure.</param>
+ public CompilationFailure(
+ string sourceFilePath,
+ string sourceFileContent,
+ string compiledContent,
+ IEnumerable<DiagnosticMessage> messages,
+ string failureSummary)
+ {
+ SourceFilePath = sourceFilePath;
+ SourceFileContent = sourceFileContent;
+ CompiledContent = compiledContent;
+ Messages = messages;
+ FailureSummary = failureSummary;
+ }
+
+ /// <summary>
+ /// Path of the file that produced the compilation failure.
+ /// </summary>
+ public string SourceFilePath { get; }
+
+ /// <summary>
+ /// Contents of the file.
+ /// </summary>
+ public string SourceFileContent { get; }
+
+ /// <summary>
+ /// Contents being compiled.
+ /// </summary>
+ /// <remarks>
+ /// For templated files, the <see cref="SourceFileContent"/> represents the original content and
+ /// <see cref="CompiledContent"/> represents the transformed content. This property can be null if
+ /// the exception is encountered during transformation.
+ /// </remarks>
+ public string CompiledContent { get; }
+
+ /// <summary>
+ /// Gets a sequence of <see cref="DiagnosticMessage"/> produced as a result of compilation.
+ /// </summary>
+ public IEnumerable<DiagnosticMessage> Messages { get; }
+
+ /// <summary>
+ /// Summary message or instructions to fix the failure.
+ /// </summary>
+ public string FailureSummary { get; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.Abstractions/src/DiagnosticMessage.cs b/src/Middleware/Diagnostics.Abstractions/src/DiagnosticMessage.cs
new file mode 100644
index 0000000000..dc9f65f507
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/DiagnosticMessage.cs
@@ -0,0 +1,64 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// A single diagnostic message.
+ /// </summary>
+ public class DiagnosticMessage
+ {
+ public DiagnosticMessage(
+ string message,
+ string formattedMessage,
+ string filePath,
+ int startLine,
+ int startColumn,
+ int endLine,
+ int endColumn)
+ {
+ Message = message;
+ SourceFilePath = filePath;
+ StartLine = startLine;
+ EndLine = endLine;
+ StartColumn = startColumn;
+ EndColumn = endColumn;
+ FormattedMessage = formattedMessage;
+ }
+
+ /// <summary>
+ /// Path of the file that produced the message.
+ /// </summary>
+ public string SourceFilePath { get; }
+
+ /// <summary>
+ /// Gets the error message.
+ /// </summary>
+ public string Message { get; }
+
+ /// <summary>
+ /// Gets the one-based line index for the start of the compilation error.
+ /// </summary>
+ public int StartLine { get; }
+
+ /// <summary>
+ /// Gets the zero-based column index for the start of the compilation error.
+ /// </summary>
+ public int StartColumn { get; }
+
+ /// <summary>
+ /// Gets the one-based line index for the end of the compilation error.
+ /// </summary>
+ public int EndLine { get; }
+
+ /// <summary>
+ /// Gets the zero-based column index for the end of the compilation error.
+ /// </summary>
+ public int EndColumn { get; }
+
+ /// <summary>
+ /// Gets the formatted error message.
+ /// </summary>
+ public string FormattedMessage { get; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.Abstractions/src/ICompilationException.cs b/src/Middleware/Diagnostics.Abstractions/src/ICompilationException.cs
new file mode 100644
index 0000000000..480dddf74b
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/ICompilationException.cs
@@ -0,0 +1,22 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// Specifies the contract for an exception representing compilation failure.
+ /// </summary>
+ /// <remarks>
+ /// This interface is implemented on exceptions thrown during compilation to enable consumers
+ /// to read compilation-related data out of the exception
+ /// </remarks>
+ public interface ICompilationException
+ {
+ /// <summary>
+ /// Gets a sequence of <see cref="CompilationFailure"/> with compilation failures.
+ /// </summary>
+ IEnumerable<CompilationFailure> CompilationFailures { get; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerFeature.cs b/src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerFeature.cs
new file mode 100644
index 0000000000..23069e3df4
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerFeature.cs
@@ -0,0 +1,12 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public interface IExceptionHandlerFeature
+ {
+ Exception Error { get; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerPathFeature.cs b/src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerPathFeature.cs
new file mode 100644
index 0000000000..7de318da8c
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/IExceptionHandlerPathFeature.cs
@@ -0,0 +1,17 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// Represents an exception handler with the original path of the request.
+ /// </summary>
+ public interface IExceptionHandlerPathFeature : IExceptionHandlerFeature
+ {
+ /// <summary>
+ /// The portion of the request path that identifies the requested resource. The value
+ /// is un-escaped.
+ /// </summary>
+ string Path { get; }
+ }
+}
diff --git a/src/Middleware/Diagnostics.Abstractions/src/IStatusCodePagesFeature.cs b/src/Middleware/Diagnostics.Abstractions/src/IStatusCodePagesFeature.cs
new file mode 100644
index 0000000000..d5c709e869
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/IStatusCodePagesFeature.cs
@@ -0,0 +1,16 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// Represents the Status code pages feature.
+ /// </summary>
+ public interface IStatusCodePagesFeature
+ {
+ /// <summary>
+ /// Indicates if the status code middleware will handle responses.
+ /// </summary>
+ bool Enabled { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.Abstractions/src/IStatusCodeReExecuteFeature.cs b/src/Middleware/Diagnostics.Abstractions/src/IStatusCodeReExecuteFeature.cs
new file mode 100644
index 0000000000..31451e0bc7
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/IStatusCodeReExecuteFeature.cs
@@ -0,0 +1,14 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public interface IStatusCodeReExecuteFeature
+ {
+ string OriginalPathBase { get; set; }
+
+ string OriginalPath { get; set; }
+
+ string OriginalQueryString { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.Abstractions/src/Microsoft.AspNetCore.Diagnostics.Abstractions.csproj b/src/Middleware/Diagnostics.Abstractions/src/Microsoft.AspNetCore.Diagnostics.Abstractions.csproj
new file mode 100644
index 0000000000..ee9c1f7b85
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/Microsoft.AspNetCore.Diagnostics.Abstractions.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>ASP.NET Core diagnostics middleware abstractions and feature interface definitions.</Description>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <NoWarn>$(NoWarn);CS1591</NoWarn>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>aspnetcore;diagnostics</PackageTags>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics.Abstractions/src/baseline.netcore.json b/src/Middleware/Diagnostics.Abstractions/src/baseline.netcore.json
new file mode 100644
index 0000000000..6cbf6b065c
--- /dev/null
+++ b/src/Middleware/Diagnostics.Abstractions/src/baseline.netcore.json
@@ -0,0 +1,356 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.Diagnostics.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.CompilationFailure",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_SourceFilePath",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_SourceFileContent",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_CompiledContent",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Messages",
+ "Parameters": [],
+ "ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Diagnostics.DiagnosticMessage>",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_FailureSummary",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "sourceFilePath",
+ "Type": "System.String"
+ },
+ {
+ "Name": "sourceFileContent",
+ "Type": "System.String"
+ },
+ {
+ "Name": "compiledContent",
+ "Type": "System.String"
+ },
+ {
+ "Name": "messages",
+ "Type": "System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Diagnostics.DiagnosticMessage>"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "sourceFilePath",
+ "Type": "System.String"
+ },
+ {
+ "Name": "sourceFileContent",
+ "Type": "System.String"
+ },
+ {
+ "Name": "compiledContent",
+ "Type": "System.String"
+ },
+ {
+ "Name": "messages",
+ "Type": "System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Diagnostics.DiagnosticMessage>"
+ },
+ {
+ "Name": "failureSummary",
+ "Type": "System.String"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.DiagnosticMessage",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_SourceFilePath",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Message",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_StartLine",
+ "Parameters": [],
+ "ReturnType": "System.Int32",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_StartColumn",
+ "Parameters": [],
+ "ReturnType": "System.Int32",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_EndLine",
+ "Parameters": [],
+ "ReturnType": "System.Int32",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_EndColumn",
+ "Parameters": [],
+ "ReturnType": "System.Int32",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_FormattedMessage",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "message",
+ "Type": "System.String"
+ },
+ {
+ "Name": "formattedMessage",
+ "Type": "System.String"
+ },
+ {
+ "Name": "filePath",
+ "Type": "System.String"
+ },
+ {
+ "Name": "startLine",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "startColumn",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "endLine",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "endColumn",
+ "Type": "System.Int32"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.ICompilationException",
+ "Visibility": "Public",
+ "Kind": "Interface",
+ "Abstract": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_CompilationFailures",
+ "Parameters": [],
+ "ReturnType": "System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Diagnostics.CompilationFailure>",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature",
+ "Visibility": "Public",
+ "Kind": "Interface",
+ "Abstract": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Error",
+ "Parameters": [],
+ "ReturnType": "System.Exception",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.IExceptionHandlerPathFeature",
+ "Visibility": "Public",
+ "Kind": "Interface",
+ "Abstract": true,
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Path",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.IStatusCodePagesFeature",
+ "Visibility": "Public",
+ "Kind": "Interface",
+ "Abstract": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Enabled",
+ "Parameters": [],
+ "ReturnType": "System.Boolean",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_Enabled",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Boolean"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "Kind": "Interface",
+ "Abstract": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_OriginalPathBase",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_OriginalPathBase",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_OriginalPath",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_OriginalPath",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_OriginalQueryString",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_OriginalQueryString",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageExtensions.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageExtensions.cs
new file mode 100644
index 0000000000..6184c9f310
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageExtensions.cs
@@ -0,0 +1,62 @@
+// 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 Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// <see cref="IApplicationBuilder"/> extension methods for the <see cref="DatabaseErrorPageMiddleware"/>.
+ /// </summary>
+ public static class DatabaseErrorPageExtensions
+ {
+ /// <summary>
+ /// Captures synchronous and asynchronous database related exceptions from the pipeline that may be resolved using Entity Framework
+ /// migrations. When these exceptions occur an HTML response with details of possible actions to resolve the issue is generated.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/> to register the middleware with.</param>
+ /// <returns>The same <see cref="IApplicationBuilder"/> instance so that multiple calls can be chained.</returns>
+ public static IApplicationBuilder UseDatabaseErrorPage(this IApplicationBuilder app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseDatabaseErrorPage(new DatabaseErrorPageOptions());
+ }
+
+ /// <summary>
+ /// Captures synchronous and asynchronous database related exceptions from the pipeline that may be resolved using Entity Framework
+ /// migrations. When these exceptions occur an HTML response with details of possible actions to resolve the issue is generated.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/> to register the middleware with.</param>
+ /// <param name="options">A <see cref="DatabaseErrorPageOptions"/> that specifies options for the middleware.</param>
+ /// <returns>The same <see cref="IApplicationBuilder"/> instance so that multiple calls can be chained.</returns>
+ public static IApplicationBuilder UseDatabaseErrorPage(
+ this IApplicationBuilder app, DatabaseErrorPageOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ app = app.UseMiddleware<DatabaseErrorPageMiddleware>(Options.Create(options));
+
+ app.UseMigrationsEndPoint(new MigrationsEndPointOptions
+ {
+ Path = options.MigrationsEndPointPath
+ });
+
+ return app;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageMiddleware.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageMiddleware.cs
new file mode 100644
index 0000000000..c7dee3cf1f
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageMiddleware.cs
@@ -0,0 +1,253 @@
+// 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.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Internal;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
+using Microsoft.AspNetCore.Http;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
+{
+ /// <summary>
+ /// Captures synchronous and asynchronous database related exceptions from the pipeline that may be resolved using Entity Framework
+ /// migrations. When these exceptions occur an HTML response with details of possible actions to resolve the issue is generated.
+ /// </summary>
+ public class DatabaseErrorPageMiddleware : IObserver<DiagnosticListener>, IObserver<KeyValuePair<string, object>>
+ {
+ private static readonly AsyncLocal<DiagnosticHolder> _localDiagnostic = new AsyncLocal<DiagnosticHolder>();
+
+ private sealed class DiagnosticHolder
+ {
+ public void Hold(Exception exception, Type contextType)
+ {
+ Exception = exception;
+ ContextType = contextType;
+ }
+
+ public Exception Exception { get; private set; }
+ public Type ContextType { get; private set; }
+ }
+
+ private readonly RequestDelegate _next;
+ private readonly DatabaseErrorPageOptions _options;
+ private readonly ILogger _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DatabaseErrorPageMiddleware" /> class
+ /// </summary>
+ /// <param name="next">Delegate to execute the next piece of middleware in the request pipeline.</param>
+ /// <param name="loggerFactory">
+ /// The <see cref="ILoggerFactory" /> for the application. This middleware both produces logging messages and
+ /// consumes them to detect database related exception.
+ /// </param>
+ /// <param name="options">The options to control what information is displayed on the error page.</param>
+ public DatabaseErrorPageMiddleware(
+ RequestDelegate next,
+ ILoggerFactory loggerFactory,
+ IOptions<DatabaseErrorPageOptions> options)
+ {
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ if (loggerFactory == null)
+ {
+ throw new ArgumentNullException(nameof(loggerFactory));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ _next = next;
+ _options = options.Value;
+ _logger = loggerFactory.CreateLogger<DatabaseErrorPageMiddleware>();
+
+ // Note: this currently leaks if the server hosting this middleware is disposed.
+ // See aspnet/Home #2825
+ DiagnosticListener.AllListeners.Subscribe(this);
+ }
+
+ /// <summary>
+ /// Process an individual request.
+ /// </summary>
+ /// <param name="httpContext">The HTTP context for the current request.</param>
+ /// <returns>A task that represents the asynchronous operation.</returns>
+ public virtual async Task Invoke(HttpContext httpContext)
+ {
+ if (httpContext == null)
+ {
+ throw new ArgumentNullException(nameof(httpContext));
+ }
+
+ try
+ {
+ // Because CallContext is cloned at each async operation we cannot
+ // lazily create the error object when an error is encountered, otherwise
+ // it will not be available to code outside of the current async context.
+ // We create it ahead of time so that any cloning just clones the reference
+ // to the object that will hold any errors.
+
+ _localDiagnostic.Value = new DiagnosticHolder();
+
+ await _next(httpContext);
+ }
+ catch (Exception exception)
+ {
+ try
+ {
+ if (ShouldDisplayErrorPage(exception))
+ {
+ var contextType = _localDiagnostic.Value.ContextType;
+ var context = (DbContext)httpContext.RequestServices.GetService(contextType);
+
+ if (context == null)
+ {
+ _logger.ContextNotRegisteredDatabaseErrorPageMiddleware(contextType.FullName);
+ }
+ else
+ {
+ var relationalDatabaseCreator = context.GetService<IDatabaseCreator>() as IRelationalDatabaseCreator;
+ if (relationalDatabaseCreator == null)
+ {
+ _logger.NotRelationalDatabase();
+ }
+ else
+ {
+ var databaseExists = await relationalDatabaseCreator.ExistsAsync();
+
+ var migrationsAssembly = context.GetService<IMigrationsAssembly>();
+ var modelDiffer = context.GetService<IMigrationsModelDiffer>();
+
+ // HasDifferences will return true if there is no model snapshot, but if there is an existing database
+ // and no model snapshot then we don't want to show the error page since they are most likely targeting
+ // and existing database and have just misconfigured their model
+
+ var pendingModelChanges
+ = (!databaseExists || migrationsAssembly.ModelSnapshot != null)
+ && modelDiffer.HasDifferences(migrationsAssembly.ModelSnapshot?.Model, context.Model);
+
+ var pendingMigrations
+ = (databaseExists
+ ? await context.Database.GetPendingMigrationsAsync()
+ : context.Database.GetMigrations())
+ .ToArray();
+
+ if (pendingModelChanges || pendingMigrations.Any())
+ {
+ var page = new DatabaseErrorPage
+ {
+ Model = new DatabaseErrorPageModel(
+ contextType, exception, databaseExists, pendingModelChanges, pendingMigrations, _options)
+ };
+
+ await page.ExecuteAsync(httpContext);
+
+ return;
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.DatabaseErrorPageMiddlewareException(e);
+ }
+
+ throw;
+ }
+ }
+
+ private bool ShouldDisplayErrorPage(Exception exception)
+ {
+ _logger.AttemptingToMatchException(exception.GetType());
+
+ var lastRecordedException = _localDiagnostic.Value.Exception;
+
+ if (lastRecordedException == null)
+ {
+ _logger.NoRecordedException();
+
+ return false;
+ }
+
+ var match = false;
+
+ for (var e = exception; e != null && !match; e = e.InnerException)
+ {
+ match = lastRecordedException == e;
+ }
+
+ if (!match)
+ {
+ _logger.NoMatch();
+
+ return false;
+ }
+
+ _logger.Matched();
+
+ return true;
+ }
+
+ void IObserver<DiagnosticListener>.OnNext(DiagnosticListener diagnosticListener)
+ {
+ if (diagnosticListener.Name == DbLoggerCategory.Name)
+ {
+ diagnosticListener.Subscribe(this);
+ }
+ }
+
+ void IObserver<KeyValuePair<string, object>>.OnNext(KeyValuePair<string, object> keyValuePair)
+ {
+ switch (keyValuePair.Value)
+ {
+ // NB: _localDiagnostic.Value can be null when this middleware has been leaked.
+
+ case DbContextErrorEventData contextErrorEventData:
+ {
+ _localDiagnostic.Value?.Hold(contextErrorEventData.Exception, contextErrorEventData.Context.GetType());
+
+ break;
+ }
+ case DbContextTypeErrorEventData contextTypeErrorEventData:
+ {
+ _localDiagnostic.Value?.Hold(contextTypeErrorEventData.Exception, contextTypeErrorEventData.ContextType);
+
+ break;
+ }
+ }
+ }
+
+ void IObserver<DiagnosticListener>.OnCompleted()
+ {
+ }
+
+ void IObserver<DiagnosticListener>.OnError(Exception error)
+ {
+ }
+
+ void IObserver<KeyValuePair<string, object>>.OnCompleted()
+ {
+ }
+
+ void IObserver<KeyValuePair<string, object>>.OnError(Exception error)
+ {
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageOptions.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageOptions.cs
new file mode 100644
index 0000000000..765467e5f6
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/DatabaseErrorPageOptions.cs
@@ -0,0 +1,21 @@
+// 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 Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
+using Microsoft.AspNetCore.Http;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// Options for the <see cref="DatabaseErrorPageMiddleware"/>.
+ /// </summary>
+ public class DatabaseErrorPageOptions
+ {
+ /// <summary>
+ /// Gets or sets the path that <see cref="MigrationsEndPointMiddleware"/> will listen
+ /// for requests to execute migrations commands.
+ /// </summary>
+ public virtual PathString MigrationsEndPointPath { get; set; } = MigrationsEndPointOptions.DefaultPath;
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Internal/DiagnosticsEntityFrameworkCoreLoggerExtensions.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Internal/DiagnosticsEntityFrameworkCoreLoggerExtensions.cs
new file mode 100644
index 0000000000..e7749f488a
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Internal/DiagnosticsEntityFrameworkCoreLoggerExtensions.cs
@@ -0,0 +1,153 @@
+// 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 Microsoft.Extensions.Logging;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Internal
+{
+ internal static class DiagnosticsEntityFrameworkCoreLoggerExtensions
+ {
+ // MigrationsEndPointMiddleware
+ private static readonly Action<ILogger, Exception> _noContextType = LoggerMessage.Define(
+ LogLevel.Error,
+ new EventId(1, "NoContextType"),
+ "No context type was specified. Ensure the form data from the request includes a contextTypeName value, specifying the context to apply migrations for.");
+
+ private static readonly Action<ILogger, string, Exception> _invalidContextType = LoggerMessage.Define<string>(
+ LogLevel.Error,
+ new EventId(2, "InvalidContextType"),
+ "The context type '{ContextTypeName}' could not be loaded. Ensure this is the correct type name for the context you are trying to apply migrations for.");
+
+ private static readonly Action<ILogger, string, Exception> _contextNotRegistered = LoggerMessage.Define<string>(
+ LogLevel.Error,
+ new EventId(3, "ContextNotRegistered"),
+ "The context type '{ContextTypeName}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped<>() inside the UseServices(...) call in your application startup code.");
+
+ private static readonly Action<ILogger, string, Exception> _requestPathMatched = LoggerMessage.Define<string>(
+ LogLevel.Debug,
+ new EventId(4, "RequestPathMatched"),
+ "Request path matched the path configured for this migrations endpoint({RequestPath}). Attempting to process the migrations request.");
+
+ private static readonly Action<ILogger, string, Exception> _applyingMigrations = LoggerMessage.Define<string>(
+ LogLevel.Debug,
+ new EventId(5, "ApplyingMigrations"),
+ "Request is valid, applying migrations for context '{ContextTypeName}'");
+
+ private static readonly Action<ILogger, string, Exception> _migrationsApplied = LoggerMessage.Define<string>(
+ LogLevel.Debug,
+ new EventId(6, "MigrationsApplied"),
+ "Migrations successfully applied for context '{ContextTypeName}'.");
+
+ private static readonly Action<ILogger, string, Exception> _migrationsEndPointMiddlewareException = LoggerMessage.Define<string>(
+ LogLevel.Error,
+ new EventId(7, "MigrationsEndPointException"),
+ "An error occurred while applying the migrations for '{ContextTypeName}'. See InnerException for details:");
+
+ // DatabaseErrorPageMiddleware
+ private static readonly Action<ILogger, Type, Exception> _attemptingToMatchException = LoggerMessage.Define<Type>(
+ LogLevel.Debug,
+ new EventId(1, "AttemptingToMatchException"),
+ "{ExceptionType} occurred, checking if Entity Framework recorded this exception as resulting from a failed database operation.");
+
+ private static readonly Action<ILogger, Exception> _noRecordedException = LoggerMessage.Define(
+ LogLevel.Debug,
+ new EventId(2, "NoRecordedException"),
+ "Entity Framework did not record any exceptions due to failed database operations. This means the current exception is not a failed Entity Framework database operation, or the current exception occurred from a DbContext that was not obtained from request services.");
+
+ private static readonly Action<ILogger, Exception> _noMatch = LoggerMessage.Define(
+ LogLevel.Debug,
+ new EventId(3, "NoMatchFound"),
+ "The current exception (and its inner exceptions) do not match the last exception Entity Framework recorded due to a failed database operation. This means the database operation exception was handled and another exception occurred later in the request.");
+
+ private static readonly Action<ILogger, Exception> _matched = LoggerMessage.Define(
+ LogLevel.Debug,
+ new EventId(4, "MatchFound"),
+ "Entity Framework recorded that the current exception was due to a failed database operation. Attempting to show database error page.");
+
+ private static readonly Action<ILogger, string, Exception> _contextNotRegisteredDatabaseErrorPageMiddleware = LoggerMessage.Define<string>(
+ LogLevel.Error,
+ new EventId(5, "ContextNotRegistered"),
+ "The context type '{ContextTypeName}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped<>() inside the UseServices(...) call in your application startup code. Skipping display of the database error page.");
+
+ private static readonly Action<ILogger, Exception> _notRelationalDatabase = LoggerMessage.Define(
+ LogLevel.Debug,
+ new EventId(6, "NotRelationalDatabase"),
+ "The target data store is not a relational database. Skipping the database error page.");
+
+ private static readonly Action<ILogger, Exception> _databaseErrorPageMiddlewareException = LoggerMessage.Define(
+ LogLevel.Error,
+ new EventId(7, "DatabaseErrorPageException"),
+ "An exception occurred while calculating the database error page content. Skipping display of the database error page.");
+
+ public static void NoContextType(this ILogger logger)
+ {
+ _noContextType(logger, null);
+ }
+
+ public static void InvalidContextType(this ILogger logger, string contextTypeName)
+ {
+ _invalidContextType(logger, contextTypeName, null);
+ }
+
+ public static void ContextNotRegistered(this ILogger logger, string contextTypeName)
+ {
+ _contextNotRegistered(logger, contextTypeName, null);
+ }
+
+ public static void RequestPathMatched(this ILogger logger, string requestPath)
+ {
+ _requestPathMatched(logger, requestPath, null);
+ }
+
+ public static void ApplyingMigrations(this ILogger logger, string contextTypeName)
+ {
+ _applyingMigrations(logger, contextTypeName, null);
+ }
+
+ public static void MigrationsApplied(this ILogger logger, string contextTypeName)
+ {
+ _migrationsApplied(logger, contextTypeName, null);
+ }
+
+ public static void MigrationsEndPointMiddlewareException(this ILogger logger, string context, Exception exception)
+ {
+ _migrationsEndPointMiddlewareException(logger, context, exception);
+ }
+
+ public static void AttemptingToMatchException(this ILogger logger, Type exceptionType)
+ {
+ _attemptingToMatchException(logger, exceptionType, null);
+ }
+
+ public static void NoRecordedException(this ILogger logger)
+ {
+ _noRecordedException(logger, null);
+ }
+
+ public static void NoMatch(this ILogger logger)
+ {
+ _noMatch(logger, null);
+ }
+
+ public static void Matched(this ILogger logger)
+ {
+ _matched(logger, null);
+ }
+
+ public static void NotRelationalDatabase(this ILogger logger)
+ {
+ _notRelationalDatabase(logger, null);
+ }
+
+ public static void ContextNotRegisteredDatabaseErrorPageMiddleware(this ILogger logger, string contextTypeName)
+ {
+ _contextNotRegisteredDatabaseErrorPageMiddleware(logger, contextTypeName, null);
+ }
+
+ public static void DatabaseErrorPageMiddlewareException(this ILogger logger, Exception exception)
+ {
+ _databaseErrorPageMiddlewareException(logger, exception);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj
new file mode 100644
index 0000000000..f418a50148
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>ASP.NET Core middleware for Entity Framework Core error pages. Use this middleware to detect and diagnose errors with Entity Framework Core migrations.</Description>
+ <TargetFrameworks>netstandard2.0</TargetFrameworks>
+ <NoWarn>$(NoWarn);CS1591</NoWarn>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>aspnetcore;diagnostics;entityframeworkcore</PackageTags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
+ <Reference Include="Microsoft.EntityFrameworkCore.Relational" />
+ <Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointExtensions.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointExtensions.cs
new file mode 100644
index 0000000000..c635bb43fd
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointExtensions.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// <see cref="IApplicationBuilder"/> extension methods for the <see cref="MigrationsEndPointMiddleware"/>.
+ /// </summary>
+ public static class MigrationsEndPointExtensions
+ {
+ /// <summary>
+ /// Processes requests to execute migrations operations. The middleware will listen for requests made to <see cref="MigrationsEndPointOptions.DefaultPath"/>.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/> to register the middleware with.</param>
+ /// <returns>The same <see cref="IApplicationBuilder"/> instance so that multiple calls can be chained.</returns>
+ public static IApplicationBuilder UseMigrationsEndPoint(this IApplicationBuilder app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseMigrationsEndPoint(new MigrationsEndPointOptions());
+ }
+
+ /// <summary>
+ /// Processes requests to execute migrations operations. The middleware will listen for requests to the path configured in <paramref name="options"/>.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/> to register the middleware with.</param>
+ /// <param name="options">An action to set the options for the middleware.</param>
+ /// <returns>The same <see cref="IApplicationBuilder"/> instance so that multiple calls can be chained.</returns>
+ public static IApplicationBuilder UseMigrationsEndPoint(this IApplicationBuilder app, MigrationsEndPointOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ return app.UseMiddleware<MigrationsEndPointMiddleware>(Options.Create(options));
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointMiddleware.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointMiddleware.cs
new file mode 100644
index 0000000000..4562bf4218
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointMiddleware.cs
@@ -0,0 +1,159 @@
+// Copyright (c) Microsoft Open Technologies, Inc. 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.Net;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Internal;
+using Microsoft.AspNetCore.Http;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
+{
+ /// <summary>
+ /// Processes requests to execute migrations operations. The middleware will listen for requests to the path configured in the supplied options.
+ /// </summary>
+ public class MigrationsEndPointMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly ILogger _logger;
+ private readonly MigrationsEndPointOptions _options;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MigrationsEndPointMiddleware"/> class
+ /// </summary>
+ /// <param name="next">Delegate to execute the next piece of middleware in the request pipeline.</param>
+ /// <param name="logger">The <see cref="Logger{T}"/> to write messages to.</param>
+ /// <param name="options">The options to control the behavior of the middleware.</param>
+ public MigrationsEndPointMiddleware(
+ RequestDelegate next,
+ ILogger<MigrationsEndPointMiddleware> logger,
+ IOptions<MigrationsEndPointOptions> options)
+ {
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ if (logger == null)
+ {
+ throw new ArgumentNullException(nameof(logger));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ _next = next;
+ _logger = logger;
+ _options = options.Value;
+ }
+
+ /// <summary>
+ /// Process an individual request.
+ /// </summary>
+ /// <param name="context">The context for the current request.</param>
+ /// <returns>A task that represents the asynchronous operation.</returns>
+ public virtual async Task Invoke(HttpContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (context.Request.Path.Equals(_options.Path))
+ {
+ _logger.RequestPathMatched(context.Request.Path);
+
+ var db = await GetDbContext(context, _logger);
+
+ if (db != null)
+ {
+ try
+ {
+ _logger.ApplyingMigrations(db.GetType().FullName);
+
+ db.Database.Migrate();
+
+ context.Response.StatusCode = (int)HttpStatusCode.NoContent;
+ context.Response.Headers.Add("Pragma", new[] { "no-cache" });
+ context.Response.Headers.Add("Cache-Control", new[] { "no-cache" });
+
+ _logger.MigrationsApplied(db.GetType().FullName);
+ }
+ catch (Exception ex)
+ {
+ var message = Strings.FormatMigrationsEndPointMiddleware_Exception(db.GetType().FullName) + ex;
+
+ _logger.MigrationsEndPointMiddlewareException(db.GetType().FullName, ex);
+
+ throw new InvalidOperationException(message, ex);
+ }
+ }
+ }
+ else
+ {
+ await _next(context);
+ }
+ }
+
+ private static async Task<DbContext> GetDbContext(HttpContext context, ILogger logger)
+ {
+ var form = await context.Request.ReadFormAsync();
+ var contextTypeName = form["context"];
+
+ if (string.IsNullOrWhiteSpace(contextTypeName))
+ {
+ logger.NoContextType();
+
+ await WriteErrorToResponse(context.Response, Strings.MigrationsEndPointMiddleware_NoContextType);
+
+ return null;
+ }
+
+ var contextType = Type.GetType(contextTypeName);
+
+ if (contextType == null)
+ {
+ var message = Strings.FormatMigrationsEndPointMiddleware_InvalidContextType(contextTypeName);
+
+ logger.InvalidContextType(contextTypeName);
+
+ await WriteErrorToResponse(context.Response, message);
+
+ return null;
+ }
+
+ var db = (DbContext)context.RequestServices.GetService(contextType);
+
+ if (db == null)
+ {
+ var message = Strings.FormatMigrationsEndPointMiddleware_ContextNotRegistered(contextType.FullName);
+
+ logger.ContextNotRegistered(contextType.FullName);
+
+ await WriteErrorToResponse(context.Response, message);
+
+ return null;
+ }
+
+ return db;
+ }
+
+ private static async Task WriteErrorToResponse(HttpResponse response, string error)
+ {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.Headers.Add("Pragma", new[] { "no-cache" });
+ response.Headers.Add("Cache-Control", new[] { "no-cache" });
+ response.ContentType = "text/plain";
+
+ // Padding to >512 to ensure IE doesn't hide the message
+ // http://stackoverflow.com/questions/16741062/what-rules-does-ie-use-to-determine-whether-to-show-the-entity-body
+ await response.WriteAsync(error.PadRight(513));
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointOptions.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointOptions.cs
new file mode 100644
index 0000000000..48b053c13f
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/MigrationsEndPointOptions.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
+using Microsoft.AspNetCore.Http;
+
+// ReSharper disable once CheckNamespace
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// Options for the <see cref="MigrationsEndPointMiddleware"/>.
+ /// </summary>
+ public class MigrationsEndPointOptions
+ {
+ /// <summary>
+ /// The default value for <see cref="Path"/>.
+ /// </summary>
+ public static PathString DefaultPath = new PathString("/ApplyDatabaseMigrations");
+
+ /// <summary>
+ /// Gets or sets the path that the <see cref="MigrationsEndPointMiddleware"/> will listen
+ /// for requests to execute migrations commands.
+ /// </summary>
+ public virtual PathString Path { get; set; } = DefaultPath;
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/AssemblyInfo.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..019e858be0
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/AssemblyInfo.cs
@@ -0,0 +1,8 @@
+// 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.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/Strings.Designer.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/Strings.Designer.cs
new file mode 100644
index 0000000000..3266d2f144
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Properties/Strings.Designer.cs
@@ -0,0 +1,408 @@
+// <auto-generated />
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
+{
+ using System.Globalization;
+ using System.Reflection;
+ using System.Resources;
+
+ internal static class Strings
+ {
+ private static readonly ResourceManager _resourceManager
+ = new ResourceManager("Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Strings", typeof(Strings).GetTypeInfo().Assembly);
+
+ /// <summary>
+ /// The context type '{0}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped&lt;&gt;() inside the UseServices(...) call in your application startup code. Skipping display of the database error page.
+ /// </summary>
+ internal static string DatabaseErrorPageMiddleware_ContextNotRegistered
+ {
+ get => GetString("DatabaseErrorPageMiddleware_ContextNotRegistered");
+ }
+
+ /// <summary>
+ /// The context type '{0}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped&lt;&gt;() inside the UseServices(...) call in your application startup code. Skipping display of the database error page.
+ /// </summary>
+ internal static string FormatDatabaseErrorPageMiddleware_ContextNotRegistered(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPageMiddleware_ContextNotRegistered"), p0);
+
+ /// <summary>
+ /// An exception occurred while calculating the database error page content. Skipping display of the database error page.
+ /// </summary>
+ internal static string DatabaseErrorPageMiddleware_Exception
+ {
+ get => GetString("DatabaseErrorPageMiddleware_Exception");
+ }
+
+ /// <summary>
+ /// An exception occurred while calculating the database error page content. Skipping display of the database error page.
+ /// </summary>
+ internal static string FormatDatabaseErrorPageMiddleware_Exception()
+ => GetString("DatabaseErrorPageMiddleware_Exception");
+
+ /// <summary>
+ /// &gt; dotnet ef migrations add [migration name]
+ /// </summary>
+ internal static string DatabaseErrorPage_AddMigrationCommandCLI
+ {
+ get => GetString("DatabaseErrorPage_AddMigrationCommandCLI");
+ }
+
+ /// <summary>
+ /// &gt; dotnet ef migrations add [migration name]
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_AddMigrationCommandCLI()
+ => GetString("DatabaseErrorPage_AddMigrationCommandCLI");
+
+ /// <summary>
+ /// Apply Migrations
+ /// </summary>
+ internal static string DatabaseErrorPage_ApplyMigrationsButton
+ {
+ get => GetString("DatabaseErrorPage_ApplyMigrationsButton");
+ }
+
+ /// <summary>
+ /// Apply Migrations
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_ApplyMigrationsButton()
+ => GetString("DatabaseErrorPage_ApplyMigrationsButton");
+
+ /// <summary>
+ /// Migrations Applied
+ /// </summary>
+ internal static string DatabaseErrorPage_ApplyMigrationsButtonDone
+ {
+ get => GetString("DatabaseErrorPage_ApplyMigrationsButtonDone");
+ }
+
+ /// <summary>
+ /// Migrations Applied
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_ApplyMigrationsButtonDone()
+ => GetString("DatabaseErrorPage_ApplyMigrationsButtonDone");
+
+ /// <summary>
+ /// Applying Migrations...
+ /// </summary>
+ internal static string DatabaseErrorPage_ApplyMigrationsButtonRunning
+ {
+ get => GetString("DatabaseErrorPage_ApplyMigrationsButtonRunning");
+ }
+
+ /// <summary>
+ /// Applying Migrations...
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_ApplyMigrationsButtonRunning()
+ => GetString("DatabaseErrorPage_ApplyMigrationsButtonRunning");
+
+ /// <summary>
+ /// An error occurred applying migrations, try applying them from the command line
+ /// </summary>
+ internal static string DatabaseErrorPage_ApplyMigrationsFailed
+ {
+ get => GetString("DatabaseErrorPage_ApplyMigrationsFailed");
+ }
+
+ /// <summary>
+ /// An error occurred applying migrations, try applying them from the command line
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_ApplyMigrationsFailed()
+ => GetString("DatabaseErrorPage_ApplyMigrationsFailed");
+
+ /// <summary>
+ /// In Visual Studio, you can use the Package Manager Console to apply pending migrations to the database:
+ /// </summary>
+ internal static string DatabaseErrorPage_HowToApplyFromPMC
+ {
+ get => GetString("DatabaseErrorPage_HowToApplyFromPMC");
+ }
+
+ /// <summary>
+ /// In Visual Studio, you can use the Package Manager Console to apply pending migrations to the database:
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_HowToApplyFromPMC()
+ => GetString("DatabaseErrorPage_HowToApplyFromPMC");
+
+ /// <summary>
+ /// Try refreshing the page
+ /// </summary>
+ internal static string DatabaseErrorPage_MigrationsAppliedRefresh
+ {
+ get => GetString("DatabaseErrorPage_MigrationsAppliedRefresh");
+ }
+
+ /// <summary>
+ /// Try refreshing the page
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_MigrationsAppliedRefresh()
+ => GetString("DatabaseErrorPage_MigrationsAppliedRefresh");
+
+ /// <summary>
+ /// In Visual Studio, use the Package Manager Console to scaffold a new migration and apply it to the database:
+ /// </summary>
+ internal static string DatabaseErrorPage_NoDbOrMigrationsInfoPMC
+ {
+ get => GetString("DatabaseErrorPage_NoDbOrMigrationsInfoPMC");
+ }
+
+ /// <summary>
+ /// In Visual Studio, use the Package Manager Console to scaffold a new migration and apply it to the database:
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_NoDbOrMigrationsInfoPMC()
+ => GetString("DatabaseErrorPage_NoDbOrMigrationsInfoPMC");
+
+ /// <summary>
+ /// Use migrations to create the database for {0}
+ /// </summary>
+ internal static string DatabaseErrorPage_NoDbOrMigrationsTitle
+ {
+ get => GetString("DatabaseErrorPage_NoDbOrMigrationsTitle");
+ }
+
+ /// <summary>
+ /// Use migrations to create the database for {0}
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_NoDbOrMigrationsTitle(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_NoDbOrMigrationsTitle"), p0);
+
+ /// <summary>
+ /// In Visual Studio, use the Package Manager Console to scaffold a new migration for these changes and apply them to the database:
+ /// </summary>
+ internal static string DatabaseErrorPage_PendingChangesInfoPMC
+ {
+ get => GetString("DatabaseErrorPage_PendingChangesInfoPMC");
+ }
+
+ /// <summary>
+ /// In Visual Studio, use the Package Manager Console to scaffold a new migration for these changes and apply them to the database:
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_PendingChangesInfoPMC()
+ => GetString("DatabaseErrorPage_PendingChangesInfoPMC");
+
+ /// <summary>
+ /// There are pending model changes for {0}
+ /// </summary>
+ internal static string DatabaseErrorPage_PendingChangesTitle
+ {
+ get => GetString("DatabaseErrorPage_PendingChangesTitle");
+ }
+
+ /// <summary>
+ /// There are pending model changes for {0}
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_PendingChangesTitle(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_PendingChangesTitle"), p0);
+
+ /// <summary>
+ /// There are migrations for {0} that have not been applied to the database
+ /// </summary>
+ internal static string DatabaseErrorPage_PendingMigrationsInfo
+ {
+ get => GetString("DatabaseErrorPage_PendingMigrationsInfo");
+ }
+
+ /// <summary>
+ /// There are migrations for {0} that have not been applied to the database
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_PendingMigrationsInfo(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_PendingMigrationsInfo"), p0);
+
+ /// <summary>
+ /// Applying existing migrations for {0} may resolve this issue
+ /// </summary>
+ internal static string DatabaseErrorPage_PendingMigrationsTitle
+ {
+ get => GetString("DatabaseErrorPage_PendingMigrationsTitle");
+ }
+
+ /// <summary>
+ /// Applying existing migrations for {0} may resolve this issue
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_PendingMigrationsTitle(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("DatabaseErrorPage_PendingMigrationsTitle"), p0);
+
+ /// <summary>
+ /// &gt; dotnet ef database update
+ /// </summary>
+ internal static string DatabaseErrorPage_ApplyMigrationsCommandCLI
+ {
+ get => GetString("DatabaseErrorPage_ApplyMigrationsCommandCLI");
+ }
+
+ /// <summary>
+ /// &gt; dotnet ef database update
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_ApplyMigrationsCommandCLI()
+ => GetString("DatabaseErrorPage_ApplyMigrationsCommandCLI");
+
+ /// <summary>
+ /// The context type '{0}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped&lt;{0}&gt;() inside the UseServices(...) call in your application startup code.
+ /// </summary>
+ internal static string MigrationsEndPointMiddleware_ContextNotRegistered
+ {
+ get => GetString("MigrationsEndPointMiddleware_ContextNotRegistered");
+ }
+
+ /// <summary>
+ /// The context type '{0}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped&lt;{0}&gt;() inside the UseServices(...) call in your application startup code.
+ /// </summary>
+ internal static string FormatMigrationsEndPointMiddleware_ContextNotRegistered(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_ContextNotRegistered"), p0);
+
+ /// <summary>
+ /// An error occurred while applying the migrations for '{0}'. See InnerException for details.
+ /// </summary>
+ internal static string MigrationsEndPointMiddleware_Exception
+ {
+ get => GetString("MigrationsEndPointMiddleware_Exception");
+ }
+
+ /// <summary>
+ /// An error occurred while applying the migrations for '{0}'. See InnerException for details.
+ /// </summary>
+ internal static string FormatMigrationsEndPointMiddleware_Exception(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_Exception"), p0);
+
+ /// <summary>
+ /// The context type '{0}' could not be loaded. Ensure this is the correct type name for the context you are trying to apply migrations for.
+ /// </summary>
+ internal static string MigrationsEndPointMiddleware_InvalidContextType
+ {
+ get => GetString("MigrationsEndPointMiddleware_InvalidContextType");
+ }
+
+ /// <summary>
+ /// The context type '{0}' could not be loaded. Ensure this is the correct type name for the context you are trying to apply migrations for.
+ /// </summary>
+ internal static string FormatMigrationsEndPointMiddleware_InvalidContextType(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("MigrationsEndPointMiddleware_InvalidContextType"), p0);
+
+ /// <summary>
+ /// No context type was specified. Ensure the form data from the request includes a contextTypeName value, specifying the context to apply migrations for.
+ /// </summary>
+ internal static string MigrationsEndPointMiddleware_NoContextType
+ {
+ get => GetString("MigrationsEndPointMiddleware_NoContextType");
+ }
+
+ /// <summary>
+ /// No context type was specified. Ensure the form data from the request includes a contextTypeName value, specifying the context to apply migrations for.
+ /// </summary>
+ internal static string FormatMigrationsEndPointMiddleware_NoContextType()
+ => GetString("MigrationsEndPointMiddleware_NoContextType");
+
+ /// <summary>
+ /// A database operation failed while processing the request.
+ /// </summary>
+ internal static string DatabaseErrorPage_Title
+ {
+ get => GetString("DatabaseErrorPage_Title");
+ }
+
+ /// <summary>
+ /// A database operation failed while processing the request.
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_Title()
+ => GetString("DatabaseErrorPage_Title");
+
+ /// <summary>
+ /// Entity Framework did not record any exceptions due to failed database operations. This means the current exception is not a failed Entity Framework database operation, or the current exception occurred from a DbContext that was not obtained from request services.
+ /// </summary>
+ internal static string DatabaseErrorPage_NoRecordedException
+ {
+ get => GetString("DatabaseErrorPage_NoRecordedException");
+ }
+
+ /// <summary>
+ /// Entity Framework did not record any exceptions due to failed database operations. This means the current exception is not a failed Entity Framework database operation, or the current exception occurred from a DbContext that was not obtained from request services.
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_NoRecordedException()
+ => GetString("DatabaseErrorPage_NoRecordedException");
+
+ /// <summary>
+ /// PM&gt; Add-Migration [migration name]
+ /// </summary>
+ internal static string DatabaseErrorPage_AddMigrationCommandPMC
+ {
+ get => GetString("DatabaseErrorPage_AddMigrationCommandPMC");
+ }
+
+ /// <summary>
+ /// PM&gt; Add-Migration [migration name]
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_AddMigrationCommandPMC()
+ => GetString("DatabaseErrorPage_AddMigrationCommandPMC");
+
+ /// <summary>
+ /// PM&gt; Update-Database
+ /// </summary>
+ internal static string DatabaseErrorPage_ApplyMigrationsCommandPMC
+ {
+ get => GetString("DatabaseErrorPage_ApplyMigrationsCommandPMC");
+ }
+
+ /// <summary>
+ /// PM&gt; Update-Database
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_ApplyMigrationsCommandPMC()
+ => GetString("DatabaseErrorPage_ApplyMigrationsCommandPMC");
+
+ /// <summary>
+ /// Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:
+ /// </summary>
+ internal static string DatabaseErrorPage_NoDbOrMigrationsInfoCLI
+ {
+ get => GetString("DatabaseErrorPage_NoDbOrMigrationsInfoCLI");
+ }
+
+ /// <summary>
+ /// Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_NoDbOrMigrationsInfoCLI()
+ => GetString("DatabaseErrorPage_NoDbOrMigrationsInfoCLI");
+
+ /// <summary>
+ /// Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:
+ /// </summary>
+ internal static string DatabaseErrorPage_PendingChangesInfoCLI
+ {
+ get => GetString("DatabaseErrorPage_PendingChangesInfoCLI");
+ }
+
+ /// <summary>
+ /// Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_PendingChangesInfoCLI()
+ => GetString("DatabaseErrorPage_PendingChangesInfoCLI");
+
+ /// <summary>
+ /// Alternatively, you can apply pending migrations from a command prompt at your project directory:
+ /// </summary>
+ internal static string DatabaseErrorPage_HowToApplyFromCLI
+ {
+ get => GetString("DatabaseErrorPage_HowToApplyFromCLI");
+ }
+
+ /// <summary>
+ /// Alternatively, you can apply pending migrations from a command prompt at your project directory:
+ /// </summary>
+ internal static string FormatDatabaseErrorPage_HowToApplyFromCLI()
+ => GetString("DatabaseErrorPage_HowToApplyFromCLI");
+
+ private static string GetString(string name, params string[] formatterNames)
+ {
+ var value = _resourceManager.GetString(name);
+
+ System.Diagnostics.Debug.Assert(value != null);
+
+ if (formatterNames != null)
+ {
+ for (var i = 0; i < formatterNames.Length; i++)
+ {
+ value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
+ }
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Strings.resx b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Strings.resx
new file mode 100644
index 0000000000..3f987c8716
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Strings.resx
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="DatabaseErrorPageMiddleware_ContextNotRegistered" xml:space="preserve">
+ <value>The context type '{0}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped&lt;&gt;() inside the UseServices(...) call in your application startup code. Skipping display of the database error page.</value>
+ </data>
+ <data name="DatabaseErrorPageMiddleware_Exception" xml:space="preserve">
+ <value>An exception occurred while calculating the database error page content. Skipping display of the database error page.</value>
+ </data>
+ <data name="DatabaseErrorPage_AddMigrationCommandCLI" xml:space="preserve">
+ <value>&gt; dotnet ef migrations add [migration name]</value>
+ </data>
+ <data name="DatabaseErrorPage_ApplyMigrationsButton" xml:space="preserve">
+ <value>Apply Migrations</value>
+ </data>
+ <data name="DatabaseErrorPage_ApplyMigrationsButtonDone" xml:space="preserve">
+ <value>Migrations Applied</value>
+ </data>
+ <data name="DatabaseErrorPage_ApplyMigrationsButtonRunning" xml:space="preserve">
+ <value>Applying Migrations...</value>
+ </data>
+ <data name="DatabaseErrorPage_ApplyMigrationsFailed" xml:space="preserve">
+ <value>An error occurred applying migrations, try applying them from the command line</value>
+ </data>
+ <data name="DatabaseErrorPage_HowToApplyFromPMC" xml:space="preserve">
+ <value>In Visual Studio, you can use the Package Manager Console to apply pending migrations to the database:</value>
+ </data>
+ <data name="DatabaseErrorPage_MigrationsAppliedRefresh" xml:space="preserve">
+ <value>Try refreshing the page</value>
+ </data>
+ <data name="DatabaseErrorPage_NoDbOrMigrationsInfoPMC" xml:space="preserve">
+ <value>In Visual Studio, use the Package Manager Console to scaffold a new migration and apply it to the database:</value>
+ </data>
+ <data name="DatabaseErrorPage_NoDbOrMigrationsTitle" xml:space="preserve">
+ <value>Use migrations to create the database for {0}</value>
+ </data>
+ <data name="DatabaseErrorPage_PendingChangesInfoPMC" xml:space="preserve">
+ <value>In Visual Studio, use the Package Manager Console to scaffold a new migration for these changes and apply them to the database:</value>
+ </data>
+ <data name="DatabaseErrorPage_PendingChangesTitle" xml:space="preserve">
+ <value>There are pending model changes for {0}</value>
+ </data>
+ <data name="DatabaseErrorPage_PendingMigrationsInfo" xml:space="preserve">
+ <value>There are migrations for {0} that have not been applied to the database</value>
+ </data>
+ <data name="DatabaseErrorPage_PendingMigrationsTitle" xml:space="preserve">
+ <value>Applying existing migrations for {0} may resolve this issue</value>
+ </data>
+ <data name="DatabaseErrorPage_ApplyMigrationsCommandCLI" xml:space="preserve">
+ <value>&gt; dotnet ef database update</value>
+ </data>
+ <data name="MigrationsEndPointMiddleware_ContextNotRegistered" xml:space="preserve">
+ <value>The context type '{0}' was not found in services. This usually means the context was not registered in services during startup. You probably want to call AddScoped&lt;{0}&gt;() inside the UseServices(...) call in your application startup code.</value>
+ </data>
+ <data name="MigrationsEndPointMiddleware_Exception" xml:space="preserve">
+ <value>An error occurred while applying the migrations for '{0}'. See InnerException for details.</value>
+ </data>
+ <data name="MigrationsEndPointMiddleware_InvalidContextType" xml:space="preserve">
+ <value>The context type '{0}' could not be loaded. Ensure this is the correct type name for the context you are trying to apply migrations for.</value>
+ </data>
+ <data name="MigrationsEndPointMiddleware_NoContextType" xml:space="preserve">
+ <value>No context type was specified. Ensure the form data from the request includes a contextTypeName value, specifying the context to apply migrations for.</value>
+ </data>
+ <data name="DatabaseErrorPage_Title" xml:space="preserve">
+ <value>A database operation failed while processing the request.</value>
+ </data>
+ <data name="DatabaseErrorPage_NoRecordedException" xml:space="preserve">
+ <value>Entity Framework did not record any exceptions due to failed database operations. This means the current exception is not a failed Entity Framework database operation, or the current exception occurred from a DbContext that was not obtained from request services.</value>
+ </data>
+ <data name="DatabaseErrorPage_AddMigrationCommandPMC" xml:space="preserve">
+ <value>PM&gt; Add-Migration [migration name]</value>
+ </data>
+ <data name="DatabaseErrorPage_ApplyMigrationsCommandPMC" xml:space="preserve">
+ <value>PM&gt; Update-Database</value>
+ </data>
+ <data name="DatabaseErrorPage_NoDbOrMigrationsInfoCLI" xml:space="preserve">
+ <value>Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:</value>
+ </data>
+ <data name="DatabaseErrorPage_PendingChangesInfoCLI" xml:space="preserve">
+ <value>Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:</value>
+ </data>
+ <data name="DatabaseErrorPage_HowToApplyFromCLI" xml:space="preserve">
+ <value>Alternatively, you can apply pending migrations from a command prompt at your project directory:</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.Designer.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.Designer.cs
new file mode 100644
index 0000000000..b9a3641004
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.Designer.cs
@@ -0,0 +1,424 @@
+// <auto-generated/>
+#pragma warning disable 1591
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
+{
+ #line hidden
+#line 1 "DatabaseErrorPage.cshtml"
+using System;
+
+#line default
+#line hidden
+ using System.Threading.Tasks;
+#line 2 "DatabaseErrorPage.cshtml"
+using System.Linq;
+
+#line default
+#line hidden
+#line 3 "DatabaseErrorPage.cshtml"
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore;
+
+#line default
+#line hidden
+#line 4 "DatabaseErrorPage.cshtml"
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
+
+#line default
+#line hidden
+ internal class DatabaseErrorPage : Microsoft.Extensions.RazorViews.BaseView
+ {
+ #pragma warning disable 1998
+ public async override global::System.Threading.Tasks.Task ExecuteAsync()
+ {
+#line 5 "DatabaseErrorPage.cshtml"
+
+ Response.StatusCode = 500;
+ Response.ContentType = "text/html; charset=utf-8";
+ Response.ContentLength = null; // Clear any prior Content-Length
+
+#line default
+#line hidden
+ WriteLiteral(@"<!DOCTYPE html>
+
+<html lang=""en"" xmlns=""http://www.w3.org/1999/xhtml"">
+<head>
+ <meta charset=""utf-8"" />
+ <title>Internal Server Error</title>
+ <style>
+ body {
+ font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
+ font-size: .813em;
+ line-height: 1.4em;
+ color: #222;
+}
+
+h1, h2, h3, h4, h5 {
+ font-weight: 100;
+}
+
+h1 {
+ color: #44525e;
+ margin: 15px 0 15px 0;
+}
+
+h2 {
+ margin: 10px 5px 0 0;
+}
+
+h3 {
+ color: #363636;
+ margin: 5px 5px 0 0;
+}
+
+code {
+ font-family: Consolas, ""Courier New"", courier, monospace;
+}
+
+a {
+ color: #1ba1e2;
+ text-decoration: none;
+}
+
+ a:hover {
+ color: #13709e;
+ text-decoration: underline;
+ }
+
+hr {
+ border: 1px #ddd solid;
+}
+
+body .titleerror {
+ padding: 3px;
+}
+
+#applyMigrations {
+ font-size: 14px;
+ background: #44c5f2;
+ color: #ffffff;
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-weight: norma");
+ WriteLiteral(@"l;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ cursor: pointer;
+ border: 1px solid transparent;
+}
+
+ #applyMigrations:disabled {
+ background-color: #a9e4f9;
+ border-color: #44c5f2;
+ }
+
+.error {
+ color: red;
+}
+
+.expanded {
+ display: block;
+}
+
+.collapsed {
+ display: none;
+}
+
+ </style>
+</head>
+<body>
+ <h1>");
+#line 113 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_Title);
+
+#line default
+#line hidden
+ WriteLiteral("</h1>\r\n <p>\r\n");
+#line 115 "DatabaseErrorPage.cshtml"
+ for (Exception ex = Model.Exception; ex != null; ex = ex.InnerException)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <span>");
+#line 117 "DatabaseErrorPage.cshtml"
+ Write(ex.GetType().Name);
+
+#line default
+#line hidden
+ WriteLiteral(": ");
+#line 117 "DatabaseErrorPage.cshtml"
+ Write(ex.Message);
+
+#line default
+#line hidden
+ WriteLiteral("</span>\r\n <br />\r\n");
+#line 119 "DatabaseErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </p>\r\n <hr />\r\n\r\n");
+#line 123 "DatabaseErrorPage.cshtml"
+ if (!Model.DatabaseExists && !Model.PendingMigrations.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <h2>");
+#line 125 "DatabaseErrorPage.cshtml"
+ Write(Strings.FormatDatabaseErrorPage_NoDbOrMigrationsTitle(Model.ContextType.Name));
+
+#line default
+#line hidden
+ WriteLiteral("</h2>\r\n <p>");
+#line 126 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfoPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n <code> ");
+#line 127 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_AddMigrationCommandPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <br />\r\n <code> ");
+#line 129 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <p>");
+#line 130 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_NoDbOrMigrationsInfoCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n <code> ");
+#line 131 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_AddMigrationCommandCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <br />\r\n <code> ");
+#line 133 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <hr />\r\n");
+#line 135 "DatabaseErrorPage.cshtml"
+ }
+ else if (Model.PendingMigrations.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <div>\r\n <h2>");
+#line 139 "DatabaseErrorPage.cshtml"
+ Write(Strings.FormatDatabaseErrorPage_PendingMigrationsTitle(Model.ContextType.Name));
+
+#line default
+#line hidden
+ WriteLiteral("</h2>\r\n <p>");
+#line 140 "DatabaseErrorPage.cshtml"
+ Write(Strings.FormatDatabaseErrorPage_PendingMigrationsInfo(Model.ContextType.Name));
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n\r\n <ul>\r\n");
+#line 143 "DatabaseErrorPage.cshtml"
+ foreach (var migration in Model.PendingMigrations)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li>");
+#line 145 "DatabaseErrorPage.cshtml"
+ Write(migration);
+
+#line default
+#line hidden
+ WriteLiteral("</li>\r\n");
+#line 146 "DatabaseErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ul>\r\n\r\n <p>\r\n <button id=\"applyMigrations\" onclick=\"ApplyMigrations()\">");
+#line 150 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_ApplyMigrationsButton);
+
+#line default
+#line hidden
+ WriteLiteral(@"</button>
+ <span id=""applyMigrationsError"" class=""error""></span>
+ <span id=""applyMigrationsSuccess""></span>
+ </p>
+ <script>
+ function ApplyMigrations() {
+ applyMigrations.disabled = true;
+ applyMigrationsError.innerHTML = """";
+ applyMigrations.innerHTML = """);
+#line 158 "DatabaseErrorPage.cshtml"
+ Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButtonRunning));
+
+#line default
+#line hidden
+ WriteLiteral("\";\r\n\r\n var req = new XMLHttpRequest();\r\n\r\n req.onload = function (e) {\r\n if (req.status === 204) {\r\n applyMigrations.innerHTML = \"");
+#line 164 "DatabaseErrorPage.cshtml"
+ Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButtonDone));
+
+#line default
+#line hidden
+ WriteLiteral("\";\r\n applyMigrationsSuccess.innerHTML = \"");
+#line 165 "DatabaseErrorPage.cshtml"
+ Write(JavaScriptEncode(Strings.DatabaseErrorPage_MigrationsAppliedRefresh));
+
+#line default
+#line hidden
+ WriteLiteral(@""";
+ } else {
+ ErrorApplyingMigrations();
+ }
+ };
+
+ req.onerror = function (e) {
+ ErrorApplyingMigrations();
+ };
+
+ var formBody = ""context=");
+#line 175 "DatabaseErrorPage.cshtml"
+ Write(JavaScriptEncode(UrlEncode(Model.ContextType.AssemblyQualifiedName)));
+
+#line default
+#line hidden
+ WriteLiteral("\";\r\n req.open(\"POST\", \"");
+#line 176 "DatabaseErrorPage.cshtml"
+ Write(JavaScriptEncode(Model.Options.MigrationsEndPointPath.Value));
+
+#line default
+#line hidden
+ WriteLiteral(@""", true);
+ req.setRequestHeader(""Content-type"", ""application/x-www-form-urlencoded"");
+ req.setRequestHeader(""Content-length"", formBody.length);
+ req.setRequestHeader(""Connection"", ""close"");
+ req.send(formBody);
+ }
+
+ function ErrorApplyingMigrations() {
+ applyMigrations.innerHTML = """);
+#line 184 "DatabaseErrorPage.cshtml"
+ Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButton));
+
+#line default
+#line hidden
+ WriteLiteral("\";\r\n applyMigrationsError.innerHTML = \"");
+#line 185 "DatabaseErrorPage.cshtml"
+ Write(JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsFailed));
+
+#line default
+#line hidden
+ WriteLiteral("\";\r\n applyMigrations.disabled = false;\r\n }\r\n </script>\r\n\r\n <p>");
+#line 190 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_HowToApplyFromPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n <code>");
+#line 191 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <p>");
+#line 192 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_HowToApplyFromCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n <code>");
+#line 193 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <hr />\r\n </div>\r\n");
+#line 196 "DatabaseErrorPage.cshtml"
+ }
+ else if (Model.PendingModelChanges)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <div>\r\n <h2>");
+#line 200 "DatabaseErrorPage.cshtml"
+ Write(Strings.FormatDatabaseErrorPage_PendingChangesTitle(Model.ContextType.Name));
+
+#line default
+#line hidden
+ WriteLiteral("</h2>\r\n <p>");
+#line 201 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_PendingChangesInfoPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n <code>");
+#line 202 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_AddMigrationCommandPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <br />\r\n <code>");
+#line 204 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <p>");
+#line 205 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_PendingChangesInfoCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n <code>");
+#line 206 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_AddMigrationCommandCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <br />\r\n <code>");
+#line 208 "DatabaseErrorPage.cshtml"
+ Write(Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI);
+
+#line default
+#line hidden
+ WriteLiteral("</code>\r\n <hr />\r\n </div>\r\n");
+#line 211 "DatabaseErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral("</body>\r\n</html>");
+ }
+ #pragma warning restore 1998
+#line 11 "DatabaseErrorPage.cshtml"
+
+ public DatabaseErrorPageModel Model { get; set; }
+
+ public string UrlEncode(string content)
+ {
+ return UrlEncoder.Encode(content);
+ }
+
+ public string JavaScriptEncode(string content)
+ {
+ return JavaScriptEncoder.Encode(content);
+ }
+
+#line default
+#line hidden
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.cshtml b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.cshtml
new file mode 100644
index 0000000000..70711befb1
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPage.cshtml
@@ -0,0 +1,135 @@
+@using System
+@using System.Linq
+@using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
+@using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
+@{
+ Response.StatusCode = 500;
+ Response.ContentType = "text/html; charset=utf-8";
+ Response.ContentLength = null; // Clear any prior Content-Length
+}
+@functions
+{
+ public DatabaseErrorPageModel Model { get; set; }
+
+ public string UrlEncode(string content)
+ {
+ return UrlEncoder.Encode(content);
+ }
+
+ public string JavaScriptEncode(string content)
+ {
+ return JavaScriptEncoder.Encode(content);
+ }
+}
+<!DOCTYPE html>
+
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta charset="utf-8" />
+ <title>Internal Server Error</title>
+ <style>
+ <%$ include: ErrorPage.css %>
+ </style>
+</head>
+<body>
+ <h1>@Strings.DatabaseErrorPage_Title</h1>
+ <p>
+ @for (Exception ex = Model.Exception; ex != null; ex = ex.InnerException)
+ {
+ <span>@ex.GetType().Name: @ex.Message</span>
+ <br />
+ }
+ </p>
+ <hr />
+
+ @if (!Model.DatabaseExists && !Model.PendingMigrations.Any())
+ {
+ <h2>@Strings.FormatDatabaseErrorPage_NoDbOrMigrationsTitle(Model.ContextType.Name)</h2>
+ <p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfoPMC</p>
+ <code> @Strings.DatabaseErrorPage_AddMigrationCommandPMC</code>
+ <br />
+ <code> @Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
+ <p>@Strings.DatabaseErrorPage_NoDbOrMigrationsInfoCLI</p>
+ <code> @Strings.DatabaseErrorPage_AddMigrationCommandCLI</code>
+ <br />
+ <code> @Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
+ <hr />
+ }
+ else if (Model.PendingMigrations.Any())
+ {
+ <div>
+ <h2>@Strings.FormatDatabaseErrorPage_PendingMigrationsTitle(Model.ContextType.Name)</h2>
+ <p>@Strings.FormatDatabaseErrorPage_PendingMigrationsInfo(Model.ContextType.Name)</p>
+
+ <ul>
+ @foreach (var migration in Model.PendingMigrations)
+ {
+ <li>@migration</li>
+ }
+ </ul>
+
+ <p>
+ <button id="applyMigrations" onclick="ApplyMigrations()">@Strings.DatabaseErrorPage_ApplyMigrationsButton</button>
+ <span id="applyMigrationsError" class="error"></span>
+ <span id="applyMigrationsSuccess"></span>
+ </p>
+ <script>
+ function ApplyMigrations() {
+ applyMigrations.disabled = true;
+ applyMigrationsError.innerHTML = "";
+ applyMigrations.innerHTML = "@JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButtonRunning)";
+
+ var req = new XMLHttpRequest();
+
+ req.onload = function (e) {
+ if (req.status === 204) {
+ applyMigrations.innerHTML = "@JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButtonDone)";
+ applyMigrationsSuccess.innerHTML = "@JavaScriptEncode(Strings.DatabaseErrorPage_MigrationsAppliedRefresh)";
+ } else {
+ ErrorApplyingMigrations();
+ }
+ };
+
+ req.onerror = function (e) {
+ ErrorApplyingMigrations();
+ };
+
+ var formBody = "context=@JavaScriptEncode(UrlEncode(Model.ContextType.AssemblyQualifiedName))";
+ req.open("POST", "@JavaScriptEncode(Model.Options.MigrationsEndPointPath.Value)", true);
+ req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ req.setRequestHeader("Content-length", formBody.length);
+ req.setRequestHeader("Connection", "close");
+ req.send(formBody);
+ }
+
+ function ErrorApplyingMigrations() {
+ applyMigrations.innerHTML = "@JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsButton)";
+ applyMigrationsError.innerHTML = "@JavaScriptEncode(Strings.DatabaseErrorPage_ApplyMigrationsFailed)";
+ applyMigrations.disabled = false;
+ }
+ </script>
+
+ <p>@Strings.DatabaseErrorPage_HowToApplyFromPMC</p>
+ <code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
+ <p>@Strings.DatabaseErrorPage_HowToApplyFromCLI</p>
+ <code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
+ <hr />
+ </div>
+ }
+ else if (Model.PendingModelChanges)
+ {
+ <div>
+ <h2>@Strings.FormatDatabaseErrorPage_PendingChangesTitle(Model.ContextType.Name)</h2>
+ <p>@Strings.DatabaseErrorPage_PendingChangesInfoPMC</p>
+ <code>@Strings.DatabaseErrorPage_AddMigrationCommandPMC</code>
+ <br />
+ <code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandPMC</code>
+ <p>@Strings.DatabaseErrorPage_PendingChangesInfoCLI</p>
+ <code>@Strings.DatabaseErrorPage_AddMigrationCommandCLI</code>
+ <br />
+ <code>@Strings.DatabaseErrorPage_ApplyMigrationsCommandCLI</code>
+ <hr />
+ </div>
+ }
+</body>
+</html> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPageModel.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPageModel.cs
new file mode 100644
index 0000000000..d6520c52ac
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/DatabaseErrorPageModel.cs
@@ -0,0 +1,35 @@
+// 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 Microsoft.AspNetCore.Builder;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views
+{
+ internal class DatabaseErrorPageModel
+ {
+ public DatabaseErrorPageModel(
+ Type contextType,
+ Exception exception,
+ bool databaseExists,
+ bool pendingModelChanges,
+ IEnumerable<string> pendingMigrations,
+ DatabaseErrorPageOptions options)
+ {
+ ContextType = contextType;
+ Exception = exception;
+ DatabaseExists = databaseExists;
+ PendingModelChanges = pendingModelChanges;
+ PendingMigrations = pendingMigrations;
+ Options = options;
+ }
+
+ public virtual Type ContextType { get; }
+ public virtual Exception Exception { get; }
+ public virtual bool DatabaseExists { get; }
+ public virtual bool PendingModelChanges { get; }
+ public virtual IEnumerable<string> PendingMigrations { get; }
+ public virtual DatabaseErrorPageOptions Options { get; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/ErrorPage.css b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/ErrorPage.css
new file mode 100644
index 0000000000..b875f43069
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/Views/ErrorPage.css
@@ -0,0 +1,78 @@
+body {
+ font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
+ font-size: .813em;
+ line-height: 1.4em;
+ color: #222;
+}
+
+h1, h2, h3, h4, h5 {
+ font-weight: 100;
+}
+
+h1 {
+ color: #44525e;
+ margin: 15px 0 15px 0;
+}
+
+h2 {
+ margin: 10px 5px 0 0;
+}
+
+h3 {
+ color: #363636;
+ margin: 5px 5px 0 0;
+}
+
+code {
+ font-family: Consolas, "Courier New", courier, monospace;
+}
+
+a {
+ color: #1ba1e2;
+ text-decoration: none;
+}
+
+ a:hover {
+ color: #13709e;
+ text-decoration: underline;
+ }
+
+hr {
+ border: 1px #ddd solid;
+}
+
+body .titleerror {
+ padding: 3px;
+}
+
+#applyMigrations {
+ font-size: 14px;
+ background: #44c5f2;
+ color: #ffffff;
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-weight: normal;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ cursor: pointer;
+ border: 1px solid transparent;
+}
+
+ #applyMigrations:disabled {
+ background-color: #a9e4f9;
+ border-color: #44c5f2;
+ }
+
+.error {
+ color: red;
+}
+
+.expanded {
+ display: block;
+}
+
+.collapsed {
+ display: none;
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/src/baseline.netcore.json b/src/Middleware/Diagnostics.EntityFrameworkCore/src/baseline.netcore.json
new file mode 100644
index 0000000000..287e144156
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/src/baseline.netcore.json
@@ -0,0 +1,795 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Prefix",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Value",
+ "Parameters": [],
+ "ReturnType": "System.Object",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Literal",
+ "Parameters": [],
+ "ReturnType": "System.Boolean",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "FromTuple",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Tuple<System.String, System.Object, System.Boolean>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Static": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "FromTuple",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Tuple<System.String, System.String, System.Boolean>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Static": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "op_Implicit",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Tuple<System.String, System.Object, System.Boolean>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Static": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "prefix",
+ "Type": "System.String"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ },
+ {
+ "Name": "literal",
+ "Type": "System.Boolean"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.BaseView",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Context",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.HttpContext",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Request",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.HttpRequest",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Response",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.HttpResponse",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Output",
+ "Parameters": [],
+ "ReturnType": "System.IO.StreamWriter",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_HtmlEncoder",
+ "Parameters": [],
+ "ReturnType": "System.Text.Encodings.Web.HtmlEncoder",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_HtmlEncoder",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Text.Encodings.Web.HtmlEncoder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_UrlEncoder",
+ "Parameters": [],
+ "ReturnType": "System.Text.Encodings.Web.UrlEncoder",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_UrlEncoder",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Text.Encodings.Web.UrlEncoder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_JavaScriptEncoder",
+ "Parameters": [],
+ "ReturnType": "System.Text.Encodings.Web.JavaScriptEncoder",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_JavaScriptEncoder",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Text.Encodings.Web.JavaScriptEncoder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "ExecuteAsync",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "ExecuteAsync",
+ "Parameters": [],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Virtual": true,
+ "Abstract": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteral",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteral",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteAttributeValue",
+ "Parameters": [
+ {
+ "Name": "thingy",
+ "Type": "System.String"
+ },
+ {
+ "Name": "startPostion",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ },
+ {
+ "Name": "endValue",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "dealyo",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "yesno",
+ "Type": "System.Boolean"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "BeginWriteAttribute",
+ "Parameters": [
+ {
+ "Name": "name",
+ "Type": "System.String"
+ },
+ {
+ "Name": "begining",
+ "Type": "System.String"
+ },
+ {
+ "Name": "startPosition",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "ending",
+ "Type": "System.String"
+ },
+ {
+ "Name": "endPosition",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "thingy",
+ "Type": "System.Int32"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "EndWriteAttribute",
+ "Parameters": [],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteAttributeTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "name",
+ "Type": "System.String"
+ },
+ {
+ "Name": "leader",
+ "Type": "System.String"
+ },
+ {
+ "Name": "trailer",
+ "Type": "System.String"
+ },
+ {
+ "Name": "values",
+ "Type": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue[]",
+ "IsParams": true
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Write",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Write",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Write",
+ "Parameters": [
+ {
+ "Name": "result",
+ "Type": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.HelperResult"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteralTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteralTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "HtmlEncodeAndReplaceLineBreaks",
+ "Parameters": [
+ {
+ "Name": "input",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.String",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Protected",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.HelperResult",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_WriteAction",
+ "Parameters": [],
+ "ReturnType": "System.Action<System.IO.TextWriter>",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "action",
+ "Type": "System.Action<System.IO.TextWriter>"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "System.IObserver<System.Diagnostics.DiagnosticListener>",
+ "System.IObserver<System.Collections.Generic.KeyValuePair<System.String, System.Object>>"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Invoke",
+ "Parameters": [
+ {
+ "Name": "httpContext",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Virtual": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ },
+ {
+ "Name": "loggerFactory",
+ "Type": "Microsoft.Extensions.Logging.ILoggerFactory"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.DatabaseErrorPageOptions>"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Invoke",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Virtual": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ },
+ {
+ "Name": "logger",
+ "Type": "Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware>"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.MigrationsEndPointOptions>"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.DatabaseErrorPageExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "UseDatabaseErrorPage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseDatabaseErrorPage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.AspNetCore.Builder.DatabaseErrorPageOptions"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.DatabaseErrorPageOptions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_MigrationsEndPointPath",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.PathString",
+ "Virtual": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_MigrationsEndPointPath",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "Microsoft.AspNetCore.Http.PathString"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Virtual": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.MigrationsEndPointExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "UseMigrationsEndPoint",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseMigrationsEndPoint",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.AspNetCore.Builder.MigrationsEndPointOptions"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.MigrationsEndPointOptions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Path",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.PathString",
+ "Virtual": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_Path",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "Microsoft.AspNetCore.Http.PathString"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Virtual": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Field",
+ "Name": "DefaultPath",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.PathString",
+ "Static": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs
new file mode 100644
index 0000000000..bc9764c339
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/DatabaseErrorPageMiddlewareTest.cs
@@ -0,0 +1,526 @@
+// 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.Data.SqlClient;
+using System.Diagnostics;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.AspNetCore.Testing.xunit;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class DatabaseErrorPageMiddlewareTest
+ {
+ [Fact]
+ public async Task Successful_requests_pass_thru()
+ {
+ var builder = new WebHostBuilder().Configure(app => app
+ .UseDatabaseErrorPage()
+ .UseMiddleware<SuccessMiddleware>());
+ var server = new TestServer(builder);
+
+ HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
+
+ Assert.Equal("Request Handled", await response.Content.ReadAsStringAsync());
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+
+ class SuccessMiddleware
+ {
+ public SuccessMiddleware(RequestDelegate next)
+ { }
+
+ public virtual async Task Invoke(HttpContext context)
+ {
+ context.Response.StatusCode = (int)HttpStatusCode.OK;
+ await context.Response.WriteAsync("Request Handled");
+ }
+ }
+
+ [Fact]
+ public async Task Non_database_exceptions_pass_thru()
+ {
+ var builder = new WebHostBuilder().Configure(app => app
+ .UseDatabaseErrorPage()
+ .UseMiddleware<ExceptionMiddleware>());
+ var server = new TestServer(builder);
+
+ var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
+ await server.CreateClient().GetAsync("http://localhost/"));
+
+ Assert.Equal("Exception requested from TestMiddleware", ex.Message);
+ }
+
+ class ExceptionMiddleware
+ {
+ public ExceptionMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ throw new InvalidOperationException("Exception requested from TestMiddleware");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Existing_database_not_using_migrations_exception_passes_thru()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ TestServer server = SetupTestServer<BloggingContext, DatabaseErrorButNoMigrationsMiddleware>(database);
+ var ex = await Assert.ThrowsAsync<DbUpdateException>(async () =>
+ await server.CreateClient().GetAsync("http://localhost/"));
+
+ Assert.Equal("Invalid column name 'Name'.", ex.InnerException.Message);
+ }
+ }
+
+ class DatabaseErrorButNoMigrationsMiddleware
+ {
+ public DatabaseErrorButNoMigrationsMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ var db = context.RequestServices.GetService<BloggingContext>();
+ db.Database.EnsureCreated();
+ db.Database.ExecuteSqlCommand("ALTER TABLE dbo.Blogs DROP COLUMN Name");
+
+ db.Blogs.Add(new Blog());
+ db.SaveChanges();
+ throw new Exception("SaveChanges should have thrown");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Error_page_displayed_no_migrations()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ TestServer server = SetupTestServer<BloggingContext, NoMigrationsMiddleware>(database);
+ HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
+
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", typeof(BloggingContext).Name), content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_AddMigrationCommandPMC").Replace(">", "&gt;"), content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_ApplyMigrationsCommandPMC").Replace(">", "&gt;"), content);
+ }
+ }
+
+ class NoMigrationsMiddleware
+ {
+ public NoMigrationsMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ var db = context.RequestServices.GetService<BloggingContext>();
+ db.Blogs.Add(new Blog());
+ db.SaveChanges();
+ throw new Exception("SaveChanges should have thrown");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public void No_exception_on_diagnostic_event_received_when_null_state()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ using (var server = SetupTestServer<BloggingContext, NoMigrationsMiddleware>(database))
+ {
+ using (var db = server.Host.Services.GetService<BloggingContext>())
+ {
+ db.Blogs.Add(new Blog());
+
+ try
+ {
+ db.SaveChanges();
+ }
+ catch (SqlException)
+ {
+ }
+ }
+ }
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Error_page_displayed_pending_migrations()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ TestServer server = SetupTestServer<BloggingContextWithMigrations, PendingMigrationsMiddleware>(database);
+ HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
+
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingMigrationsTitle", typeof(BloggingContextWithMigrations).Name), content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_ApplyMigrationsCommandPMC").Replace(">", "&gt;"), content);
+ Assert.Contains("<li>111111111111111_MigrationOne</li>", content);
+ Assert.Contains("<li>222222222222222_MigrationTwo</li>", content);
+
+ Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_AddMigrationCommandPMC").Replace(">", "&gt;"), content);
+ }
+ }
+
+ class PendingMigrationsMiddleware
+ {
+ public PendingMigrationsMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ var db = context.RequestServices.GetService<BloggingContextWithMigrations>();
+ db.Blogs.Add(new Blog());
+ db.SaveChanges();
+ throw new Exception("SaveChanges should have thrown");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Error_page_displayed_pending_model_changes()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ TestServer server = SetupTestServer<BloggingContextWithPendingModelChanges, PendingModelChangesMiddleware>(database);
+ HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
+
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingChangesTitle", typeof(BloggingContextWithPendingModelChanges).Name), content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_AddMigrationCommandCLI").Replace(">", "&gt;"), content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_AddMigrationCommandPMC").Replace(">", "&gt;"), content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_ApplyMigrationsCommandCLI").Replace(">", "&gt;"), content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_ApplyMigrationsCommandPMC").Replace(">", "&gt;"), content);
+ }
+ }
+
+ class PendingModelChangesMiddleware
+ {
+ public PendingModelChangesMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ var db = context.RequestServices.GetService<BloggingContextWithPendingModelChanges>();
+ db.Database.Migrate();
+
+ db.Blogs.Add(new Blog());
+ db.SaveChanges();
+ throw new Exception("SaveChanges should have thrown");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Error_page_then_apply_migrations()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ TestServer server = SetupTestServer<BloggingContextWithMigrations, ApplyMigrationsMiddleware>(database);
+ var client = server.CreateClient();
+
+ var expectedMigrationsEndpoint = "/ApplyDatabaseMigrations";
+ var expectedContextType = typeof(BloggingContextWithMigrations).AssemblyQualifiedName;
+
+ // Step One: Initial request with database failure
+ HttpResponseMessage response = await client.GetAsync("http://localhost/");
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+
+ // Ensure the url we're going to test is what the page is using in it's JavaScript
+ var javaScriptEncoder = JavaScriptEncoder.Default;
+ Assert.Contains("req.open(\"POST\", \"" + JavaScriptEncode(expectedMigrationsEndpoint) + "\", true);", content);
+ Assert.Contains("var formBody = \"context=" + JavaScriptEncode(UrlEncode(expectedContextType)) + "\";", content);
+
+ // Step Two: Request to migrations endpoint
+ var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
+ {
+ new KeyValuePair<string, string>("context", expectedContextType)
+ });
+
+ response = await client.PostAsync("http://localhost" + expectedMigrationsEndpoint, formData);
+ content = await response.Content.ReadAsStringAsync();
+ Console.WriteLine(content);
+ Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
+
+ // Step Three: Successful request after migrations applied
+ response = await client.GetAsync("http://localhost/");
+ content = await response.Content.ReadAsStringAsync();
+ Assert.Equal("Saved a Blog", content);
+ }
+ }
+
+ class ApplyMigrationsMiddleware
+ {
+ public ApplyMigrationsMiddleware(RequestDelegate next)
+ { }
+
+ public virtual async Task Invoke(HttpContext context)
+ {
+ var db = context.RequestServices.GetService<BloggingContextWithMigrations>();
+ db.Blogs.Add(new Blog());
+ db.SaveChanges();
+ await context.Response.WriteAsync("Saved a Blog");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Customize_migrations_end_point()
+ {
+ var migrationsEndpoint = "/MyCustomEndPoints/ApplyMyMigrationsHere";
+
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseDatabaseErrorPage(new DatabaseErrorPageOptions
+ {
+ MigrationsEndPointPath = new PathString(migrationsEndpoint)
+ });
+
+ app.UseMiddleware<PendingMigrationsMiddleware>();
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddDbContext<BloggingContextWithMigrations>(optionsBuilder =>
+ {
+ optionsBuilder.UseSqlServer(database.ConnectionString);
+ });
+ });
+ var server = new TestServer(builder);
+
+ HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
+
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Contains("req.open(\"POST\", \"" + JavaScriptEncode(migrationsEndpoint) + "\", true);", content);
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Pass_thru_when_context_not_in_services()
+ {
+ var logProvider = new TestLoggerProvider();
+
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseDatabaseErrorPage();
+ app.UseMiddleware<ContextNotRegisteredInServicesMiddleware>();
+#pragma warning disable CS0618 // Type or member is obsolete
+ app.ApplicationServices.GetService<ILoggerFactory>().AddProvider(logProvider);
+#pragma warning restore CS0618 // Type or member is obsolete
+ });
+ var server = new TestServer(builder);
+
+ var ex = await Assert.ThrowsAsync<SqlException>(async () =>
+ {
+ try
+ {
+ await server.CreateClient().GetAsync("http://localhost/");
+ }
+ catch (InvalidOperationException exception) when (exception.InnerException != null)
+ {
+ throw exception.InnerException;
+ }
+ });
+
+ Assert.Contains(logProvider.Logger.Messages.ToList(), m =>
+ m.StartsWith(StringsHelpers.GetResourceString("FormatDatabaseErrorPageMiddleware_ContextNotRegistered", typeof(BloggingContext))));
+ }
+
+ class ContextNotRegisteredInServicesMiddleware
+ {
+ public ContextNotRegisteredInServicesMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ var optionsBuilder = new DbContextOptionsBuilder()
+ .UseLoggerFactory(context.RequestServices.GetService<ILoggerFactory>());
+ if (!PlatformHelper.IsMono)
+ {
+ optionsBuilder.UseSqlServer(database.ConnectionString, b => b.CommandTimeout(600).EnableRetryOnFailure());
+ }
+ else
+ {
+ optionsBuilder.UseInMemoryDatabase("Scratch");
+ }
+
+ var db = new BloggingContext(optionsBuilder.Options);
+ db.Blogs.Add(new Blog());
+ db.SaveChanges();
+ throw new Exception("SaveChanges should have thrown");
+ }
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Pass_thru_when_exception_in_logic()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ var logProvider = new TestLoggerProvider();
+
+ var server = SetupTestServer<BloggingContextWithSnapshotThatThrows, ExceptionInLogicMiddleware>(database, logProvider);
+
+ var ex = await Assert.ThrowsAsync<SqlException>(async () =>
+ {
+ try
+ {
+ await server.CreateClient().GetAsync("http://localhost/");
+ }
+ catch (InvalidOperationException exception) when (exception.InnerException != null)
+ {
+ throw exception.InnerException;
+ }
+ });
+
+ Assert.Contains(logProvider.Logger.Messages.ToList(), m =>
+ m.StartsWith(StringsHelpers.GetResourceString("FormatDatabaseErrorPageMiddleware_Exception")));
+ }
+ }
+
+ class ExceptionInLogicMiddleware
+ {
+ public ExceptionInLogicMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ var db = context.RequestServices.GetService<BloggingContextWithSnapshotThatThrows>();
+ db.Blogs.Add(new Blog());
+ db.SaveChanges();
+ throw new Exception("SaveChanges should have thrown");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Error_page_displayed_when_exception_wrapped()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ TestServer server = SetupTestServer<BloggingContext, WrappedExceptionMiddleware>(database);
+ HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
+
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Contains("I wrapped your exception", content);
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", typeof(BloggingContext).Name), content);
+ }
+ }
+
+ class WrappedExceptionMiddleware
+ {
+ public WrappedExceptionMiddleware(RequestDelegate next)
+ { }
+
+ public virtual Task Invoke(HttpContext context)
+ {
+ var db = context.RequestServices.GetService<BloggingContext>();
+ db.Blogs.Add(new Blog());
+ try
+ {
+ db.SaveChanges();
+ throw new Exception("SaveChanges should have thrown");
+ }
+ catch (Exception ex)
+ {
+ throw new Exception("I wrapped your exception", ex);
+ }
+ }
+ }
+
+ private static TestServer SetupTestServer<TContext, TMiddleware>(SqlServerTestStore database, ILoggerProvider logProvider = null)
+ where TContext : DbContext
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseDatabaseErrorPage();
+
+ app.UseMiddleware<TMiddleware>();
+
+ if (logProvider != null)
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ app.ApplicationServices.GetService<ILoggerFactory>().AddProvider(logProvider);
+#pragma warning restore CS0618 // Type or member is obsolete
+ }
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddDbContext<TContext>(optionsBuilder =>
+ {
+ if (!PlatformHelper.IsMono)
+ {
+ optionsBuilder.UseSqlServer(
+ database.ConnectionString,
+ b => b.CommandTimeout(600).EnableRetryOnFailure());
+ }
+ else
+ {
+ optionsBuilder.UseInMemoryDatabase("Scratch");
+ }
+ });
+ });
+ return new TestServer(builder);
+ }
+
+ private static UrlEncoder _urlEncoder = UrlEncoder.Default;
+ private static string UrlEncode(string content)
+ {
+ return _urlEncoder.Encode(content);
+ }
+
+ private static JavaScriptEncoder _javaScriptEncoder = JavaScriptEncoder.Default;
+ private static string JavaScriptEncode(string content)
+ {
+ return _javaScriptEncoder.Encode(content);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Diagnostics.EFCore.FunctionalTests.csproj b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Diagnostics.EFCore.FunctionalTests.csproj
new file mode 100644
index 0000000000..a82ac7c490
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Diagnostics.EFCore.FunctionalTests.csproj
@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
+ <!-- Mitigation for long path issues -->
+ <AssemblyName>Diagnostics.EFCore.FunctionalTests</AssemblyName>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
+ <Reference Include="Microsoft.AspNetCore.TestHost" />
+ <Reference Include="Microsoft.EntityFrameworkCore.InMemory" />
+ <Reference Include="Microsoft.EntityFrameworkCore.SqlServer" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/PlatformHelper.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/PlatformHelper.cs
new file mode 100644
index 0000000000..b05aacb836
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/PlatformHelper.cs
@@ -0,0 +1,18 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers
+{
+ public class PlatformHelper
+ {
+ public static bool IsMono
+ {
+ get
+ {
+ return Type.GetType("Mono.Runtime") != null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/StringHelpers.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/StringHelpers.cs
new file mode 100644
index 0000000000..6b4263791a
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/StringHelpers.cs
@@ -0,0 +1,19 @@
+// 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.Linq;
+using System.Reflection;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers
+{
+ public class StringsHelpers
+ {
+ public static string GetResourceString(string stringName, params object[] parameters)
+ {
+ var strings = typeof(DatabaseErrorPageMiddleware).GetTypeInfo().Assembly.GetType("Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Strings").GetTypeInfo();
+ var method = strings.GetDeclaredMethods(stringName).Single();
+ return (string)method.Invoke(null, parameters);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/TestLoggerProvider.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/TestLoggerProvider.cs
new file mode 100644
index 0000000000..901557715a
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/Helpers/TestLoggerProvider.cs
@@ -0,0 +1,61 @@
+// 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 Microsoft.Extensions.Logging;
+using System.Collections.Generic;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers
+{
+ public class TestLoggerProvider : ILoggerProvider
+ {
+ private readonly TestLogger _logger = new TestLogger();
+
+ public TestLogger Logger
+ {
+ get { return _logger; }
+ }
+
+ public ILogger CreateLogger(string name)
+ {
+ return _logger;
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public class TestLogger : ILogger
+ {
+ private List<string> _messages = new List<string>();
+
+ public IEnumerable<string> Messages
+ {
+ get { return _messages; }
+ }
+
+ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
+ {
+ _messages.Add(formatter(state, exception));
+ }
+
+ public bool IsEnabled(LogLevel logLevel)
+ {
+ return true;
+ }
+
+ public IDisposable BeginScope<TState>(TState state)
+ {
+ return NullScope.Instance;
+ }
+
+ public class NullScope : IDisposable
+ {
+ public static NullScope Instance = new NullScope();
+
+ public void Dispose()
+ { }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/MigrationsEndPointMiddlewareTest.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/MigrationsEndPointMiddlewareTest.cs
new file mode 100644
index 0000000000..d02f80a93b
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/MigrationsEndPointMiddlewareTest.cs
@@ -0,0 +1,224 @@
+// Copyright (c) Microsoft Open Technologies, Inc. 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.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.AspNetCore.Testing.xunit;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class MigrationsEndPointMiddlewareTest
+ {
+ [Fact]
+ public async Task Non_migration_requests_pass_thru()
+ {
+ var builder = new WebHostBuilder().Configure(app => app
+ .UseMigrationsEndPoint()
+ .UseMiddleware<SuccessMiddleware>());
+ var server = new TestServer(builder);
+
+ HttpResponseMessage response = await server.CreateClient().GetAsync("http://localhost/");
+
+ Assert.Equal("Request Handled", await response.Content.ReadAsStringAsync());
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+
+ class SuccessMiddleware
+ {
+ public SuccessMiddleware(RequestDelegate next)
+ { }
+
+ public virtual async Task Invoke(HttpContext context)
+ {
+ context.Response.StatusCode = (int)HttpStatusCode.OK;
+ await context.Response.WriteAsync("Request Handled");
+ }
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Migration_request_default_path()
+ {
+ await Migration_request(useCustomPath: false);
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Migration_request_custom_path()
+ {
+ await Migration_request(useCustomPath: true);
+ }
+
+ private async Task Migration_request(bool useCustomPath)
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ var optionsBuilder = new DbContextOptionsBuilder();
+ optionsBuilder.UseSqlServer(database.ConnectionString);
+
+ var path = useCustomPath ? new PathString("/EndPoints/ApplyMyMigrations") : MigrationsEndPointOptions.DefaultPath;
+
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ if (useCustomPath)
+ {
+ app.UseMigrationsEndPoint(new MigrationsEndPointOptions
+ {
+ Path = path
+ });
+ }
+ else
+ {
+ app.UseMigrationsEndPoint();
+ }
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddDbContext<BloggingContextWithMigrations>(options =>
+ {
+ options.UseSqlServer(database.ConnectionString);
+ });
+ });
+ var server = new TestServer(builder);
+
+ using (var db = BloggingContextWithMigrations.CreateWithoutExternalServiceProvider(optionsBuilder.Options))
+ {
+ var databaseCreator = db.GetService<IRelationalDatabaseCreator>();
+ Assert.False(databaseCreator.Exists());
+
+ var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
+ {
+ new KeyValuePair<string, string>("context", typeof(BloggingContextWithMigrations).AssemblyQualifiedName)
+ });
+
+ HttpResponseMessage response = await server.CreateClient()
+ .PostAsync("http://localhost" + path, formData);
+
+ Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
+
+ Assert.True(databaseCreator.Exists());
+
+ var historyRepository = db.GetService<IHistoryRepository>();
+ var appliedMigrations = historyRepository.GetAppliedMigrations();
+ Assert.Equal(2, appliedMigrations.Count);
+ Assert.Equal("111111111111111_MigrationOne", appliedMigrations.ElementAt(0).MigrationId);
+ Assert.Equal("222222222222222_MigrationTwo", appliedMigrations.ElementAt(1).MigrationId);
+ }
+ }
+ }
+
+ [Fact]
+ public async Task Context_type_not_specified()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseMigrationsEndPoint();
+ });
+ var server = new TestServer(builder);
+
+ var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>());
+
+ var response = await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData);
+ var content = await response.Content.ReadAsStringAsync();
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_NoContextType"), content);
+ Assert.True(content.Length > 512);
+ }
+
+ [Fact]
+ public async Task Invalid_context_type_specified()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseMigrationsEndPoint();
+ });
+ var server = new TestServer(builder);
+
+ var typeName = "You won't find this type ;)";
+ var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
+ {
+ new KeyValuePair<string, string>("context", typeName)
+ });
+
+ var response = await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData);
+ var content = await response.Content.ReadAsStringAsync();
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_InvalidContextType", typeName), content);
+ Assert.True(content.Length > 512);
+ }
+
+ [Fact]
+ public async Task Context_not_registered_in_services()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app => app.UseMigrationsEndPoint())
+ .ConfigureServices(services => services.AddEntityFrameworkSqlServer());
+ var server = new TestServer(builder);
+
+ var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
+ {
+ new KeyValuePair<string, string>("context", typeof(BloggingContext).AssemblyQualifiedName)
+ });
+
+ var response = await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData);
+ var content = await response.Content.ReadAsStringAsync();
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_ContextNotRegistered", typeof(BloggingContext)), content);
+ Assert.True(content.Length > 512);
+ }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task Exception_while_applying_migrations()
+ {
+ using (var database = SqlServerTestStore.CreateScratch())
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app => app.UseMigrationsEndPoint())
+ .ConfigureServices(services =>
+ {
+ services.AddDbContext<BloggingContextWithSnapshotThatThrows>(optionsBuilder =>
+ {
+ optionsBuilder.UseSqlServer(database.ConnectionString);
+ });
+ });
+ var server = new TestServer(builder);
+
+ var formData = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
+ {
+ new KeyValuePair<string, string>("context", typeof(BloggingContextWithSnapshotThatThrows).AssemblyQualifiedName)
+ });
+
+ var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () =>
+ await server.CreateClient().PostAsync("http://localhost" + MigrationsEndPointOptions.DefaultPath, formData));
+
+ Assert.StartsWith(StringsHelpers.GetResourceString("FormatMigrationsEndPointMiddleware_Exception", typeof(BloggingContextWithSnapshotThatThrows)), ex.Message);
+ Assert.Equal("Welcome to the invalid migration!", ex.InnerException.Message);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs
new file mode 100644
index 0000000000..cfdfcf8d07
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/SqlServerTestStore.cs
@@ -0,0 +1,60 @@
+// 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.Data.SqlClient;
+using System.Threading;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Helpers;
+using Microsoft.EntityFrameworkCore;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class SqlServerTestStore : IDisposable
+ {
+ private static int _scratchCount;
+
+ public static SqlServerTestStore CreateScratch()
+ {
+ var name = "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.FunctionalTests.Scratch_" + Interlocked.Increment(ref _scratchCount);
+ var db = new SqlServerTestStore(name);
+ return db;
+ }
+
+ private readonly string _connectionString;
+
+ private SqlServerTestStore(string name)
+ {
+ _connectionString = new SqlConnectionStringBuilder
+ {
+ DataSource = @"(localdb)\MSSQLLocalDB",
+ InitialCatalog = name,
+ IntegratedSecurity = true,
+ ConnectTimeout = 600
+ }.ConnectionString;
+ }
+
+ public string ConnectionString
+ {
+ get { return _connectionString; }
+ }
+
+ private void EnsureDeleted()
+ {
+ if (!PlatformHelper.IsMono)
+ {
+ var optionsBuilder = new DbContextOptionsBuilder();
+ optionsBuilder.UseSqlServer(_connectionString, b => b.CommandTimeout(600).EnableRetryOnFailure());
+
+ using (var db = new DbContext(optionsBuilder.Options))
+ {
+ db.Database.EnsureDeleted();
+ }
+ }
+ }
+
+ public virtual void Dispose()
+ {
+ EnsureDeleted();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/Blog.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/Blog.cs
new file mode 100644
index 0000000000..3861146450
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/Blog.cs
@@ -0,0 +1,13 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class Blog
+ {
+ public int BlogId { get; set; }
+ public string Name { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContext.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContext.cs
new file mode 100644
index 0000000000..cab6af6b93
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContext.cs
@@ -0,0 +1,18 @@
+// 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 Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class BloggingContext : DbContext
+ {
+ public BloggingContext(DbContextOptions options)
+ : base(options)
+ { }
+
+ public DbSet<Blog> Blogs { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithMigrations.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithMigrations.cs
new file mode 100644
index 0000000000..90e38ed1c0
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithMigrations.cs
@@ -0,0 +1,71 @@
+// 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 Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class BloggingContextWithMigrations : BloggingContext
+ {
+ public BloggingContextWithMigrations(DbContextOptions options)
+ : base(options)
+ { }
+
+ // Providing a factory method so that the ctor is hidden from DI
+ public static BloggingContextWithMigrations CreateWithoutExternalServiceProvider(DbContextOptions options)
+ {
+ return new BloggingContextWithMigrations(options);
+ }
+
+ [DbContext(typeof(BloggingContextWithMigrations))]
+ public class BloggingContextWithMigrationsModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder builder)
+ {
+ builder.Entity("Blogging.Models.Blog", b =>
+ {
+ b.Property<int>("BlogId");
+ b.Property<string>("Name");
+ b.HasKey("BlogId");
+ });
+ }
+ }
+
+ [DbContext(typeof(BloggingContextWithMigrations))]
+ [Migration("111111111111111_MigrationOne")]
+ public class MigrationOne : Migration
+ {
+ public override IModel TargetModel => new BloggingContextWithMigrationsModelSnapshot().Model;
+
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable("Blogs",
+ c => new
+ {
+ BlogId = c.Column<int>().Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
+ Name = c.Column<string>(nullable: true),
+ })
+ .PrimaryKey("PK_Blog", t => t.BlogId);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable("Blogs");
+ }
+ }
+
+ [DbContext(typeof(BloggingContextWithMigrations))]
+ [Migration("222222222222222_MigrationTwo")]
+ public class MigrationTwo : Migration
+ {
+ public override IModel TargetModel => new BloggingContextWithMigrationsModelSnapshot().Model;
+
+ protected override void Up(MigrationBuilder migrationBuilder)
+ { }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithPendingModelChanges.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithPendingModelChanges.cs
new file mode 100644
index 0000000000..b2cc986039
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithPendingModelChanges.cs
@@ -0,0 +1,33 @@
+// 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 Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class BloggingContextWithPendingModelChanges : BloggingContext
+ {
+ public BloggingContextWithPendingModelChanges(DbContextOptions options)
+ : base(options)
+ { }
+
+ [DbContext(typeof(BloggingContextWithPendingModelChanges))]
+ public class BloggingModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+ }
+ }
+
+ [DbContext(typeof(BloggingContextWithPendingModelChanges))]
+ [Migration("111111111111111_MigrationOne")]
+ public partial class MigrationOne : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ { }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithSnapshotThatThrows.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithSnapshotThatThrows.cs
new file mode 100644
index 0000000000..fb96dfa523
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/FunctionalTests/TestModels/BloggingContextWithSnapshotThatThrows.cs
@@ -0,0 +1,39 @@
+// 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 Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class BloggingContextWithSnapshotThatThrows : BloggingContext
+ {
+ public BloggingContextWithSnapshotThatThrows(DbContextOptions options)
+ : base(options)
+ { }
+
+ [DbContext(typeof(BloggingContextWithSnapshotThatThrows))]
+ public class BloggingContextWithSnapshotThatThrowsModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+ throw new Exception("Welcome to the invalid snapshot!");
+ }
+ }
+
+ [DbContext(typeof(BloggingContextWithSnapshotThatThrows))]
+ [Migration("111111111111111_MigrationOne")]
+ public class MigrationOne : Migration
+ {
+ public override IModel TargetModel => new BloggingContextWithSnapshotThatThrowsModelSnapshot().Model;
+
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ throw new Exception("Welcome to the invalid migration!");
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageOptionsTest.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageOptionsTest.cs
new file mode 100644
index 0000000000..842c5f14a7
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageOptionsTest.cs
@@ -0,0 +1,28 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class DatabaseErrorPageOptionsTest
+ {
+ [Fact]
+ public void Empty_MigrationsEndPointPath_by_default()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ Assert.Equal(MigrationsEndPointOptions.DefaultPath, options.MigrationsEndPointPath);
+ }
+
+ [Fact]
+ public void MigrationsEndPointPath_is_respected()
+ {
+ var options = new DatabaseErrorPageOptions();
+ options.MigrationsEndPointPath = "/test";
+
+ Assert.Equal("/test", options.MigrationsEndPointPath);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageTest.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageTest.cs
new file mode 100644
index 0000000000..46ca2f2a96
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/DatabaseErrorPageTest.cs
@@ -0,0 +1,199 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.Helpers;
+using Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Views;
+using Microsoft.AspNetCore.Http;
+using Microsoft.EntityFrameworkCore;
+using Moq;
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests
+{
+ public class DatabaseErrorPageTest
+ {
+ [Fact]
+ public async Task No_database_or_migrations_only_displays_scaffold_first_migration()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception(),
+ databaseExists: false,
+ pendingModelChanges: false,
+ pendingMigrations: new string[] { },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ AssertHelpers.DisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
+ AssertHelpers.NotDisplaysApplyMigrations(typeof(BloggingContext), content);
+ AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
+ }
+
+ [Fact]
+ public async Task No_database_with_migrations_only_displays_apply_migrations()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception(),
+ databaseExists: false,
+ pendingModelChanges: false,
+ pendingMigrations: new[] { "111_MigrationOne" },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
+ AssertHelpers.DisplaysApplyMigrations(typeof(BloggingContext), content);
+ AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
+ }
+
+ [Fact]
+ public async Task Existing_database_with_migrations_only_displays_apply_migrations()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception(),
+ databaseExists: true,
+ pendingModelChanges: false,
+ pendingMigrations: new[] { "111_MigrationOne" },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
+ AssertHelpers.DisplaysApplyMigrations(typeof(BloggingContext), content);
+ AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
+ }
+
+ [Fact]
+ public async Task Existing_database_with_migrations_and_pending_model_changes_only_displays_apply_migrations()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception(),
+ databaseExists: true,
+ pendingModelChanges: true,
+ pendingMigrations: new[] { "111_MigrationOne" },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
+ AssertHelpers.DisplaysApplyMigrations(typeof(BloggingContext), content);
+ AssertHelpers.NotDisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
+ }
+
+ [Fact]
+ public async Task Pending_model_changes_only_displays_scaffold_next_migration()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception(),
+ databaseExists: true,
+ pendingModelChanges: true,
+ pendingMigrations: new string[] { },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ AssertHelpers.NotDisplaysScaffoldFirstMigration(typeof(BloggingContext), content);
+ AssertHelpers.NotDisplaysApplyMigrations(typeof(BloggingContext), content);
+ AssertHelpers.DisplaysScaffoldNextMigraion(typeof(BloggingContext), content);
+ }
+
+ [Fact]
+ public async Task Exception_details_are_displayed()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception("Something bad happened"),
+ databaseExists: false,
+ pendingModelChanges: false,
+ pendingMigrations: new string[] { },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ Assert.Contains("Something bad happened", content);
+ }
+
+ [Fact]
+ public async Task Inner_exception_details_are_displayed()
+ {
+ var options = new DatabaseErrorPageOptions();
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception("Something bad happened", new Exception("Because something more badder happened")),
+ databaseExists: false,
+ pendingModelChanges: false,
+ pendingMigrations: new string[] { },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ Assert.Contains("Something bad happened", content);
+ Assert.Contains("Because something more badder happened", content);
+ }
+
+ [Fact]
+ public async Task MigrationsEndPointPath_is_respected()
+ {
+ var options = new DatabaseErrorPageOptions();
+ options.MigrationsEndPointPath = "/HitThisEndPoint";
+
+ var model = new DatabaseErrorPageModel(
+ contextType: typeof(BloggingContext),
+ exception: new Exception(),
+ databaseExists: true,
+ pendingModelChanges: false,
+ pendingMigrations: new[] { "111_MigrationOne" },
+ options: options);
+
+ var content = await ExecutePage(options, model);
+
+ Assert.Contains(options.MigrationsEndPointPath.Value, content);
+ }
+
+
+ private static async Task<string> ExecutePage(DatabaseErrorPageOptions options, DatabaseErrorPageModel model)
+ {
+ var page = new DatabaseErrorPage();
+ var context = new Mock<HttpContext>();
+ var response = new Mock<HttpResponse>();
+ var stream = new MemoryStream();
+
+ response.Setup(r => r.Body).Returns(stream);
+ context.Setup(c => c.Response).Returns(response.Object);
+
+ page.Model = model;
+
+ await page.ExecuteAsync(context.Object);
+ var content = Encoding.ASCII.GetString(stream.ToArray());
+ return content;
+ }
+
+ private class BloggingContext : DbContext
+ {
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/AssertHelpers.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/AssertHelpers.cs
new file mode 100644
index 0000000000..80d8b45e18
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/AssertHelpers.cs
@@ -0,0 +1,41 @@
+// 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 Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.Helpers
+{
+ public static class AssertHelpers
+ {
+ public static void DisplaysScaffoldFirstMigration(Type contextType, string content)
+ {
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", contextType.Name), content);
+ }
+
+ public static void NotDisplaysScaffoldFirstMigration(Type contextType, string content)
+ {
+ Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_NoDbOrMigrationsTitle", contextType.Name), content);
+ }
+
+ public static void DisplaysApplyMigrations(Type contextType, string content)
+ {
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingMigrationsTitle", contextType.Name), content);
+ }
+
+ public static void NotDisplaysApplyMigrations(Type contextType, string content)
+ {
+ Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingMigrationsTitle", contextType.Name), content);
+ }
+
+ public static void DisplaysScaffoldNextMigraion(Type contextType, string content)
+ {
+ Assert.Contains(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingChangesTitle", contextType.Name), content);
+ }
+
+ public static void NotDisplaysScaffoldNextMigraion(Type contextType, string content)
+ {
+ Assert.DoesNotContain(StringsHelpers.GetResourceString("FormatDatabaseErrorPage_PendingChangesTitle", contextType.Name), content);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/StringHelpers.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/StringHelpers.cs
new file mode 100644
index 0000000000..68d7d3392e
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Helpers/StringHelpers.cs
@@ -0,0 +1,19 @@
+// 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.Linq;
+using System.Reflection;
+
+namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.Helpers
+{
+ public class StringsHelpers
+ {
+ public static string GetResourceString(string stringName, params object[] parameters)
+ {
+ var strings = typeof(DatabaseErrorPageMiddleware).GetTypeInfo().Assembly.GetType("Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Strings").GetTypeInfo();
+ var method = strings.GetDeclaredMethods(stringName).Single();
+ return (string)method.Invoke(null, parameters);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.csproj b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.csproj
new file mode 100644
index 0000000000..fc5a2b350f
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
+ <Reference Include="Microsoft.EntityFrameworkCore.InMemory" />
+ </ItemGroup>
+
+
+</Project>
diff --git a/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/TestHelperExtensions.cs b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/TestHelperExtensions.cs
new file mode 100644
index 0000000000..e87b4ef99d
--- /dev/null
+++ b/src/Middleware/Diagnostics.EntityFrameworkCore/test/UnitTests/TestHelperExtensions.cs
@@ -0,0 +1,21 @@
+// 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 Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.EntityFrameworkCore.Tests
+{
+ public static class TestHelperExtensions
+ {
+ public static IServiceCollection AddProviderServices(this IServiceCollection serviceCollection)
+ {
+ return serviceCollection.AddEntityFrameworkInMemoryDatabase();
+ }
+
+ public static DbContextOptions UseProviderOptions(this DbContextOptions options)
+ {
+ return options;
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/.csslintrc b/src/Middleware/Diagnostics/src/.csslintrc
new file mode 100644
index 0000000000..fda9775958
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/.csslintrc
@@ -0,0 +1,7 @@
+{
+ "unique-headings": false,
+ "ids": false,
+ "box-sizing": false,
+ "qualified-headings": false,
+ "display-property-grouping": false
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/.jshintrc b/src/Middleware/Diagnostics/src/.jshintrc
new file mode 100644
index 0000000000..077404aaa4
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/.jshintrc
@@ -0,0 +1,3 @@
+{
+
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageExtensions.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageExtensions.cs
new file mode 100644
index 0000000000..6ef0f6dfe1
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageExtensions.cs
@@ -0,0 +1,53 @@
+// 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 Microsoft.AspNetCore.Diagnostics;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// <see cref="IApplicationBuilder"/> extension methods for the <see cref="DeveloperExceptionPageMiddleware"/>.
+ /// </summary>
+ public static class DeveloperExceptionPageExtensions
+ {
+ /// <summary>
+ /// Captures synchronous and asynchronous <see cref="Exception"/> instances from the pipeline and generates HTML error responses.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ public static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseMiddleware<DeveloperExceptionPageMiddleware>();
+ }
+
+ /// <summary>
+ /// Captures synchronous and asynchronous <see cref="Exception"/> instances from the pipeline and generates HTML error responses.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <param name="options">A <see cref="DeveloperExceptionPageOptions"/> that specifies options for the middleware.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ public static IApplicationBuilder UseDeveloperExceptionPage(
+ this IApplicationBuilder app,
+ DeveloperExceptionPageOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ return app.UseMiddleware<DeveloperExceptionPageMiddleware>(Options.Create(options));
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
new file mode 100644
index 0000000000..c58c53ce8b
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
@@ -0,0 +1,210 @@
+// 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.Diagnostics;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.Internal;
+using Microsoft.AspNetCore.Diagnostics.RazorViews;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.Extensions.StackTrace.Sources;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// Captures synchronous and asynchronous exceptions from the pipeline and generates HTML error responses.
+ /// </summary>
+ public class DeveloperExceptionPageMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly DeveloperExceptionPageOptions _options;
+ private readonly ILogger _logger;
+ private readonly IFileProvider _fileProvider;
+ private readonly DiagnosticSource _diagnosticSource;
+ private readonly ExceptionDetailsProvider _exceptionDetailsProvider;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DeveloperExceptionPageMiddleware"/> class
+ /// </summary>
+ /// <param name="next"></param>
+ /// <param name="options"></param>
+ /// <param name="loggerFactory"></param>
+ /// <param name="hostingEnvironment"></param>
+ /// <param name="diagnosticSource"></param>
+ public DeveloperExceptionPageMiddleware(
+ RequestDelegate next,
+ IOptions<DeveloperExceptionPageOptions> options,
+ ILoggerFactory loggerFactory,
+ IHostingEnvironment hostingEnvironment,
+ DiagnosticSource diagnosticSource)
+ {
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ _next = next;
+ _options = options.Value;
+ _logger = loggerFactory.CreateLogger<DeveloperExceptionPageMiddleware>();
+ _fileProvider = _options.FileProvider ?? hostingEnvironment.ContentRootFileProvider;
+ _diagnosticSource = diagnosticSource;
+ _exceptionDetailsProvider = new ExceptionDetailsProvider(_fileProvider, _options.SourceCodeLineCount);
+ }
+
+ /// <summary>
+ /// Process an individual request.
+ /// </summary>
+ /// <param name="context"></param>
+ /// <returns></returns>
+ public async Task Invoke(HttpContext context)
+ {
+ try
+ {
+ await _next(context);
+ }
+ catch (Exception ex)
+ {
+ _logger.UnhandledException(ex);
+
+ if (context.Response.HasStarted)
+ {
+ _logger.ResponseStartedErrorPageMiddleware();
+ throw;
+ }
+
+ try
+ {
+ context.Response.Clear();
+ context.Response.StatusCode = 500;
+
+ await DisplayException(context, ex);
+
+ if (_diagnosticSource.IsEnabled("Microsoft.AspNetCore.Diagnostics.UnhandledException"))
+ {
+ _diagnosticSource.Write("Microsoft.AspNetCore.Diagnostics.UnhandledException", new { httpContext = context, exception = ex });
+ }
+
+ return;
+ }
+ catch (Exception ex2)
+ {
+ // If there's a Exception while generating the error page, re-throw the original exception.
+ _logger.DisplayErrorPageException(ex2);
+ }
+ throw;
+ }
+ }
+
+ // Assumes the response headers have not been sent. If they have, still attempt to write to the body.
+ private Task DisplayException(HttpContext context, Exception ex)
+ {
+ var compilationException = ex as ICompilationException;
+ if (compilationException != null)
+ {
+ return DisplayCompilationException(context, compilationException);
+ }
+
+ return DisplayRuntimeException(context, ex);
+ }
+
+ private Task DisplayCompilationException(
+ HttpContext context,
+ ICompilationException compilationException)
+ {
+ var model = new CompilationErrorPageModel
+ {
+ Options = _options,
+ };
+
+ var errorPage = new CompilationErrorPage
+ {
+ Model = model
+ };
+
+ if (compilationException.CompilationFailures == null)
+ {
+ return errorPage.ExecuteAsync(context);
+ }
+
+ foreach (var compilationFailure in compilationException.CompilationFailures)
+ {
+ if (compilationFailure == null)
+ {
+ continue;
+ }
+
+ var stackFrames = new List<StackFrameSourceCodeInfo>();
+ var exceptionDetails = new ExceptionDetails
+ {
+ StackFrames = stackFrames,
+ ErrorMessage = compilationFailure.FailureSummary,
+ };
+ model.ErrorDetails.Add(exceptionDetails);
+ model.CompiledContent.Add(compilationFailure.CompiledContent);
+
+ if (compilationFailure.Messages == null)
+ {
+ continue;
+ }
+
+ var sourceLines = compilationFailure
+ .SourceFileContent?
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.None);
+
+ foreach (var item in compilationFailure.Messages)
+ {
+ if (item == null)
+ {
+ continue;
+ }
+
+ var frame = new StackFrameSourceCodeInfo
+ {
+ File = compilationFailure.SourceFilePath,
+ Line = item.StartLine,
+ Function = string.Empty
+ };
+
+ if (sourceLines != null)
+ {
+ _exceptionDetailsProvider.ReadFrameContent(frame, sourceLines, item.StartLine, item.EndLine);
+ }
+
+ frame.ErrorDetails = item.Message;
+
+ stackFrames.Add(frame);
+ }
+ }
+
+ return errorPage.ExecuteAsync(context);
+ }
+
+ private Task DisplayRuntimeException(HttpContext context, Exception ex)
+ {
+ var request = context.Request;
+
+ var model = new ErrorPageModel
+ {
+ Options = _options,
+ ErrorDetails = _exceptionDetailsProvider.GetDetails(ex),
+ Query = request.Query,
+ Cookies = request.Cookies,
+ Headers = request.Headers
+ };
+
+ var errorPage = new ErrorPage(model);
+ return errorPage.ExecuteAsync(context);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageOptions.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageOptions.cs
new file mode 100644
index 0000000000..2562dbc3b8
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageOptions.cs
@@ -0,0 +1,37 @@
+// 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 Microsoft.AspNetCore.Diagnostics;
+using Microsoft.Extensions.FileProviders;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// Options for the <see cref="DeveloperExceptionPageMiddleware"/>.
+ /// </summary>
+ public class DeveloperExceptionPageOptions
+ {
+ /// <summary>
+ /// Create an instance with the default options settings.
+ /// </summary>
+ public DeveloperExceptionPageOptions()
+ {
+ SourceCodeLineCount = 6;
+ }
+
+ /// <summary>
+ /// Determines how many lines of code to include before and after the line of code
+ /// present in an exception's stack frame. Only applies when symbols are available and
+ /// source code referenced by the exception stack trace is present on the server.
+ /// </summary>
+ public int SourceCodeLineCount { get; set; }
+
+ /// <summary>
+ /// Provides files containing source code used to display contextual information of an exception.
+ /// </summary>
+ /// <remarks>
+ /// If <c>null</c> <see cref="DeveloperExceptionPageMiddleware" /> will use a <see cref="PhysicalFileProvider"/>.
+ /// </remarks>
+ public IFileProvider FileProvider { get; set; }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorModel.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorModel.cs
new file mode 100644
index 0000000000..f84bb4f19d
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorModel.cs
@@ -0,0 +1,30 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.StackTrace.Sources;
+
+namespace Microsoft.AspNetCore.Diagnostics.RazorViews
+{
+ /// <summary>
+ /// Holds data to be displayed on the compilation error page.
+ /// </summary>
+ internal class CompilationErrorPageModel
+ {
+ /// <summary>
+ /// Options for what output to display.
+ /// </summary>
+ public DeveloperExceptionPageOptions Options { get; set; }
+
+ /// <summary>
+ /// Detailed information about each parse or compilation error.
+ /// </summary>
+ public IList<ExceptionDetails> ErrorDetails { get; } = new List<ExceptionDetails>();
+
+ /// <summary>
+ /// Gets the generated content that produced the corresponding <see cref="ErrorDetails"/>.
+ /// </summary>
+ public IList<string> CompiledContent { get; } = new List<string>();
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.Designer.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.Designer.cs
new file mode 100644
index 0000000000..d5342bf79a
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.Designer.cs
@@ -0,0 +1,751 @@
+// <auto-generated/>
+#pragma warning disable 1591
+namespace Microsoft.AspNetCore.Diagnostics.RazorViews
+{
+ #line hidden
+#line 1 "CompilationErrorPage.cshtml"
+using System;
+
+#line default
+#line hidden
+ using System.Threading.Tasks;
+#line 2 "CompilationErrorPage.cshtml"
+using System.Globalization;
+
+#line default
+#line hidden
+#line 3 "CompilationErrorPage.cshtml"
+using System.Linq;
+
+#line default
+#line hidden
+#line 4 "CompilationErrorPage.cshtml"
+using System.Net;
+
+#line default
+#line hidden
+#line 5 "CompilationErrorPage.cshtml"
+using Microsoft.AspNetCore.Diagnostics;
+
+#line default
+#line hidden
+#line 6 "CompilationErrorPage.cshtml"
+using Microsoft.AspNetCore.Diagnostics.RazorViews;
+
+#line default
+#line hidden
+ internal class CompilationErrorPage : Microsoft.Extensions.RazorViews.BaseView
+ {
+ #pragma warning disable 1998
+ public async override global::System.Threading.Tasks.Task ExecuteAsync()
+ {
+#line 11 "CompilationErrorPage.cshtml"
+
+ Response.StatusCode = 500;
+ Response.ContentType = "text/html; charset=utf-8";
+ Response.ContentLength = null; // Clear any prior Content-Length
+
+#line default
+#line hidden
+ WriteLiteral("<!DOCTYPE html>\r\n<html>\r\n <head>\r\n <meta charset=\"utf-8\" />\r\n <title>");
+#line 20 "CompilationErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_Title);
+
+#line default
+#line hidden
+ WriteLiteral(@"</title>
+ <style>
+ body {
+ font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
+ font-size: .813em;
+ color: #222;
+ background-color: #fff;
+}
+
+h1, h2, h3, h4, h5 {
+ /*font-family: 'Segoe UI',Tahoma,Arial,Helvetica,sans-serif;*/
+ font-weight: 100;
+}
+
+h1 {
+ color: #44525e;
+ margin: 15px 0 15px 0;
+}
+
+h2 {
+ margin: 10px 5px 0 0;
+}
+
+h3 {
+ color: #363636;
+ margin: 5px 5px 0 0;
+}
+
+code {
+ font-family: Consolas, ""Courier New"", courier, monospace;
+}
+
+body .titleerror {
+ padding: 3px 3px 6px 3px;
+ display: block;
+ font-size: 1.5em;
+ font-weight: 100;
+}
+
+body .location {
+ margin: 3px 0 10px 30px;
+}
+
+#header {
+ font-size: 18px;
+ padding: 15px 0;
+ border-top: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+ margin-bottom: 0;
+}
+
+ #header li {
+ display: inline;
+ margin: 5px;
+ padding: 5px;
+ color: #a0a0a0;
+ cursor: pointer;
+ }
+
+ #h");
+ WriteLiteral(@"eader .selected {
+ background: #44c5f2;
+ color: #fff;
+ }
+
+#stackpage ul {
+ list-style: none;
+ padding-left: 0;
+ margin: 0;
+ /*border-bottom: 1px #ddd solid;*/
+}
+
+#stackpage .details {
+ font-size: 1.2em;
+ padding: 3px;
+ color: #000;
+}
+
+#stackpage .stackerror {
+ padding: 5px;
+ border-bottom: 1px #ddd solid;
+}
+
+
+#stackpage .frame {
+ padding: 0;
+ margin: 0 0 0 30px;
+}
+
+ #stackpage .frame h3 {
+ padding: 2px;
+ margin: 0;
+ }
+
+#stackpage .source {
+ padding: 0 0 0 30px;
+}
+
+ #stackpage .source ol li {
+ font-family: Consolas, ""Courier New"", courier, monospace;
+ white-space: pre;
+ background-color: #fbfbfb;
+ }
+
+#stackpage .frame .source .highlight li span {
+ color: #FF0000;
+}
+
+#stackpage .source ol.collapsible li {
+ color: #888;
+}
+
+ #stackpage .source ol.collapsible li span {
+ color: #606060;
+ }
+
+.page table {
+ border-collapse: separate;
+ bo");
+ WriteLiteral(@"rder-spacing: 0;
+ margin: 0 0 20px;
+}
+
+.page th {
+ vertical-align: bottom;
+ padding: 10px 5px 5px 5px;
+ font-weight: 400;
+ color: #a0a0a0;
+ text-align: left;
+}
+
+.page td {
+ padding: 3px 10px;
+}
+
+.page th, .page td {
+ border-right: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+ border-left: 1px transparent solid;
+ border-top: 1px transparent solid;
+ box-sizing: border-box;
+}
+
+ .page th:last-child, .page td:last-child {
+ border-right: 1px transparent solid;
+ }
+
+.page .length {
+ text-align: right;
+}
+
+a {
+ color: #1ba1e2;
+ text-decoration: none;
+}
+
+ a:hover {
+ color: #13709e;
+ text-decoration: underline;
+ }
+
+.showRawException {
+ cursor: pointer;
+ color: #44c5f2;
+ background-color: transparent;
+ font-size: 1.2em;
+ text-align: left;
+ text-decoration: none;
+ display: inline-block;
+ border: 0;
+ padding: 0;
+}
+
+.rawExceptionStackTrace {
+ font-size: 1.2em;
+");
+ WriteLiteral(@"}
+
+.rawExceptionBlock {
+ border-top: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+}
+
+.showRawExceptionContainer {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.expandCollapseButton {
+ cursor: pointer;
+ float: left;
+ height: 16px;
+ width: 16px;
+ font-size: 10px;
+ position: absolute;
+ left: 10px;
+ background-color: #eee;
+ padding: 0;
+ border: 0;
+ margin: 0;
+}
+
+ </style>
+ </head>
+ <body>
+ <h1>");
+#line 222 "CompilationErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_CompilationException);
+
+#line default
+#line hidden
+ WriteLiteral("</h1>\r\n");
+#line 223 "CompilationErrorPage.cshtml"
+
+ var exceptionDetailId = "";
+
+
+#line default
+#line hidden
+ WriteLiteral(" ");
+#line 226 "CompilationErrorPage.cshtml"
+ for (var i = 0; i < Model.ErrorDetails.Count; i++)
+ {
+ var errorDetail = Model.ErrorDetails[i];
+ exceptionDetailId = "exceptionDetail" + i;
+
+
+#line default
+#line hidden
+ WriteLiteral(" <div id=\"stackpage\" class=\"page\">\r\n");
+#line 232 "CompilationErrorPage.cshtml"
+
+ var stackFrameCount = 0;
+ var frameId = "";
+ var fileName = errorDetail.StackFrames.FirstOrDefault()?.File;
+ if (!string.IsNullOrEmpty(fileName))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <div class=\"titleerror\">");
+#line 238 "CompilationErrorPage.cshtml"
+ Write(fileName);
+
+#line default
+#line hidden
+ WriteLiteral("</div>\r\n");
+#line 239 "CompilationErrorPage.cshtml"
+ }
+
+
+#line default
+#line hidden
+ WriteLiteral(" ");
+#line 241 "CompilationErrorPage.cshtml"
+ if (!string.IsNullOrEmpty(errorDetail.ErrorMessage))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <div class=\"details\">");
+#line 243 "CompilationErrorPage.cshtml"
+ Write(errorDetail.ErrorMessage);
+
+#line default
+#line hidden
+ WriteLiteral("</div>\r\n");
+#line 244 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" <br />\r\n <ul>\r\n");
+#line 247 "CompilationErrorPage.cshtml"
+ foreach (var frame in errorDetail.StackFrames)
+ {
+ stackFrameCount++;
+ frameId = "frame" + stackFrameCount;
+
+
+#line default
+#line hidden
+ WriteLiteral(" <li class=\"frame\"");
+ BeginWriteAttribute("id", " id=\"", 5268, "\"", 5281, 1);
+#line 252 "CompilationErrorPage.cshtml"
+WriteAttributeValue("", 5273, frameId, 5273, 8, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(">\r\n");
+#line 253 "CompilationErrorPage.cshtml"
+ if (!string.IsNullOrEmpty(frame.ErrorDetails))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <h3>");
+#line 255 "CompilationErrorPage.cshtml"
+ Write(frame.ErrorDetails);
+
+#line default
+#line hidden
+ WriteLiteral("</h3>\r\n");
+#line 256 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral("\r\n");
+#line 258 "CompilationErrorPage.cshtml"
+ if (frame.Line != 0 && frame.ContextCode.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <button class=\"expandCollapseButton\" data-frameId=\"");
+#line 260 "CompilationErrorPage.cshtml"
+ Write(frameId);
+
+#line default
+#line hidden
+ WriteLiteral("\">+</button>\r\n <div class=\"source\">\r\n");
+#line 262 "CompilationErrorPage.cshtml"
+ if (frame.PreContextCode.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <ol");
+ BeginWriteAttribute("start", " start=\"", 5864, "\"", 5893, 1);
+#line 264 "CompilationErrorPage.cshtml"
+WriteAttributeValue("", 5872, frame.PreContextLine, 5872, 21, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"collapsible\">\r\n");
+#line 265 "CompilationErrorPage.cshtml"
+ foreach (var line in frame.PreContextCode)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li><span>");
+#line 267 "CompilationErrorPage.cshtml"
+ Write(line);
+
+#line default
+#line hidden
+ WriteLiteral("</span></li>\r\n");
+#line 268 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ol>\r\n");
+#line 270 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" <ol");
+ BeginWriteAttribute("start", " start=\"", 6274, "\"", 6293, 1);
+#line 271 "CompilationErrorPage.cshtml"
+WriteAttributeValue("", 6282, frame.Line, 6282, 11, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"highlight\">\r\n");
+#line 272 "CompilationErrorPage.cshtml"
+ foreach (var line in frame.ContextCode)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li><span>");
+#line 274 "CompilationErrorPage.cshtml"
+ Write(line);
+
+#line default
+#line hidden
+ WriteLiteral("</span></li>\r\n");
+#line 275 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ol>\r\n");
+#line 277 "CompilationErrorPage.cshtml"
+ if (frame.PostContextCode.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <ol");
+ BeginWriteAttribute("start", " start=\'", 6720, "\'", 6745, 1);
+#line 279 "CompilationErrorPage.cshtml"
+WriteAttributeValue("", 6728, frame.Line + 1, 6728, 17, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"collapsible\">\r\n");
+#line 280 "CompilationErrorPage.cshtml"
+ foreach (var line in frame.PostContextCode)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li><span>");
+#line 282 "CompilationErrorPage.cshtml"
+ Write(line);
+
+#line default
+#line hidden
+ WriteLiteral("</span></li>\r\n");
+#line 283 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ol>\r\n");
+#line 285 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </div>\r\n");
+#line 287 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </li>\r\n");
+#line 289 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ul>\r\n <br />\r\n </div>\r\n");
+#line 293 "CompilationErrorPage.cshtml"
+ if (!string.IsNullOrEmpty(Model.CompiledContent[i]))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <div class=\"rawExceptionBlock\">\r\n <div class=\"showRawExceptionContainer\">\r\n <button class=\"showRawException\" data-exceptionDetailId=\"");
+#line 297 "CompilationErrorPage.cshtml"
+ Write(exceptionDetailId);
+
+#line default
+#line hidden
+ WriteLiteral("\">Show compilation source</button>\r\n </div>\r\n <div");
+ BeginWriteAttribute("id", " id=\"", 7647, "\"", 7670, 1);
+#line 299 "CompilationErrorPage.cshtml"
+WriteAttributeValue("", 7652, exceptionDetailId, 7652, 18, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"rawExceptionDetails\">\r\n <pre class=\"rawExceptionStackTrace\">");
+#line 300 "CompilationErrorPage.cshtml"
+ Write(Model.CompiledContent[i]);
+
+#line default
+#line hidden
+ WriteLiteral("</pre>\r\n </div>\r\n </div>\r\n");
+#line 303 "CompilationErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+#line 303 "CompilationErrorPage.cshtml"
+
+ }
+
+#line default
+#line hidden
+ WriteLiteral(@"
+ <script>
+ //<!--
+ (function (window, undefined) {
+ ""use strict"";
+
+ function ns(selector, element) {
+ return new NodeCollection(selector, element);
+ }
+
+ function NodeCollection(selector, element) {
+ this.items = [];
+ element = element || window.document;
+
+ var nodeList;
+
+ if (typeof (selector) === ""string"") {
+ nodeList = element.querySelectorAll(selector);
+ for (var i = 0, l = nodeList.length; i < l; i++) {
+ this.items.push(nodeList.item(i));
+ }
+ }
+ }
+
+ NodeCollection.prototype = {
+ each: function (callback) {
+ for (var i = 0, l = this.items.length; i < l; i++) {
+ callback(this.items[i], i);
+ }
+ return this;
+ },
+
+ children: function (selector) {
+ var children = [];
+
+ this.each(function (el) {
+ children = children.concat(ns(selector, e");
+ WriteLiteral(@"l).items);
+ });
+
+ return ns(children);
+ },
+
+ hide: function () {
+ this.each(function (el) {
+ el.style.display = ""none"";
+ });
+
+ return this;
+ },
+
+ toggle: function () {
+ this.each(function (el) {
+ el.style.display = el.style.display === ""none"" ? """" : ""none"";
+ });
+
+ return this;
+ },
+
+ show: function () {
+ this.each(function (el) {
+ el.style.display = """";
+ });
+
+ return this;
+ },
+
+ addClass: function (className) {
+ this.each(function (el) {
+ var existingClassName = el.className,
+ classNames;
+ if (!existingClassName) {
+ el.className = className;
+ } else {
+ classNames = existingClassName.split("" "");
+ if (classNames.indexOf(classNa");
+ WriteLiteral(@"me) < 0) {
+ el.className = existingClassName + "" "" + className;
+ }
+ }
+ });
+
+ return this;
+ },
+
+ removeClass: function (className) {
+ this.each(function (el) {
+ var existingClassName = el.className,
+ classNames, index;
+ if (existingClassName === className) {
+ el.className = """";
+ } else if (existingClassName) {
+ classNames = existingClassName.split("" "");
+ index = classNames.indexOf(className);
+ if (index > 0) {
+ classNames.splice(index, 1);
+ el.className = classNames.join("" "");
+ }
+ }
+ });
+
+ return this;
+ },
+
+ attr: function (name) {
+ if (this.items.length === 0) {
+ return null;
+ }
+
+ ");
+ WriteLiteral(@" return this.items[0].getAttribute(name);
+ },
+
+ on: function (eventName, handler) {
+ this.each(function (el, idx) {
+ var callback = function (e) {
+ e = e || window.event;
+ if (!e.which && e.keyCode) {
+ e.which = e.keyCode; // Normalize IE8 key events
+ }
+ handler.apply(el, [e]);
+ };
+
+ if (el.addEventListener) { // DOM Events
+ el.addEventListener(eventName, callback, false);
+ } else if (el.attachEvent) { // IE8 events
+ el.attachEvent(""on"" + eventName, callback);
+ } else {
+ el[""on"" + type] = callback;
+ }
+ });
+
+ return this;
+ },
+
+ click: function (handler) {
+ return this.on(""click"", handler);
+ },
+
+ keypress: function (handler) {
+ return this.o");
+ WriteLiteral(@"n(""keypress"", handler);
+ }
+ };
+
+ function frame(el) {
+ ns("".source .collapsible"", el).toggle();
+ }
+
+ function expandCollapseButton(el) {
+ var frameId = el.getAttribute(""data-frameId"");
+ frame(document.getElementById(frameId));
+ if (el.innerText === ""+"") {
+ el.innerText = ""-"";
+ }
+ else {
+ el.innerText = ""+"";
+ }
+ }
+
+ function tab(el) {
+ var unselected = ns(""#header .selected"").removeClass(""selected"").attr(""id"");
+ var selected = ns(""#"" + el.id).addClass(""selected"").attr(""id"");
+
+ ns(""#"" + unselected + ""page"").hide();
+ ns(""#"" + selected + ""page"").show();
+ }
+
+ ns("".rawExceptionDetails"").hide();
+ ns("".collapsible"").hide();
+ ns("".page"").hide();
+ ns(""#stackpage"").show();
+
+ ns("".expandCollapseButton"")
+ .click(function () {
+ expandCollapseButton(this);
+ })
+ .keypress(function (e) {
+ if (e.which === 13)");
+ WriteLiteral(@" {
+ expandCollapseButton(this);
+ }
+ });
+
+ ns(""#header li"")
+ .click(function () {
+ tab(this);
+ })
+ .keypress(function (e) {
+ if (e.which === 13) {
+ tab(this);
+ }
+ });
+
+ ns("".showRawException"")
+ .click(function () {
+ var exceptionDetailId = this.getAttribute(""data-exceptionDetailId"");
+ ns(""#"" + exceptionDetailId).toggle();
+ });
+})(window);
+ //-->
+ </script>
+ </body>
+</html>
+");
+ }
+ #pragma warning restore 1998
+#line 8 "CompilationErrorPage.cshtml"
+
+ public CompilationErrorPageModel Model { get; set; }
+
+#line default
+#line hidden
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.cshtml b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.cshtml
new file mode 100644
index 0000000000..961c6bedbb
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/CompilationErrorPage.cshtml
@@ -0,0 +1,116 @@
+@using System
+@using System.Globalization
+@using System.Linq
+@using System.Net
+@using Microsoft.AspNetCore.Diagnostics
+@using Microsoft.AspNetCore.Diagnostics.RazorViews
+@functions
+{
+ public CompilationErrorPageModel Model { get; set; }
+}
+@{
+ Response.StatusCode = 500;
+ Response.ContentType = "text/html; charset=utf-8";
+ Response.ContentLength = null; // Clear any prior Content-Length
+}
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>@Resources.ErrorPageHtml_Title</title>
+ <style>
+ <%$ include: ErrorPage.css %>
+ </style>
+ </head>
+ <body>
+ <h1>@Resources.ErrorPageHtml_CompilationException</h1>
+ @{
+ var exceptionDetailId = "";
+ }
+ @for (var i = 0; i < Model.ErrorDetails.Count; i++)
+ {
+ var errorDetail = Model.ErrorDetails[i];
+ exceptionDetailId = "exceptionDetail" + i;
+
+ <div id="stackpage" class="page">
+ @{
+ var stackFrameCount = 0;
+ var frameId = "";
+ var fileName = errorDetail.StackFrames.FirstOrDefault()?.File;
+ if (!string.IsNullOrEmpty(fileName))
+ {
+ <div class="titleerror">@fileName</div>
+ }
+ }
+ @if (!string.IsNullOrEmpty(errorDetail.ErrorMessage))
+ {
+ <div class="details">@errorDetail.ErrorMessage</div>
+ }
+ <br />
+ <ul>
+ @foreach (var frame in errorDetail.StackFrames)
+ {
+ stackFrameCount++;
+ frameId = "frame" + stackFrameCount;
+
+ <li class="frame" id="@frameId">
+ @if (!string.IsNullOrEmpty(frame.ErrorDetails))
+ {
+ <h3>@frame.ErrorDetails</h3>
+ }
+
+ @if (frame.Line != 0 && frame.ContextCode.Any())
+ {
+ <button class="expandCollapseButton" data-frameId="@frameId">+</button>
+ <div class="source">
+ @if (frame.PreContextCode.Any())
+ {
+ <ol start="@frame.PreContextLine" class="collapsible">
+ @foreach (var line in frame.PreContextCode)
+ {
+ <li><span>@line</span></li>
+ }
+ </ol>
+ }
+ <ol start="@frame.Line" class="highlight">
+ @foreach (var line in frame.ContextCode)
+ {
+ <li><span>@line</span></li>
+ }
+ </ol>
+ @if (frame.PostContextCode.Any())
+ {
+ <ol start='@(frame.Line + 1)' class="collapsible">
+ @foreach (var line in frame.PostContextCode)
+ {
+ <li><span>@line</span></li>
+ }
+ </ol>
+ }
+ </div>
+ }
+ </li>
+ }
+ </ul>
+ <br />
+ </div>
+ @if (!string.IsNullOrEmpty(Model.CompiledContent[i]))
+ {
+ <div class="rawExceptionBlock">
+ <div class="showRawExceptionContainer">
+ <button class="showRawException" data-exceptionDetailId="@exceptionDetailId">Show compilation source</button>
+ </div>
+ <div id="@exceptionDetailId" class="rawExceptionDetails">
+ <pre class="rawExceptionStackTrace">@Model.CompiledContent[i]</pre>
+ </div>
+ </div>
+ }
+ }
+
+ <script>
+ //<!--
+ <%$ include: ErrorPage.js %>
+ //-->
+ </script>
+ </body>
+</html>
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.Designer.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.Designer.cs
new file mode 100644
index 0000000000..ea4969b0ed
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.Designer.cs
@@ -0,0 +1,1109 @@
+// <auto-generated/>
+#pragma warning disable 1591
+namespace Microsoft.AspNetCore.Diagnostics.RazorViews
+{
+ #line hidden
+#line 1 "ErrorPage.cshtml"
+using System;
+
+#line default
+#line hidden
+ using System.Threading.Tasks;
+#line 2 "ErrorPage.cshtml"
+using System.Globalization;
+
+#line default
+#line hidden
+#line 3 "ErrorPage.cshtml"
+using System.Linq;
+
+#line default
+#line hidden
+#line 4 "ErrorPage.cshtml"
+using System.Net;
+
+#line default
+#line hidden
+#line 5 "ErrorPage.cshtml"
+using System.Reflection;
+
+#line default
+#line hidden
+#line 6 "ErrorPage.cshtml"
+using Microsoft.AspNetCore.Diagnostics.RazorViews;
+
+#line default
+#line hidden
+#line 7 "ErrorPage.cshtml"
+using Microsoft.AspNetCore.Diagnostics;
+
+#line default
+#line hidden
+ internal class ErrorPage : Microsoft.Extensions.RazorViews.BaseView
+ {
+ #pragma warning disable 1998
+ public async override global::System.Threading.Tasks.Task ExecuteAsync()
+ {
+#line 17 "ErrorPage.cshtml"
+
+ // TODO: Response.ReasonPhrase = "Internal Server Error";
+ Response.ContentType = "text/html; charset=utf-8";
+ string location = string.Empty;
+
+#line default
+#line hidden
+ WriteLiteral("<!DOCTYPE html>\r\n<html");
+ BeginWriteAttribute("lang", " lang=\"", 536, "\"", 597, 1);
+#line 23 "ErrorPage.cshtml"
+WriteAttributeValue("", 543, CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, 543, 54, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" xmlns=\"http://www.w3.org/1999/xhtml\">\r\n <head>\r\n <meta charset=\"utf-8\" />\r\n <title>");
+#line 26 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_Title);
+
+#line default
+#line hidden
+ WriteLiteral(@"</title>
+ <style>
+ body {
+ font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
+ font-size: .813em;
+ color: #222;
+ background-color: #fff;
+}
+
+h1, h2, h3, h4, h5 {
+ /*font-family: 'Segoe UI',Tahoma,Arial,Helvetica,sans-serif;*/
+ font-weight: 100;
+}
+
+h1 {
+ color: #44525e;
+ margin: 15px 0 15px 0;
+}
+
+h2 {
+ margin: 10px 5px 0 0;
+}
+
+h3 {
+ color: #363636;
+ margin: 5px 5px 0 0;
+}
+
+code {
+ font-family: Consolas, ""Courier New"", courier, monospace;
+}
+
+body .titleerror {
+ padding: 3px 3px 6px 3px;
+ display: block;
+ font-size: 1.5em;
+ font-weight: 100;
+}
+
+body .location {
+ margin: 3px 0 10px 30px;
+}
+
+#header {
+ font-size: 18px;
+ padding: 15px 0;
+ border-top: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+ margin-bottom: 0;
+}
+
+ #header li {
+ display: inline;
+ margin: 5px;
+ padding: 5px;
+ color: #a0a0a0;
+ cursor: pointer;
+ }
+
+ #h");
+ WriteLiteral(@"eader .selected {
+ background: #44c5f2;
+ color: #fff;
+ }
+
+#stackpage ul {
+ list-style: none;
+ padding-left: 0;
+ margin: 0;
+ /*border-bottom: 1px #ddd solid;*/
+}
+
+#stackpage .details {
+ font-size: 1.2em;
+ padding: 3px;
+ color: #000;
+}
+
+#stackpage .stackerror {
+ padding: 5px;
+ border-bottom: 1px #ddd solid;
+}
+
+
+#stackpage .frame {
+ padding: 0;
+ margin: 0 0 0 30px;
+}
+
+ #stackpage .frame h3 {
+ padding: 2px;
+ margin: 0;
+ }
+
+#stackpage .source {
+ padding: 0 0 0 30px;
+}
+
+ #stackpage .source ol li {
+ font-family: Consolas, ""Courier New"", courier, monospace;
+ white-space: pre;
+ background-color: #fbfbfb;
+ }
+
+#stackpage .frame .source .highlight li span {
+ color: #FF0000;
+}
+
+#stackpage .source ol.collapsible li {
+ color: #888;
+}
+
+ #stackpage .source ol.collapsible li span {
+ color: #606060;
+ }
+
+.page table {
+ border-collapse: separate;
+ bo");
+ WriteLiteral(@"rder-spacing: 0;
+ margin: 0 0 20px;
+}
+
+.page th {
+ vertical-align: bottom;
+ padding: 10px 5px 5px 5px;
+ font-weight: 400;
+ color: #a0a0a0;
+ text-align: left;
+}
+
+.page td {
+ padding: 3px 10px;
+}
+
+.page th, .page td {
+ border-right: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+ border-left: 1px transparent solid;
+ border-top: 1px transparent solid;
+ box-sizing: border-box;
+}
+
+ .page th:last-child, .page td:last-child {
+ border-right: 1px transparent solid;
+ }
+
+.page .length {
+ text-align: right;
+}
+
+a {
+ color: #1ba1e2;
+ text-decoration: none;
+}
+
+ a:hover {
+ color: #13709e;
+ text-decoration: underline;
+ }
+
+.showRawException {
+ cursor: pointer;
+ color: #44c5f2;
+ background-color: transparent;
+ font-size: 1.2em;
+ text-align: left;
+ text-decoration: none;
+ display: inline-block;
+ border: 0;
+ padding: 0;
+}
+
+.rawExceptionStackTrace {
+ font-size: 1.2em;
+");
+ WriteLiteral(@"}
+
+.rawExceptionBlock {
+ border-top: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+}
+
+.showRawExceptionContainer {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.expandCollapseButton {
+ cursor: pointer;
+ float: left;
+ height: 16px;
+ width: 16px;
+ font-size: 10px;
+ position: absolute;
+ left: 10px;
+ background-color: #eee;
+ padding: 0;
+ border: 0;
+ margin: 0;
+}
+
+ </style>
+ </head>
+ <body>
+ <h1>");
+#line 228 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_UnhandledException);
+
+#line default
+#line hidden
+ WriteLiteral("</h1>\r\n");
+#line 229 "ErrorPage.cshtml"
+ foreach (var errorDetail in Model.ErrorDetails)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <div class=\"titleerror\">");
+#line 231 "ErrorPage.cshtml"
+ Write(errorDetail.Error.GetType().Name);
+
+#line default
+#line hidden
+ WriteLiteral(": ");
+#line 231 "ErrorPage.cshtml"
+ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message));
+
+#line default
+#line hidden
+ WriteLiteral("</div>\r\n");
+#line 232 "ErrorPage.cshtml"
+
+ var firstFrame = errorDetail.StackFrames.FirstOrDefault();
+ if (firstFrame != null)
+ {
+ location = firstFrame.Function;
+ }
+ if (!string.IsNullOrEmpty(location) && firstFrame != null && !string.IsNullOrEmpty(firstFrame.File))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <p class=\"location\">");
+#line 240 "ErrorPage.cshtml"
+ Write(location);
+
+#line default
+#line hidden
+ WriteLiteral(" in <code");
+ BeginWriteAttribute("title", " title=\"", 4950, "\"", 4974, 1);
+#line 240 "ErrorPage.cshtml"
+WriteAttributeValue("", 4958, firstFrame.File, 4958, 16, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(">");
+#line 240 "ErrorPage.cshtml"
+ Write(System.IO.Path.GetFileName(firstFrame.File));
+
+#line default
+#line hidden
+ WriteLiteral("</code>, line ");
+#line 240 "ErrorPage.cshtml"
+ Write(firstFrame.Line);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n");
+#line 241 "ErrorPage.cshtml"
+ }
+ else if (!string.IsNullOrEmpty(location))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <p class=\"location\">");
+#line 244 "ErrorPage.cshtml"
+ Write(location);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n");
+#line 245 "ErrorPage.cshtml"
+ }
+ else
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <p class=\"location\">");
+#line 248 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_UnknownLocation);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n");
+#line 249 "ErrorPage.cshtml"
+ }
+
+ var reflectionTypeLoadException = errorDetail.Error as ReflectionTypeLoadException;
+ if (reflectionTypeLoadException != null)
+ {
+ if (reflectionTypeLoadException.LoaderExceptions.Length > 0)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <h3>Loader Exceptions:</h3>\r\n <ul>\r\n");
+#line 258 "ErrorPage.cshtml"
+ foreach (var ex in reflectionTypeLoadException.LoaderExceptions)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li>");
+#line 260 "ErrorPage.cshtml"
+ Write(ex.Message);
+
+#line default
+#line hidden
+ WriteLiteral("</li>\r\n");
+#line 261 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ul>\r\n");
+#line 263 "ErrorPage.cshtml"
+ }
+ }
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" <ul id=\"header\">\r\n <li id=\"stack\" tabindex=\"1\" class=\"selected\">\r\n ");
+#line 268 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_StackButton);
+
+#line default
+#line hidden
+ WriteLiteral("\r\n </li>\r\n <li id=\"query\" tabindex=\"2\">\r\n ");
+#line 271 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_QueryButton);
+
+#line default
+#line hidden
+ WriteLiteral("\r\n </li>\r\n <li id=\"cookies\" tabindex=\"3\">\r\n ");
+#line 274 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_CookiesButton);
+
+#line default
+#line hidden
+ WriteLiteral("\r\n </li>\r\n <li id=\"headers\" tabindex=\"4\">\r\n ");
+#line 277 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_HeadersButton);
+
+#line default
+#line hidden
+ WriteLiteral("\r\n </li>\r\n </ul>\r\n\r\n <div id=\"stackpage\" class=\"page\">\r\n <ul>\r\n");
+#line 283 "ErrorPage.cshtml"
+
+ var exceptionCount = 0;
+ var stackFrameCount = 0;
+ var exceptionDetailId = "";
+ var frameId = "";
+
+
+#line default
+#line hidden
+ WriteLiteral(" ");
+#line 289 "ErrorPage.cshtml"
+ foreach (var errorDetail in Model.ErrorDetails)
+ {
+ exceptionCount++;
+ exceptionDetailId = "exceptionDetail" + exceptionCount;
+
+
+#line default
+#line hidden
+ WriteLiteral(" <li>\r\n <h2 class=\"stackerror\">");
+#line 295 "ErrorPage.cshtml"
+ Write(errorDetail.Error.GetType().Name);
+
+#line default
+#line hidden
+ WriteLiteral(": ");
+#line 295 "ErrorPage.cshtml"
+ Write(errorDetail.Error.Message);
+
+#line default
+#line hidden
+ WriteLiteral("</h2>\r\n <ul>\r\n");
+#line 297 "ErrorPage.cshtml"
+ foreach (var frame in errorDetail.StackFrames)
+ {
+ stackFrameCount++;
+ frameId = "frame" + stackFrameCount;
+
+
+#line default
+#line hidden
+ WriteLiteral(" <li class=\"frame\"");
+ BeginWriteAttribute("id", " id=\"", 7386, "\"", 7399, 1);
+#line 302 "ErrorPage.cshtml"
+WriteAttributeValue("", 7391, frameId, 7391, 8, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(">\r\n");
+#line 303 "ErrorPage.cshtml"
+ if (string.IsNullOrEmpty(frame.File))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <h3>");
+#line 305 "ErrorPage.cshtml"
+ Write(frame.Function);
+
+#line default
+#line hidden
+ WriteLiteral("</h3>\r\n");
+#line 306 "ErrorPage.cshtml"
+ }
+ else
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <h3>");
+#line 309 "ErrorPage.cshtml"
+ Write(frame.Function);
+
+#line default
+#line hidden
+ WriteLiteral(" in <code");
+ BeginWriteAttribute("title", " title=\"", 7744, "\"", 7763, 1);
+#line 309 "ErrorPage.cshtml"
+WriteAttributeValue("", 7752, frame.File, 7752, 11, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(">");
+#line 309 "ErrorPage.cshtml"
+ Write(System.IO.Path.GetFileName(frame.File));
+
+#line default
+#line hidden
+ WriteLiteral("</code></h3>\r\n");
+#line 310 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral("\r\n");
+#line 312 "ErrorPage.cshtml"
+ if (frame.Line != 0 && frame.ContextCode.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <button class=\"expandCollapseButton\" data-frameId=\"");
+#line 314 "ErrorPage.cshtml"
+ Write(frameId);
+
+#line default
+#line hidden
+ WriteLiteral("\">+</button>\r\n <div class=\"source\">\r\n");
+#line 316 "ErrorPage.cshtml"
+ if (frame.PreContextCode.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <ol");
+ BeginWriteAttribute("start", " start=\"", 8303, "\"", 8332, 1);
+#line 318 "ErrorPage.cshtml"
+WriteAttributeValue("", 8311, frame.PreContextLine, 8311, 21, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"collapsible\">\r\n");
+#line 319 "ErrorPage.cshtml"
+ foreach (var line in frame.PreContextCode)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li><span>");
+#line 321 "ErrorPage.cshtml"
+ Write(line);
+
+#line default
+#line hidden
+ WriteLiteral("</span></li>\r\n");
+#line 322 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ol>\r\n");
+#line 324 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral("\r\n <ol");
+ BeginWriteAttribute("start", " start=\"", 8771, "\"", 8790, 1);
+#line 326 "ErrorPage.cshtml"
+WriteAttributeValue("", 8779, frame.Line, 8779, 11, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"highlight\">\r\n");
+#line 327 "ErrorPage.cshtml"
+ foreach (var line in frame.ContextCode)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li><span>");
+#line 329 "ErrorPage.cshtml"
+ Write(line);
+
+#line default
+#line hidden
+ WriteLiteral("</span></li>\r\n");
+#line 330 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ol>\r\n\r\n");
+#line 333 "ErrorPage.cshtml"
+ if (frame.PostContextCode.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <ol");
+ BeginWriteAttribute("start", " start=\'", 9283, "\'", 9308, 1);
+#line 335 "ErrorPage.cshtml"
+WriteAttributeValue("", 9291, frame.Line + 1, 9291, 17, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"collapsible\">\r\n");
+#line 336 "ErrorPage.cshtml"
+ foreach (var line in frame.PostContextCode)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <li><span>");
+#line 338 "ErrorPage.cshtml"
+ Write(line);
+
+#line default
+#line hidden
+ WriteLiteral("</span></li>\r\n");
+#line 339 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ol>\r\n");
+#line 341 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </div>\r\n");
+#line 343 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </li>\r\n");
+#line 345 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(@" </ul>
+ </li>
+ <li>
+ <br/>
+ <div class=""rawExceptionBlock"">
+ <div class=""showRawExceptionContainer"">
+ <button class=""showRawException"" data-exceptionDetailId=""");
+#line 352 "ErrorPage.cshtml"
+ Write(exceptionDetailId);
+
+#line default
+#line hidden
+ WriteLiteral("\">Show raw exception details</button>\r\n </div>\r\n <div");
+ BeginWriteAttribute("id", " id=\"", 10299, "\"", 10322, 1);
+#line 354 "ErrorPage.cshtml"
+WriteAttributeValue("", 10304, exceptionDetailId, 10304, 18, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" class=\"rawExceptionDetails\">\r\n <pre class=\"rawExceptionStackTrace\">");
+#line 355 "ErrorPage.cshtml"
+ Write(errorDetail.Error.ToString());
+
+#line default
+#line hidden
+ WriteLiteral("</pre>\r\n </div>\r\n </div>\r\n </li>\r\n");
+#line 359 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </ul>\r\n </div>\r\n\r\n <div id=\"querypage\" class=\"page\">\r\n");
+#line 364 "ErrorPage.cshtml"
+ if (Model.Query.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <table>\r\n <thead>\r\n <tr>\r\n <th>");
+#line 369 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_VariableColumn);
+
+#line default
+#line hidden
+ WriteLiteral("</th>\r\n <th>");
+#line 370 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_ValueColumn);
+
+#line default
+#line hidden
+ WriteLiteral("</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n");
+#line 374 "ErrorPage.cshtml"
+ foreach (var kv in Model.Query.OrderBy(kv => kv.Key))
+ {
+ foreach (var v in kv.Value)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <tr>\r\n <td>");
+#line 379 "ErrorPage.cshtml"
+ Write(kv.Key);
+
+#line default
+#line hidden
+ WriteLiteral("</td>\r\n <td>");
+#line 380 "ErrorPage.cshtml"
+ Write(v);
+
+#line default
+#line hidden
+ WriteLiteral("</td>\r\n </tr>\r\n");
+#line 382 "ErrorPage.cshtml"
+ }
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </tbody>\r\n </table>\r\n");
+#line 386 "ErrorPage.cshtml"
+ }
+ else
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <p>");
+#line 389 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_NoQueryStringData);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n");
+#line 390 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </div>\r\n\r\n <div id=\"cookiespage\" class=\"page\">\r\n");
+#line 394 "ErrorPage.cshtml"
+ if (Model.Cookies.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <table>\r\n <thead>\r\n <tr>\r\n <th>");
+#line 399 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_VariableColumn);
+
+#line default
+#line hidden
+ WriteLiteral("</th>\r\n <th>");
+#line 400 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_ValueColumn);
+
+#line default
+#line hidden
+ WriteLiteral("</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n");
+#line 404 "ErrorPage.cshtml"
+ foreach (var kv in Model.Cookies.OrderBy(kv => kv.Key))
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <tr>\r\n <td>");
+#line 407 "ErrorPage.cshtml"
+ Write(kv.Key);
+
+#line default
+#line hidden
+ WriteLiteral("</td>\r\n <td>");
+#line 408 "ErrorPage.cshtml"
+ Write(kv.Value);
+
+#line default
+#line hidden
+ WriteLiteral("</td>\r\n </tr>\r\n");
+#line 410 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </tbody>\r\n </table>\r\n");
+#line 413 "ErrorPage.cshtml"
+ }
+ else
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <p>");
+#line 416 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_NoCookieData);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n");
+#line 417 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </div>\r\n <div id=\"headerspage\" class=\"page\">\r\n");
+#line 420 "ErrorPage.cshtml"
+ if (Model.Headers.Any())
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <table>\r\n <thead>\r\n <tr>\r\n <th>");
+#line 425 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_VariableColumn);
+
+#line default
+#line hidden
+ WriteLiteral("</th>\r\n <th>");
+#line 426 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_ValueColumn);
+
+#line default
+#line hidden
+ WriteLiteral("</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n");
+#line 430 "ErrorPage.cshtml"
+ foreach (var kv in Model.Headers.OrderBy(kv => kv.Key))
+ {
+ foreach (var v in kv.Value)
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <tr>\r\n <td>");
+#line 435 "ErrorPage.cshtml"
+ Write(kv.Key);
+
+#line default
+#line hidden
+ WriteLiteral("</td>\r\n <td>");
+#line 436 "ErrorPage.cshtml"
+ Write(v);
+
+#line default
+#line hidden
+ WriteLiteral("</td>\r\n </tr>\r\n");
+#line 438 "ErrorPage.cshtml"
+ }
+ }
+
+#line default
+#line hidden
+ WriteLiteral(" </tbody>\r\n </table>\r\n");
+#line 442 "ErrorPage.cshtml"
+ }
+ else
+ {
+
+#line default
+#line hidden
+ WriteLiteral(" <p>");
+#line 445 "ErrorPage.cshtml"
+ Write(Resources.ErrorPageHtml_NoHeaderData);
+
+#line default
+#line hidden
+ WriteLiteral("</p>\r\n");
+#line 446 "ErrorPage.cshtml"
+ }
+
+#line default
+#line hidden
+ WriteLiteral(@" </div>
+ <script>
+ //<!--
+ (function (window, undefined) {
+ ""use strict"";
+
+ function ns(selector, element) {
+ return new NodeCollection(selector, element);
+ }
+
+ function NodeCollection(selector, element) {
+ this.items = [];
+ element = element || window.document;
+
+ var nodeList;
+
+ if (typeof (selector) === ""string"") {
+ nodeList = element.querySelectorAll(selector);
+ for (var i = 0, l = nodeList.length; i < l; i++) {
+ this.items.push(nodeList.item(i));
+ }
+ }
+ }
+
+ NodeCollection.prototype = {
+ each: function (callback) {
+ for (var i = 0, l = this.items.length; i < l; i++) {
+ callback(this.items[i], i);
+ }
+ return this;
+ },
+
+ children: function (selector) {
+ var children = [];
+
+ this.each(function (el) {
+ children = children.concat(");
+ WriteLiteral(@"ns(selector, el).items);
+ });
+
+ return ns(children);
+ },
+
+ hide: function () {
+ this.each(function (el) {
+ el.style.display = ""none"";
+ });
+
+ return this;
+ },
+
+ toggle: function () {
+ this.each(function (el) {
+ el.style.display = el.style.display === ""none"" ? """" : ""none"";
+ });
+
+ return this;
+ },
+
+ show: function () {
+ this.each(function (el) {
+ el.style.display = """";
+ });
+
+ return this;
+ },
+
+ addClass: function (className) {
+ this.each(function (el) {
+ var existingClassName = el.className,
+ classNames;
+ if (!existingClassName) {
+ el.className = className;
+ } else {
+ classNames = existingClassName.split("" "");
+ if (classNames.i");
+ WriteLiteral(@"ndexOf(className) < 0) {
+ el.className = existingClassName + "" "" + className;
+ }
+ }
+ });
+
+ return this;
+ },
+
+ removeClass: function (className) {
+ this.each(function (el) {
+ var existingClassName = el.className,
+ classNames, index;
+ if (existingClassName === className) {
+ el.className = """";
+ } else if (existingClassName) {
+ classNames = existingClassName.split("" "");
+ index = classNames.indexOf(className);
+ if (index > 0) {
+ classNames.splice(index, 1);
+ el.className = classNames.join("" "");
+ }
+ }
+ });
+
+ return this;
+ },
+
+ attr: function (name) {
+ if (this.items.length === 0) {
+ return null;
+ ");
+ WriteLiteral(@" }
+
+ return this.items[0].getAttribute(name);
+ },
+
+ on: function (eventName, handler) {
+ this.each(function (el, idx) {
+ var callback = function (e) {
+ e = e || window.event;
+ if (!e.which && e.keyCode) {
+ e.which = e.keyCode; // Normalize IE8 key events
+ }
+ handler.apply(el, [e]);
+ };
+
+ if (el.addEventListener) { // DOM Events
+ el.addEventListener(eventName, callback, false);
+ } else if (el.attachEvent) { // IE8 events
+ el.attachEvent(""on"" + eventName, callback);
+ } else {
+ el[""on"" + type] = callback;
+ }
+ });
+
+ return this;
+ },
+
+ click: function (handler) {
+ return this.on(""click"", handler);
+ },
+
+ keypress: function (handler) {
+ ");
+ WriteLiteral(@" return this.on(""keypress"", handler);
+ }
+ };
+
+ function frame(el) {
+ ns("".source .collapsible"", el).toggle();
+ }
+
+ function expandCollapseButton(el) {
+ var frameId = el.getAttribute(""data-frameId"");
+ frame(document.getElementById(frameId));
+ if (el.innerText === ""+"") {
+ el.innerText = ""-"";
+ }
+ else {
+ el.innerText = ""+"";
+ }
+ }
+
+ function tab(el) {
+ var unselected = ns(""#header .selected"").removeClass(""selected"").attr(""id"");
+ var selected = ns(""#"" + el.id).addClass(""selected"").attr(""id"");
+
+ ns(""#"" + unselected + ""page"").hide();
+ ns(""#"" + selected + ""page"").show();
+ }
+
+ ns("".rawExceptionDetails"").hide();
+ ns("".collapsible"").hide();
+ ns("".page"").hide();
+ ns(""#stackpage"").show();
+
+ ns("".expandCollapseButton"")
+ .click(function () {
+ expandCollapseButton(this);
+ })
+ .keypress(function (e) {
+ if (e");
+ WriteLiteral(@".which === 13) {
+ expandCollapseButton(this);
+ }
+ });
+
+ ns(""#header li"")
+ .click(function () {
+ tab(this);
+ })
+ .keypress(function (e) {
+ if (e.which === 13) {
+ tab(this);
+ }
+ });
+
+ ns("".showRawException"")
+ .click(function () {
+ var exceptionDetailId = this.getAttribute(""data-exceptionDetailId"");
+ ns(""#"" + exceptionDetailId).toggle();
+ });
+})(window);
+ //-->
+ </script>
+ </body>
+</html>
+");
+ }
+ #pragma warning restore 1998
+#line 9 "ErrorPage.cshtml"
+
+ public ErrorPage(ErrorPageModel model)
+ {
+ Model = model;
+ }
+
+ public ErrorPageModel Model { get; set; }
+
+#line default
+#line hidden
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.cshtml b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.cshtml
new file mode 100644
index 0000000000..3d0a28e8ef
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.cshtml
@@ -0,0 +1,258 @@
+@using System
+@using System.Globalization
+@using System.Linq
+@using System.Net
+@using System.Reflection
+@using Microsoft.AspNetCore.Diagnostics.RazorViews
+@using Microsoft.AspNetCore.Diagnostics
+@functions
+{
+ public ErrorPage(ErrorPageModel model)
+ {
+ Model = model;
+ }
+
+ public ErrorPageModel Model { get; set; }
+}
+@{
+ // TODO: Response.ReasonPhrase = "Internal Server Error";
+ Response.ContentType = "text/html; charset=utf-8";
+ string location = string.Empty;
+}
+<!DOCTYPE html>
+<html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName" xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8" />
+ <title>@Resources.ErrorPageHtml_Title</title>
+ <style>
+ <%$ include: ErrorPage.css %>
+ </style>
+ </head>
+ <body>
+ <h1>@Resources.ErrorPageHtml_UnhandledException</h1>
+ @foreach (var errorDetail in Model.ErrorDetails)
+ {
+ <div class="titleerror">@errorDetail.Error.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); }</div>
+
+ var firstFrame = errorDetail.StackFrames.FirstOrDefault();
+ if (firstFrame != null)
+ {
+ location = firstFrame.Function;
+ }
+ if (!string.IsNullOrEmpty(location) && firstFrame != null && !string.IsNullOrEmpty(firstFrame.File))
+ {
+ <p class="location">@location in <code title="@firstFrame.File">@System.IO.Path.GetFileName(firstFrame.File)</code>, line @firstFrame.Line</p>
+ }
+ else if (!string.IsNullOrEmpty(location))
+ {
+ <p class="location">@location</p>
+ }
+ else
+ {
+ <p class="location">@Resources.ErrorPageHtml_UnknownLocation</p>
+ }
+
+ var reflectionTypeLoadException = errorDetail.Error as ReflectionTypeLoadException;
+ if (reflectionTypeLoadException != null)
+ {
+ if (reflectionTypeLoadException.LoaderExceptions.Length > 0)
+ {
+ <h3>Loader Exceptions:</h3>
+ <ul>
+ @foreach (var ex in reflectionTypeLoadException.LoaderExceptions)
+ {
+ <li>@ex.Message</li>
+ }
+ </ul>
+ }
+ }
+ }
+ <ul id="header">
+ <li id="stack" tabindex="1" class="selected">
+ @Resources.ErrorPageHtml_StackButton
+ </li>
+ <li id="query" tabindex="2">
+ @Resources.ErrorPageHtml_QueryButton
+ </li>
+ <li id="cookies" tabindex="3">
+ @Resources.ErrorPageHtml_CookiesButton
+ </li>
+ <li id="headers" tabindex="4">
+ @Resources.ErrorPageHtml_HeadersButton
+ </li>
+ </ul>
+
+ <div id="stackpage" class="page">
+ <ul>
+ @{
+ var exceptionCount = 0;
+ var stackFrameCount = 0;
+ var exceptionDetailId = "";
+ var frameId = "";
+ }
+ @foreach (var errorDetail in Model.ErrorDetails)
+ {
+ exceptionCount++;
+ exceptionDetailId = "exceptionDetail" + exceptionCount;
+
+ <li>
+ <h2 class="stackerror">@errorDetail.Error.GetType().Name: @errorDetail.Error.Message</h2>
+ <ul>
+ @foreach (var frame in errorDetail.StackFrames)
+ {
+ stackFrameCount++;
+ frameId = "frame" + stackFrameCount;
+
+ <li class="frame" id="@frameId">
+ @if (string.IsNullOrEmpty(frame.File))
+ {
+ <h3>@frame.Function</h3>
+ }
+ else
+ {
+ <h3>@frame.Function in <code title="@frame.File">@System.IO.Path.GetFileName(frame.File)</code></h3>
+ }
+
+ @if (frame.Line != 0 && frame.ContextCode.Any())
+ {
+ <button class="expandCollapseButton" data-frameId="@frameId">+</button>
+ <div class="source">
+ @if (frame.PreContextCode.Any())
+ {
+ <ol start="@frame.PreContextLine" class="collapsible">
+ @foreach (var line in frame.PreContextCode)
+ {
+ <li><span>@line</span></li>
+ }
+ </ol>
+ }
+
+ <ol start="@frame.Line" class="highlight">
+ @foreach (var line in frame.ContextCode)
+ {
+ <li><span>@line</span></li>
+ }
+ </ol>
+
+ @if (frame.PostContextCode.Any())
+ {
+ <ol start='@(frame.Line + 1)' class="collapsible">
+ @foreach (var line in frame.PostContextCode)
+ {
+ <li><span>@line</span></li>
+ }
+ </ol>
+ }
+ </div>
+ }
+ </li>
+ }
+ </ul>
+ </li>
+ <li>
+ <br/>
+ <div class="rawExceptionBlock">
+ <div class="showRawExceptionContainer">
+ <button class="showRawException" data-exceptionDetailId="@exceptionDetailId">Show raw exception details</button>
+ </div>
+ <div id="@exceptionDetailId" class="rawExceptionDetails">
+ <pre class="rawExceptionStackTrace">@errorDetail.Error.ToString()</pre>
+ </div>
+ </div>
+ </li>
+ }
+ </ul>
+ </div>
+
+ <div id="querypage" class="page">
+ @if (Model.Query.Any())
+ {
+ <table>
+ <thead>
+ <tr>
+ <th>@Resources.ErrorPageHtml_VariableColumn</th>
+ <th>@Resources.ErrorPageHtml_ValueColumn</th>
+ </tr>
+ </thead>
+ <tbody>
+ @foreach (var kv in Model.Query.OrderBy(kv => kv.Key))
+ {
+ foreach (var v in kv.Value)
+ {
+ <tr>
+ <td>@kv.Key</td>
+ <td>@v</td>
+ </tr>
+ }
+ }
+ </tbody>
+ </table>
+ }
+ else
+ {
+ <p>@Resources.ErrorPageHtml_NoQueryStringData</p>
+ }
+ </div>
+
+ <div id="cookiespage" class="page">
+ @if (Model.Cookies.Any())
+ {
+ <table>
+ <thead>
+ <tr>
+ <th>@Resources.ErrorPageHtml_VariableColumn</th>
+ <th>@Resources.ErrorPageHtml_ValueColumn</th>
+ </tr>
+ </thead>
+ <tbody>
+ @foreach (var kv in Model.Cookies.OrderBy(kv => kv.Key))
+ {
+ <tr>
+ <td>@kv.Key</td>
+ <td>@kv.Value</td>
+ </tr>
+ }
+ </tbody>
+ </table>
+ }
+ else
+ {
+ <p>@Resources.ErrorPageHtml_NoCookieData</p>
+ }
+ </div>
+ <div id="headerspage" class="page">
+ @if (Model.Headers.Any())
+ {
+ <table>
+ <thead>
+ <tr>
+ <th>@Resources.ErrorPageHtml_VariableColumn</th>
+ <th>@Resources.ErrorPageHtml_ValueColumn</th>
+ </tr>
+ </thead>
+ <tbody>
+ @foreach (var kv in Model.Headers.OrderBy(kv => kv.Key))
+ {
+ foreach (var v in kv.Value)
+ {
+ <tr>
+ <td>@kv.Key</td>
+ <td>@v</td>
+ </tr>
+ }
+ }
+ </tbody>
+ </table>
+ }
+ else
+ {
+ <p>@Resources.ErrorPageHtml_NoHeaderData</p>
+ }
+ </div>
+ <script>
+ //<!--
+ <%$ include: ErrorPage.js %>
+ //-->
+ </script>
+ </body>
+</html>
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.css b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.css
new file mode 100644
index 0000000000..7fd48c1258
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.css
@@ -0,0 +1,196 @@
+body {
+ font-family: 'Segoe UI', Tahoma, Arial, Helvetica, sans-serif;
+ font-size: .813em;
+ color: #222;
+ background-color: #fff;
+}
+
+h1, h2, h3, h4, h5 {
+ /*font-family: 'Segoe UI',Tahoma,Arial,Helvetica,sans-serif;*/
+ font-weight: 100;
+}
+
+h1 {
+ color: #44525e;
+ margin: 15px 0 15px 0;
+}
+
+h2 {
+ margin: 10px 5px 0 0;
+}
+
+h3 {
+ color: #363636;
+ margin: 5px 5px 0 0;
+}
+
+code {
+ font-family: Consolas, "Courier New", courier, monospace;
+}
+
+body .titleerror {
+ padding: 3px 3px 6px 3px;
+ display: block;
+ font-size: 1.5em;
+ font-weight: 100;
+}
+
+body .location {
+ margin: 3px 0 10px 30px;
+}
+
+#header {
+ font-size: 18px;
+ padding: 15px 0;
+ border-top: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+ margin-bottom: 0;
+}
+
+ #header li {
+ display: inline;
+ margin: 5px;
+ padding: 5px;
+ color: #a0a0a0;
+ cursor: pointer;
+ }
+
+ #header .selected {
+ background: #44c5f2;
+ color: #fff;
+ }
+
+#stackpage ul {
+ list-style: none;
+ padding-left: 0;
+ margin: 0;
+ /*border-bottom: 1px #ddd solid;*/
+}
+
+#stackpage .details {
+ font-size: 1.2em;
+ padding: 3px;
+ color: #000;
+}
+
+#stackpage .stackerror {
+ padding: 5px;
+ border-bottom: 1px #ddd solid;
+}
+
+
+#stackpage .frame {
+ padding: 0;
+ margin: 0 0 0 30px;
+}
+
+ #stackpage .frame h3 {
+ padding: 2px;
+ margin: 0;
+ }
+
+#stackpage .source {
+ padding: 0 0 0 30px;
+}
+
+ #stackpage .source ol li {
+ font-family: Consolas, "Courier New", courier, monospace;
+ white-space: pre;
+ background-color: #fbfbfb;
+ }
+
+#stackpage .frame .source .highlight li span {
+ color: #FF0000;
+}
+
+#stackpage .source ol.collapsible li {
+ color: #888;
+}
+
+ #stackpage .source ol.collapsible li span {
+ color: #606060;
+ }
+
+.page table {
+ border-collapse: separate;
+ border-spacing: 0;
+ margin: 0 0 20px;
+}
+
+.page th {
+ vertical-align: bottom;
+ padding: 10px 5px 5px 5px;
+ font-weight: 400;
+ color: #a0a0a0;
+ text-align: left;
+}
+
+.page td {
+ padding: 3px 10px;
+}
+
+.page th, .page td {
+ border-right: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+ border-left: 1px transparent solid;
+ border-top: 1px transparent solid;
+ box-sizing: border-box;
+}
+
+ .page th:last-child, .page td:last-child {
+ border-right: 1px transparent solid;
+ }
+
+.page .length {
+ text-align: right;
+}
+
+a {
+ color: #1ba1e2;
+ text-decoration: none;
+}
+
+ a:hover {
+ color: #13709e;
+ text-decoration: underline;
+ }
+
+.showRawException {
+ cursor: pointer;
+ color: #44c5f2;
+ background-color: transparent;
+ font-size: 1.2em;
+ text-align: left;
+ text-decoration: none;
+ display: inline-block;
+ border: 0;
+ padding: 0;
+}
+
+.rawExceptionStackTrace {
+ font-size: 1.2em;
+}
+
+.rawExceptionBlock {
+ border-top: 1px #ddd solid;
+ border-bottom: 1px #ddd solid;
+}
+
+.showRawExceptionContainer {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.expandCollapseButton {
+ cursor: pointer;
+ float: left;
+ height: 16px;
+ width: 16px;
+ font-size: 10px;
+ position: absolute;
+ left: 10px;
+ background-color: #eee;
+ padding: 0;
+ border: 0;
+ margin: 0;
+}
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.js b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.js
new file mode 100644
index 0000000000..3925cfd2f2
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPage.js
@@ -0,0 +1,192 @@
+(function (window, undefined) {
+ "use strict";
+
+ function ns(selector, element) {
+ return new NodeCollection(selector, element);
+ }
+
+ function NodeCollection(selector, element) {
+ this.items = [];
+ element = element || window.document;
+
+ var nodeList;
+
+ if (typeof (selector) === "string") {
+ nodeList = element.querySelectorAll(selector);
+ for (var i = 0, l = nodeList.length; i < l; i++) {
+ this.items.push(nodeList.item(i));
+ }
+ }
+ }
+
+ NodeCollection.prototype = {
+ each: function (callback) {
+ for (var i = 0, l = this.items.length; i < l; i++) {
+ callback(this.items[i], i);
+ }
+ return this;
+ },
+
+ children: function (selector) {
+ var children = [];
+
+ this.each(function (el) {
+ children = children.concat(ns(selector, el).items);
+ });
+
+ return ns(children);
+ },
+
+ hide: function () {
+ this.each(function (el) {
+ el.style.display = "none";
+ });
+
+ return this;
+ },
+
+ toggle: function () {
+ this.each(function (el) {
+ el.style.display = el.style.display === "none" ? "" : "none";
+ });
+
+ return this;
+ },
+
+ show: function () {
+ this.each(function (el) {
+ el.style.display = "";
+ });
+
+ return this;
+ },
+
+ addClass: function (className) {
+ this.each(function (el) {
+ var existingClassName = el.className,
+ classNames;
+ if (!existingClassName) {
+ el.className = className;
+ } else {
+ classNames = existingClassName.split(" ");
+ if (classNames.indexOf(className) < 0) {
+ el.className = existingClassName + " " + className;
+ }
+ }
+ });
+
+ return this;
+ },
+
+ removeClass: function (className) {
+ this.each(function (el) {
+ var existingClassName = el.className,
+ classNames, index;
+ if (existingClassName === className) {
+ el.className = "";
+ } else if (existingClassName) {
+ classNames = existingClassName.split(" ");
+ index = classNames.indexOf(className);
+ if (index > 0) {
+ classNames.splice(index, 1);
+ el.className = classNames.join(" ");
+ }
+ }
+ });
+
+ return this;
+ },
+
+ attr: function (name) {
+ if (this.items.length === 0) {
+ return null;
+ }
+
+ return this.items[0].getAttribute(name);
+ },
+
+ on: function (eventName, handler) {
+ this.each(function (el, idx) {
+ var callback = function (e) {
+ e = e || window.event;
+ if (!e.which && e.keyCode) {
+ e.which = e.keyCode; // Normalize IE8 key events
+ }
+ handler.apply(el, [e]);
+ };
+
+ if (el.addEventListener) { // DOM Events
+ el.addEventListener(eventName, callback, false);
+ } else if (el.attachEvent) { // IE8 events
+ el.attachEvent("on" + eventName, callback);
+ } else {
+ el["on" + type] = callback;
+ }
+ });
+
+ return this;
+ },
+
+ click: function (handler) {
+ return this.on("click", handler);
+ },
+
+ keypress: function (handler) {
+ return this.on("keypress", handler);
+ }
+ };
+
+ function frame(el) {
+ ns(".source .collapsible", el).toggle();
+ }
+
+ function expandCollapseButton(el) {
+ var frameId = el.getAttribute("data-frameId");
+ frame(document.getElementById(frameId));
+ if (el.innerText === "+") {
+ el.innerText = "-";
+ }
+ else {
+ el.innerText = "+";
+ }
+ }
+
+ function tab(el) {
+ var unselected = ns("#header .selected").removeClass("selected").attr("id");
+ var selected = ns("#" + el.id).addClass("selected").attr("id");
+
+ ns("#" + unselected + "page").hide();
+ ns("#" + selected + "page").show();
+ }
+
+ ns(".rawExceptionDetails").hide();
+ ns(".collapsible").hide();
+ ns(".page").hide();
+ ns("#stackpage").show();
+
+ ns(".expandCollapseButton")
+ .click(function () {
+ expandCollapseButton(this);
+ })
+ .keypress(function (e) {
+ if (e.which === 13) {
+ expandCollapseButton(this);
+ }
+ });
+
+ ns("#header li")
+ .click(function () {
+ tab(this);
+ })
+ .keypress(function (e) {
+ if (e.which === 13) {
+ tab(this);
+ }
+ });
+
+ ns(".showRawException")
+ .click(function () {
+ var exceptionDetailId = this.getAttribute("data-exceptionDetailId");
+ ns("#" + exceptionDetailId).toggle();
+ });
+})(window); \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPageModel.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPageModel.cs
new file mode 100644
index 0000000000..cb9c213a39
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/Views/ErrorPageModel.cs
@@ -0,0 +1,42 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Extensions.StackTrace.Sources;
+
+namespace Microsoft.AspNetCore.Diagnostics.RazorViews
+{
+ /// <summary>
+ /// Holds data to be displayed on the error page.
+ /// </summary>
+ internal class ErrorPageModel
+ {
+ /// <summary>
+ /// Options for what output to display.
+ /// </summary>
+ public DeveloperExceptionPageOptions Options { get; set; }
+
+ /// <summary>
+ /// Detailed information about each exception in the stack.
+ /// </summary>
+ public IEnumerable<ExceptionDetails> ErrorDetails { get; set; }
+
+ /// <summary>
+ /// Parsed query data.
+ /// </summary>
+ public IQueryCollection Query { get; set; }
+
+ /// <summary>
+ /// Request cookies.
+ /// </summary>
+ public IRequestCookieCollection Cookies { get; set; }
+
+ /// <summary>
+ /// Request headers.
+ /// </summary>
+ public IDictionary<string, StringValues> Headers { get; set; }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerExtensions.cs b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerExtensions.cs
new file mode 100644
index 0000000000..92f3c35765
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerExtensions.cs
@@ -0,0 +1,98 @@
+// 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 Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ public static class ExceptionHandlerExtensions
+ {
+ /// <summary>
+ /// Adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline.
+ /// The request will not be re-executed if the response has already started.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseMiddleware<ExceptionHandlerMiddleware>();
+ }
+
+ /// <summary>
+ /// Adds a middleware to the pipeline that will catch exceptions, log them, reset the request path, and re-execute the request.
+ /// The request will not be re-executed if the response has already started.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="errorHandlingPath"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, string errorHandlingPath)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseExceptionHandler(new ExceptionHandlerOptions
+ {
+ ExceptionHandlingPath = new PathString(errorHandlingPath)
+ });
+ }
+
+ /// <summary>
+ /// Adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline.
+ /// The request will not be re-executed if the response has already started.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="configure"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, Action<IApplicationBuilder> configure)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+ if (configure == null)
+ {
+ throw new ArgumentNullException(nameof(configure));
+ }
+
+ var subAppBuilder = app.New();
+ configure(subAppBuilder);
+ var exceptionHandlerPipeline = subAppBuilder.Build();
+
+ return app.UseExceptionHandler(new ExceptionHandlerOptions
+ {
+ ExceptionHandler = exceptionHandlerPipeline
+ });
+ }
+
+ /// <summary>
+ /// Adds a middleware to the pipeline that will catch exceptions, log them, and re-execute the request in an alternate pipeline.
+ /// The request will not be re-executed if the response has already started.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="options"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseExceptionHandler(this IApplicationBuilder app, ExceptionHandlerOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ return app.UseMiddleware<ExceptionHandlerMiddleware>(Options.Create(options));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerFeature.cs b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerFeature.cs
new file mode 100644
index 0000000000..f01fb6a1ab
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerFeature.cs
@@ -0,0 +1,14 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class ExceptionHandlerFeature : IExceptionHandlerPathFeature
+ {
+ public Exception Error { get; set; }
+
+ public string Path { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs
new file mode 100644
index 0000000000..814c3991e8
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs
@@ -0,0 +1,115 @@
+// 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.Diagnostics;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.Internal;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class ExceptionHandlerMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly ExceptionHandlerOptions _options;
+ private readonly ILogger _logger;
+ private readonly Func<object, Task> _clearCacheHeadersDelegate;
+ private readonly DiagnosticSource _diagnosticSource;
+
+ public ExceptionHandlerMiddleware(
+ RequestDelegate next,
+ ILoggerFactory loggerFactory,
+ IOptions<ExceptionHandlerOptions> options,
+ DiagnosticSource diagnosticSource)
+ {
+ _next = next;
+ _options = options.Value;
+ _logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
+ _clearCacheHeadersDelegate = ClearCacheHeaders;
+ _diagnosticSource = diagnosticSource;
+ if (_options.ExceptionHandler == null)
+ {
+ if (_options.ExceptionHandlingPath == null)
+ {
+ throw new InvalidOperationException(Resources.FormatExceptionHandlerOptions_NotConfiguredCorrectly());
+ }
+ else
+ {
+ _options.ExceptionHandler = _next;
+ }
+ }
+ }
+
+ public async Task Invoke(HttpContext context)
+ {
+ try
+ {
+ await _next(context);
+ }
+ catch (Exception ex)
+ {
+ _logger.UnhandledException(ex);
+ // We can't do anything if the response has already started, just abort.
+ if (context.Response.HasStarted)
+ {
+ _logger.ResponseStartedErrorHandler();
+ throw;
+ }
+
+ PathString originalPath = context.Request.Path;
+ if (_options.ExceptionHandlingPath.HasValue)
+ {
+ context.Request.Path = _options.ExceptionHandlingPath;
+ }
+ try
+ {
+ context.Response.Clear();
+ var exceptionHandlerFeature = new ExceptionHandlerFeature()
+ {
+ Error = ex,
+ Path = originalPath.Value,
+ };
+ context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
+ context.Features.Set<IExceptionHandlerPathFeature>(exceptionHandlerFeature);
+ context.Response.StatusCode = 500;
+ context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);
+
+ await _options.ExceptionHandler(context);
+
+ if (_diagnosticSource.IsEnabled("Microsoft.AspNetCore.Diagnostics.HandledException"))
+ {
+ _diagnosticSource.Write("Microsoft.AspNetCore.Diagnostics.HandledException", new { httpContext = context, exception = ex });
+ }
+
+ // TODO: Optional re-throw? We'll re-throw the original exception by default if the error handler throws.
+ return;
+ }
+ catch (Exception ex2)
+ {
+ // Suppress secondary exceptions, re-throw the original.
+ _logger.ErrorHandlerException(ex2);
+ }
+ finally
+ {
+ context.Request.Path = originalPath;
+ }
+ throw; // Re-throw the original if we couldn't handle it
+ }
+ }
+
+ private Task ClearCacheHeaders(object state)
+ {
+ var response = (HttpResponse)state;
+ response.Headers[HeaderNames.CacheControl] = "no-cache";
+ response.Headers[HeaderNames.Pragma] = "no-cache";
+ response.Headers[HeaderNames.Expires] = "-1";
+ response.Headers.Remove(HeaderNames.ETag);
+ return Task.CompletedTask;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs
new file mode 100644
index 0000000000..65a2fb81e7
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs
@@ -0,0 +1,14 @@
+// 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 Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ public class ExceptionHandlerOptions
+ {
+ public PathString ExceptionHandlingPath { get; set; }
+
+ public RequestDelegate ExceptionHandler { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/Internal/DiagnosticsLoggerExtensions.cs b/src/Middleware/Diagnostics/src/Internal/DiagnosticsLoggerExtensions.cs
new file mode 100644
index 0000000000..347cc622b1
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/Internal/DiagnosticsLoggerExtensions.cs
@@ -0,0 +1,54 @@
+// 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 Microsoft.Extensions.Logging;
+
+namespace Microsoft.AspNetCore.Diagnostics.Internal
+{
+ internal static class DiagnosticsLoggerExtensions
+ {
+ // ExceptionHandlerMiddleware & DeveloperExceptionPageMiddleware
+ private static readonly Action<ILogger, Exception> _unhandledException =
+ LoggerMessage.Define(LogLevel.Error, new EventId(1, "UnhandledException"), "An unhandled exception has occurred while executing the request.");
+
+ // ExceptionHandlerMiddleware
+ private static readonly Action<ILogger, Exception> _responseStartedErrorHandler =
+ LoggerMessage.Define(LogLevel.Warning, new EventId(2, "ResponseStarted"), "The response has already started, the error handler will not be executed.");
+
+ private static readonly Action<ILogger, Exception> _errorHandlerException =
+ LoggerMessage.Define(LogLevel.Error, new EventId(3, "Exception"), "An exception was thrown attempting to execute the error handler.");
+
+ // DeveloperExceptionPageMiddleware
+ private static readonly Action<ILogger, Exception> _responseStartedErrorPageMiddleware =
+ LoggerMessage.Define(LogLevel.Warning, new EventId(2, "ResponseStarted"), "The response has already started, the error page middleware will not be executed.");
+
+ private static readonly Action<ILogger, Exception> _displayErrorPageException =
+ LoggerMessage.Define(LogLevel.Error, new EventId(3, "DisplayErrorPageException"), "An exception was thrown attempting to display the error page.");
+
+ public static void UnhandledException(this ILogger logger, Exception exception)
+ {
+ _unhandledException(logger, exception);
+ }
+
+ public static void ResponseStartedErrorHandler(this ILogger logger)
+ {
+ _responseStartedErrorHandler(logger, null);
+ }
+
+ public static void ErrorHandlerException(this ILogger logger, Exception exception)
+ {
+ _errorHandlerException(logger, exception);
+ }
+
+ public static void ResponseStartedErrorPageMiddleware(this ILogger logger)
+ {
+ _responseStartedErrorPageMiddleware(logger, null);
+ }
+
+ public static void DisplayErrorPageException(this ILogger logger, Exception exception)
+ {
+ _displayErrorPageException(logger, exception);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj b/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj
new file mode 100644
index 0000000000..a5ae405dcd
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/Microsoft.AspNetCore.Diagnostics.csproj
@@ -0,0 +1,30 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>ASP.NET Core middleware for exception handling, exception display pages, and diagnostics information. Includes developer exception page middleware, exception handler middleware, runtime info middleware, status code page middleware, and welcome page middleware</Description>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <NoWarn>$(NoWarn);CS1591</NoWarn>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>aspnetcore;diagnostics</PackageTags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="$(SharedSourceRoot)Diagnostics\*.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics.Abstractions" />
+ <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
+ <Reference Include="Microsoft.AspNetCore.Http.Extensions" />
+ <Reference Include="Microsoft.AspNetCore.WebUtilities" />
+ <Reference Include="Microsoft.Extensions.FileProviders.Physical" />
+ <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
+ <Reference Include="Microsoft.Extensions.Options" />
+ <Reference Include="Microsoft.Extensions.RazorViews.Sources" PrivateAssets="All" />
+ <Reference Include="Microsoft.Extensions.StackTrace.Sources" PrivateAssets="All" />
+ <Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" PrivateAssets="All" />
+ <Reference Include="System.Diagnostics.DiagnosticSource" />
+ <Reference Include="System.Reflection.Metadata" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/src/Properties/AssemblyInfo.cs b/src/Middleware/Diagnostics/src/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..7aa636c259
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/Properties/AssemblyInfo.cs
@@ -0,0 +1,9 @@
+// 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.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Diagnostics.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
diff --git a/src/Middleware/Diagnostics/src/Properties/Resources.Designer.cs b/src/Middleware/Diagnostics/src/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..a9ca25fef3
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/Properties/Resources.Designer.cs
@@ -0,0 +1,618 @@
+// <auto-generated />
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ using System.Globalization;
+ using System.Reflection;
+ using System.Resources;
+
+ internal static class Resources
+ {
+ private static readonly ResourceManager _resourceManager
+ = new ResourceManager("Microsoft.AspNetCore.Diagnostics.Resources", typeof(Resources).GetTypeInfo().Assembly);
+
+ /// <summary>
+ /// You are seeing this page because DiagnosticsPageMiddleware was added to your web application.
+ /// </summary>
+ internal static string DiagnosticsPageHtml_Information
+ {
+ get => GetString("DiagnosticsPageHtml_Information");
+ }
+
+ /// <summary>
+ /// You are seeing this page because DiagnosticsPageMiddleware was added to your web application.
+ /// </summary>
+ internal static string FormatDiagnosticsPageHtml_Information()
+ => GetString("DiagnosticsPageHtml_Information");
+
+ /// <summary>
+ /// Test Error Message
+ /// </summary>
+ internal static string DiagnosticsPageHtml_TestErrorMessage
+ {
+ get => GetString("DiagnosticsPageHtml_TestErrorMessage");
+ }
+
+ /// <summary>
+ /// Test Error Message
+ /// </summary>
+ internal static string FormatDiagnosticsPageHtml_TestErrorMessage()
+ => GetString("DiagnosticsPageHtml_TestErrorMessage");
+
+ /// <summary>
+ /// Test Error Page
+ /// </summary>
+ internal static string DiagnosticsPageHtml_TestErrorSection
+ {
+ get => GetString("DiagnosticsPageHtml_TestErrorSection");
+ }
+
+ /// <summary>
+ /// Test Error Page
+ /// </summary>
+ internal static string FormatDiagnosticsPageHtml_TestErrorSection()
+ => GetString("DiagnosticsPageHtml_TestErrorSection");
+
+ /// <summary>
+ /// Diagnostics Page
+ /// </summary>
+ internal static string DiagnosticsPageHtml_Title
+ {
+ get => GetString("DiagnosticsPageHtml_Title");
+ }
+
+ /// <summary>
+ /// Diagnostics Page
+ /// </summary>
+ internal static string FormatDiagnosticsPageHtml_Title()
+ => GetString("DiagnosticsPageHtml_Title");
+
+ /// <summary>
+ /// Cookies
+ /// </summary>
+ internal static string ErrorPageHtml_CookiesButton
+ {
+ get => GetString("ErrorPageHtml_CookiesButton");
+ }
+
+ /// <summary>
+ /// Cookies
+ /// </summary>
+ internal static string FormatErrorPageHtml_CookiesButton()
+ => GetString("ErrorPageHtml_CookiesButton");
+
+ /// <summary>
+ /// Headers
+ /// </summary>
+ internal static string ErrorPageHtml_HeadersButton
+ {
+ get => GetString("ErrorPageHtml_HeadersButton");
+ }
+
+ /// <summary>
+ /// Headers
+ /// </summary>
+ internal static string FormatErrorPageHtml_HeadersButton()
+ => GetString("ErrorPageHtml_HeadersButton");
+
+ /// <summary>
+ /// No cookie data.
+ /// </summary>
+ internal static string ErrorPageHtml_NoCookieData
+ {
+ get => GetString("ErrorPageHtml_NoCookieData");
+ }
+
+ /// <summary>
+ /// No cookie data.
+ /// </summary>
+ internal static string FormatErrorPageHtml_NoCookieData()
+ => GetString("ErrorPageHtml_NoCookieData");
+
+ /// <summary>
+ /// No header data.
+ /// </summary>
+ internal static string ErrorPageHtml_NoHeaderData
+ {
+ get => GetString("ErrorPageHtml_NoHeaderData");
+ }
+
+ /// <summary>
+ /// No header data.
+ /// </summary>
+ internal static string FormatErrorPageHtml_NoHeaderData()
+ => GetString("ErrorPageHtml_NoHeaderData");
+
+ /// <summary>
+ /// No QueryString data.
+ /// </summary>
+ internal static string ErrorPageHtml_NoQueryStringData
+ {
+ get => GetString("ErrorPageHtml_NoQueryStringData");
+ }
+
+ /// <summary>
+ /// No QueryString data.
+ /// </summary>
+ internal static string FormatErrorPageHtml_NoQueryStringData()
+ => GetString("ErrorPageHtml_NoQueryStringData");
+
+ /// <summary>
+ /// Query
+ /// </summary>
+ internal static string ErrorPageHtml_QueryButton
+ {
+ get => GetString("ErrorPageHtml_QueryButton");
+ }
+
+ /// <summary>
+ /// Query
+ /// </summary>
+ internal static string FormatErrorPageHtml_QueryButton()
+ => GetString("ErrorPageHtml_QueryButton");
+
+ /// <summary>
+ /// Stack
+ /// </summary>
+ internal static string ErrorPageHtml_StackButton
+ {
+ get => GetString("ErrorPageHtml_StackButton");
+ }
+
+ /// <summary>
+ /// Stack
+ /// </summary>
+ internal static string FormatErrorPageHtml_StackButton()
+ => GetString("ErrorPageHtml_StackButton");
+
+ /// <summary>
+ /// Internal Server Error
+ /// </summary>
+ internal static string ErrorPageHtml_Title
+ {
+ get => GetString("ErrorPageHtml_Title");
+ }
+
+ /// <summary>
+ /// Internal Server Error
+ /// </summary>
+ internal static string FormatErrorPageHtml_Title()
+ => GetString("ErrorPageHtml_Title");
+
+ /// <summary>
+ /// An unhandled exception occurred while processing the request.
+ /// </summary>
+ internal static string ErrorPageHtml_UnhandledException
+ {
+ get => GetString("ErrorPageHtml_UnhandledException");
+ }
+
+ /// <summary>
+ /// An unhandled exception occurred while processing the request.
+ /// </summary>
+ internal static string FormatErrorPageHtml_UnhandledException()
+ => GetString("ErrorPageHtml_UnhandledException");
+
+ /// <summary>
+ /// Unknown location
+ /// </summary>
+ internal static string ErrorPageHtml_UnknownLocation
+ {
+ get => GetString("ErrorPageHtml_UnknownLocation");
+ }
+
+ /// <summary>
+ /// Unknown location
+ /// </summary>
+ internal static string FormatErrorPageHtml_UnknownLocation()
+ => GetString("ErrorPageHtml_UnknownLocation");
+
+ /// <summary>
+ /// Value
+ /// </summary>
+ internal static string ErrorPageHtml_ValueColumn
+ {
+ get => GetString("ErrorPageHtml_ValueColumn");
+ }
+
+ /// <summary>
+ /// Value
+ /// </summary>
+ internal static string FormatErrorPageHtml_ValueColumn()
+ => GetString("ErrorPageHtml_ValueColumn");
+
+ /// <summary>
+ /// Variable
+ /// </summary>
+ internal static string ErrorPageHtml_VariableColumn
+ {
+ get => GetString("ErrorPageHtml_VariableColumn");
+ }
+
+ /// <summary>
+ /// Variable
+ /// </summary>
+ internal static string FormatErrorPageHtml_VariableColumn()
+ => GetString("ErrorPageHtml_VariableColumn");
+
+ /// <summary>
+ /// The path must start with a '/'.
+ /// </summary>
+ internal static string Exception_PathMustStartWithSlash
+ {
+ get => GetString("Exception_PathMustStartWithSlash");
+ }
+
+ /// <summary>
+ /// The path must start with a '/'.
+ /// </summary>
+ internal static string FormatException_PathMustStartWithSlash()
+ => GetString("Exception_PathMustStartWithSlash");
+
+ /// <summary>
+ /// Name
+ /// </summary>
+ internal static string RuntimeInfoPage_PackageNameColumnName
+ {
+ get => GetString("RuntimeInfoPage_PackageNameColumnName");
+ }
+
+ /// <summary>
+ /// Name
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_PackageNameColumnName()
+ => GetString("RuntimeInfoPage_PackageNameColumnName");
+
+ /// <summary>
+ /// Path
+ /// </summary>
+ internal static string RuntimeInfoPage_PackagePathColumnName
+ {
+ get => GetString("RuntimeInfoPage_PackagePathColumnName");
+ }
+
+ /// <summary>
+ /// Path
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_PackagePathColumnName()
+ => GetString("RuntimeInfoPage_PackagePathColumnName");
+
+ /// <summary>
+ /// Packages:
+ /// </summary>
+ internal static string RuntimeInfoPage_Packages
+ {
+ get => GetString("RuntimeInfoPage_Packages");
+ }
+
+ /// <summary>
+ /// Packages:
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_Packages()
+ => GetString("RuntimeInfoPage_Packages");
+
+ /// <summary>
+ /// Could not retrieve the list of loaded packages.
+ /// </summary>
+ internal static string RuntimeInfoPage_PackagesFail
+ {
+ get => GetString("RuntimeInfoPage_PackagesFail");
+ }
+
+ /// <summary>
+ /// Could not retrieve the list of loaded packages.
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_PackagesFail()
+ => GetString("RuntimeInfoPage_PackagesFail");
+
+ /// <summary>
+ /// Version
+ /// </summary>
+ internal static string RuntimeInfoPage_PackageVersionColumnName
+ {
+ get => GetString("RuntimeInfoPage_PackageVersionColumnName");
+ }
+
+ /// <summary>
+ /// Version
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_PackageVersionColumnName()
+ => GetString("RuntimeInfoPage_PackageVersionColumnName");
+
+ /// <summary>
+ /// Runtime Version:
+ /// </summary>
+ internal static string RuntimeInfoPage_RuntimeVersion
+ {
+ get => GetString("RuntimeInfoPage_RuntimeVersion");
+ }
+
+ /// <summary>
+ /// Runtime Version:
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_RuntimeVersion()
+ => GetString("RuntimeInfoPage_RuntimeVersion");
+
+ /// <summary>
+ /// Could not determine the runtime version.
+ /// </summary>
+ internal static string RuntimeInfoPage_RuntimeVersionFail
+ {
+ get => GetString("RuntimeInfoPage_RuntimeVersionFail");
+ }
+
+ /// <summary>
+ /// Could not determine the runtime version.
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_RuntimeVersionFail()
+ => GetString("RuntimeInfoPage_RuntimeVersionFail");
+
+ /// <summary>
+ /// Runtime Information
+ /// </summary>
+ internal static string RuntimeInfoPage_Title
+ {
+ get => GetString("RuntimeInfoPage_Title");
+ }
+
+ /// <summary>
+ /// Runtime Information
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_Title()
+ => GetString("RuntimeInfoPage_Title");
+
+ /// <summary>
+ /// Welcome
+ /// </summary>
+ internal static string WelcomeHeader
+ {
+ get => GetString("WelcomeHeader");
+ }
+
+ /// <summary>
+ /// Welcome
+ /// </summary>
+ internal static string FormatWelcomeHeader()
+ => GetString("WelcomeHeader");
+
+ /// <summary>
+ /// Learn more about the Microsoft ASP.NET Core components
+ /// </summary>
+ internal static string WelcomeLearnMicrosoftAspNet
+ {
+ get => GetString("WelcomeLearnMicrosoftAspNet");
+ }
+
+ /// <summary>
+ /// Learn more about the Microsoft ASP.NET Core components
+ /// </summary>
+ internal static string FormatWelcomeLearnMicrosoftAspNet()
+ => GetString("WelcomeLearnMicrosoftAspNet");
+
+ /// <summary>
+ /// Browser
+ /// </summary>
+ internal static string WelcomePageImageText_Browser
+ {
+ get => GetString("WelcomePageImageText_Browser");
+ }
+
+ /// <summary>
+ /// Browser
+ /// </summary>
+ internal static string FormatWelcomePageImageText_Browser()
+ => GetString("WelcomePageImageText_Browser");
+
+ /// <summary>
+ /// Learn More
+ /// </summary>
+ internal static string WelcomePageImageText_LearnMore
+ {
+ get => GetString("WelcomePageImageText_LearnMore");
+ }
+
+ /// <summary>
+ /// Learn More
+ /// </summary>
+ internal static string FormatWelcomePageImageText_LearnMore()
+ => GetString("WelcomePageImageText_LearnMore");
+
+ /// <summary>
+ /// Light Bulb
+ /// </summary>
+ internal static string WelcomePageImageText_LightBulb
+ {
+ get => GetString("WelcomePageImageText_LightBulb");
+ }
+
+ /// <summary>
+ /// Light Bulb
+ /// </summary>
+ internal static string FormatWelcomePageImageText_LightBulb()
+ => GetString("WelcomePageImageText_LightBulb");
+
+ /// <summary>
+ /// Skyline
+ /// </summary>
+ internal static string WelcomePageImageText_Skyline
+ {
+ get => GetString("WelcomePageImageText_Skyline");
+ }
+
+ /// <summary>
+ /// Skyline
+ /// </summary>
+ internal static string FormatWelcomePageImageText_Skyline()
+ => GetString("WelcomePageImageText_Skyline");
+
+ /// <summary>
+ /// Your ASP.NET Core application has been successfully started
+ /// </summary>
+ internal static string WelcomeStarted
+ {
+ get => GetString("WelcomeStarted");
+ }
+
+ /// <summary>
+ /// Your ASP.NET Core application has been successfully started
+ /// </summary>
+ internal static string FormatWelcomeStarted()
+ => GetString("WelcomeStarted");
+
+ /// <summary>
+ /// Your ASP.NET Core application has been successfully started.
+ /// </summary>
+ internal static string WelcomeTitle
+ {
+ get => GetString("WelcomeTitle");
+ }
+
+ /// <summary>
+ /// Your ASP.NET Core application has been successfully started.
+ /// </summary>
+ internal static string FormatWelcomeTitle()
+ => GetString("WelcomeTitle");
+
+ /// <summary>
+ /// An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.
+ /// </summary>
+ internal static string ErrorPageHtml_CompilationException
+ {
+ get => GetString("ErrorPageHtml_CompilationException");
+ }
+
+ /// <summary>
+ /// An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.
+ /// </summary>
+ internal static string FormatErrorPageHtml_CompilationException()
+ => GetString("ErrorPageHtml_CompilationException");
+
+ /// <summary>
+ /// Operating System:
+ /// </summary>
+ internal static string RuntimeInfoPage_OperatingSystem
+ {
+ get => GetString("RuntimeInfoPage_OperatingSystem");
+ }
+
+ /// <summary>
+ /// Operating System:
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_OperatingSystem()
+ => GetString("RuntimeInfoPage_OperatingSystem");
+
+ /// <summary>
+ /// Runtime Architecture:
+ /// </summary>
+ internal static string RuntimeInfoPage_RuntimeArchitecture
+ {
+ get => GetString("RuntimeInfoPage_RuntimeArchitecture");
+ }
+
+ /// <summary>
+ /// Runtime Architecture:
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_RuntimeArchitecture()
+ => GetString("RuntimeInfoPage_RuntimeArchitecture");
+
+ /// <summary>
+ /// Runtime Type:
+ /// </summary>
+ internal static string RuntimeInfoPage_RuntimeType
+ {
+ get => GetString("RuntimeInfoPage_RuntimeType");
+ }
+
+ /// <summary>
+ /// Runtime Type:
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_RuntimeType()
+ => GetString("RuntimeInfoPage_RuntimeType");
+
+ /// <summary>
+ /// Could not determine the operating system.
+ /// </summary>
+ internal static string RuntimeInfoPage_OperatingSystemFail
+ {
+ get => GetString("RuntimeInfoPage_OperatingSystemFail");
+ }
+
+ /// <summary>
+ /// Could not determine the operating system.
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_OperatingSystemFail()
+ => GetString("RuntimeInfoPage_OperatingSystemFail");
+
+ /// <summary>
+ /// Could not determine the runtime architecture.
+ /// </summary>
+ internal static string RuntimeInfoPage_RuntimeArchitectureFail
+ {
+ get => GetString("RuntimeInfoPage_RuntimeArchitectureFail");
+ }
+
+ /// <summary>
+ /// Could not determine the runtime architecture.
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_RuntimeArchitectureFail()
+ => GetString("RuntimeInfoPage_RuntimeArchitectureFail");
+
+ /// <summary>
+ /// Could not determine the runtime type.
+ /// </summary>
+ internal static string RuntimeInfoPage_RuntimeTypeFail
+ {
+ get => GetString("RuntimeInfoPage_RuntimeTypeFail");
+ }
+
+ /// <summary>
+ /// Could not determine the runtime type.
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_RuntimeTypeFail()
+ => GetString("RuntimeInfoPage_RuntimeTypeFail");
+
+ /// <summary>
+ /// Environment:
+ /// </summary>
+ internal static string RuntimeInfoPage_Environment
+ {
+ get => GetString("RuntimeInfoPage_Environment");
+ }
+
+ /// <summary>
+ /// Environment:
+ /// </summary>
+ internal static string FormatRuntimeInfoPage_Environment()
+ => GetString("RuntimeInfoPage_Environment");
+
+ /// <summary>
+ /// An error occurred when configuring the exception handler middleware. Either the 'ExceptionHandlingPath' or the 'ExceptionHandler' option must be set in 'UseExceptionHandler()'.
+ /// </summary>
+ internal static string ExceptionHandlerOptions_NotConfiguredCorrectly
+ {
+ get => GetString("ExceptionHandlerOptions_NotConfiguredCorrectly");
+ }
+
+ /// <summary>
+ /// An error occurred when configuring the exception handler middleware. Either the 'ExceptionHandlingPath' or the 'ExceptionHandler' option must be set in 'UseExceptionHandler()'.
+ /// </summary>
+ internal static string FormatExceptionHandlerOptions_NotConfiguredCorrectly()
+ => GetString("ExceptionHandlerOptions_NotConfiguredCorrectly");
+
+ private static string GetString(string name, params string[] formatterNames)
+ {
+ var value = _resourceManager.GetString(name);
+
+ System.Diagnostics.Debug.Assert(value != null);
+
+ if (formatterNames != null)
+ {
+ for (var i = 0; i < formatterNames.Length; i++)
+ {
+ value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
+ }
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/Resources.resx b/src/Middleware/Diagnostics/src/Resources.resx
new file mode 100644
index 0000000000..93a94abe1a
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/Resources.resx
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="DiagnosticsPageHtml_Information" xml:space="preserve">
+ <value>You are seeing this page because DiagnosticsPageMiddleware was added to your web application.</value>
+ </data>
+ <data name="DiagnosticsPageHtml_TestErrorMessage" xml:space="preserve">
+ <value>Test Error Message</value>
+ </data>
+ <data name="DiagnosticsPageHtml_TestErrorSection" xml:space="preserve">
+ <value>Test Error Page</value>
+ </data>
+ <data name="DiagnosticsPageHtml_Title" xml:space="preserve">
+ <value>Diagnostics Page</value>
+ </data>
+ <data name="ErrorPageHtml_CookiesButton" xml:space="preserve">
+ <value>Cookies</value>
+ <comment>as in http request cookies</comment>
+ </data>
+ <data name="ErrorPageHtml_HeadersButton" xml:space="preserve">
+ <value>Headers</value>
+ <comment>as in http request headers</comment>
+ </data>
+ <data name="ErrorPageHtml_NoCookieData" xml:space="preserve">
+ <value>No cookie data.</value>
+ </data>
+ <data name="ErrorPageHtml_NoHeaderData" xml:space="preserve">
+ <value>No header data.</value>
+ </data>
+ <data name="ErrorPageHtml_NoQueryStringData" xml:space="preserve">
+ <value>No QueryString data.</value>
+ </data>
+ <data name="ErrorPageHtml_QueryButton" xml:space="preserve">
+ <value>Query</value>
+ <comment>as in a request url query string</comment>
+ </data>
+ <data name="ErrorPageHtml_StackButton" xml:space="preserve">
+ <value>Stack</value>
+ <comment>as in an exception's call stack</comment>
+ </data>
+ <data name="ErrorPageHtml_Title" xml:space="preserve">
+ <value>Internal Server Error</value>
+ </data>
+ <data name="ErrorPageHtml_UnhandledException" xml:space="preserve">
+ <value>An unhandled exception occurred while processing the request.</value>
+ </data>
+ <data name="ErrorPageHtml_UnknownLocation" xml:space="preserve">
+ <value>Unknown location</value>
+ <comment>when a stack source code location is not known</comment>
+ </data>
+ <data name="ErrorPageHtml_ValueColumn" xml:space="preserve">
+ <value>Value</value>
+ <comment>as in dictionary value</comment>
+ </data>
+ <data name="ErrorPageHtml_VariableColumn" xml:space="preserve">
+ <value>Variable</value>
+ <comment>as in dictionary key</comment>
+ </data>
+ <data name="Exception_PathMustStartWithSlash" xml:space="preserve">
+ <value>The path must start with a '/'.</value>
+ </data>
+ <data name="RuntimeInfoPage_PackageNameColumnName" xml:space="preserve">
+ <value>Name</value>
+ </data>
+ <data name="RuntimeInfoPage_PackagePathColumnName" xml:space="preserve">
+ <value>Path</value>
+ </data>
+ <data name="RuntimeInfoPage_Packages" xml:space="preserve">
+ <value>Packages:</value>
+ </data>
+ <data name="RuntimeInfoPage_PackagesFail" xml:space="preserve">
+ <value>Could not retrieve the list of loaded packages.</value>
+ </data>
+ <data name="RuntimeInfoPage_PackageVersionColumnName" xml:space="preserve">
+ <value>Version</value>
+ </data>
+ <data name="RuntimeInfoPage_RuntimeVersion" xml:space="preserve">
+ <value>Runtime Version:</value>
+ </data>
+ <data name="RuntimeInfoPage_RuntimeVersionFail" xml:space="preserve">
+ <value>Could not determine the runtime version.</value>
+ </data>
+ <data name="RuntimeInfoPage_Title" xml:space="preserve">
+ <value>Runtime Information</value>
+ </data>
+ <data name="WelcomeHeader" xml:space="preserve">
+ <value>Welcome</value>
+ </data>
+ <data name="WelcomeLearnMicrosoftAspNet" xml:space="preserve">
+ <value>Learn more about the Microsoft ASP.NET Core components</value>
+ </data>
+ <data name="WelcomePageImageText_Browser" xml:space="preserve">
+ <value>Browser</value>
+ </data>
+ <data name="WelcomePageImageText_LearnMore" xml:space="preserve">
+ <value>Learn More</value>
+ </data>
+ <data name="WelcomePageImageText_LightBulb" xml:space="preserve">
+ <value>Light Bulb</value>
+ </data>
+ <data name="WelcomePageImageText_Skyline" xml:space="preserve">
+ <value>Skyline</value>
+ </data>
+ <data name="WelcomeStarted" xml:space="preserve">
+ <value>Your ASP.NET Core application has been successfully started</value>
+ </data>
+ <data name="WelcomeTitle" xml:space="preserve">
+ <value>Your ASP.NET Core application has been successfully started.</value>
+ </data>
+ <data name="ErrorPageHtml_CompilationException" xml:space="preserve">
+ <value>An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately.</value>
+ </data>
+ <data name="RuntimeInfoPage_OperatingSystem" xml:space="preserve">
+ <value>Operating System:</value>
+ </data>
+ <data name="RuntimeInfoPage_RuntimeArchitecture" xml:space="preserve">
+ <value>Runtime Architecture:</value>
+ </data>
+ <data name="RuntimeInfoPage_RuntimeType" xml:space="preserve">
+ <value>Runtime Type:</value>
+ </data>
+ <data name="RuntimeInfoPage_OperatingSystemFail" xml:space="preserve">
+ <value>Could not determine the operating system.</value>
+ </data>
+ <data name="RuntimeInfoPage_RuntimeArchitectureFail" xml:space="preserve">
+ <value>Could not determine the runtime architecture.</value>
+ </data>
+ <data name="RuntimeInfoPage_RuntimeTypeFail" xml:space="preserve">
+ <value>Could not determine the runtime type.</value>
+ </data>
+ <data name="RuntimeInfoPage_Environment" xml:space="preserve">
+ <value>Environment:</value>
+ </data>
+ <data name="ExceptionHandlerOptions_NotConfiguredCorrectly" xml:space="preserve">
+ <value>An error occurred when configuring the exception handler middleware. Either the 'ExceptionHandlingPath' or the 'ExceptionHandler' option must be set in 'UseExceptionHandler()'.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeContext.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeContext.cs
new file mode 100644
index 0000000000..849aee6994
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeContext.cs
@@ -0,0 +1,24 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class StatusCodeContext
+ {
+ public StatusCodeContext(HttpContext context, StatusCodePagesOptions options, RequestDelegate next)
+ {
+ HttpContext = context;
+ Options = options;
+ Next = next;
+ }
+
+ public HttpContext HttpContext { get; private set; }
+
+ public StatusCodePagesOptions Options { get; private set; }
+
+ public RequestDelegate Next { get; private set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesExtensions.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesExtensions.cs
new file mode 100644
index 0000000000..1a2707a1ed
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesExtensions.cs
@@ -0,0 +1,206 @@
+// 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.Globalization;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ public static class StatusCodePagesExtensions
+ {
+ /// <summary>
+ /// Adds a StatusCodePages middleware with the given options that checks for responses with status codes
+ /// between 400 and 599 that do not have a body.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="options"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, StatusCodePagesOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ return app.UseMiddleware<StatusCodePagesMiddleware>(Options.Create(options));
+ }
+
+ /// <summary>
+ /// Adds a StatusCodePages middleware with a default response handler that checks for responses with status codes
+ /// between 400 and 599 that do not have a body.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseMiddleware<StatusCodePagesMiddleware>();
+ }
+
+ /// <summary>
+ /// Adds a StatusCodePages middleware with the specified handler that checks for responses with status codes
+ /// between 400 and 599 that do not have a body.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="handler"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, Func<StatusCodeContext, Task> handler)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ return app.UseStatusCodePages(new StatusCodePagesOptions
+ {
+ HandleAsync = handler
+ });
+ }
+
+ /// <summary>
+ /// Adds a StatusCodePages middleware with the specified response body to send. This may include a '{0}' placeholder for the status code.
+ /// The middleware checks for responses with status codes between 400 and 599 that do not have a body.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="contentType"></param>
+ /// <param name="bodyFormat"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, string contentType, string bodyFormat)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseStatusCodePages(context =>
+ {
+ var body = string.Format(CultureInfo.InvariantCulture, bodyFormat, context.HttpContext.Response.StatusCode);
+ context.HttpContext.Response.ContentType = contentType;
+ return context.HttpContext.Response.WriteAsync(body);
+ });
+ }
+
+ /// <summary>
+ /// Adds a StatusCodePages middleware to the pipeline. Specifies that responses should be handled by redirecting
+ /// with the given location URL template. This may include a '{0}' placeholder for the status code. URLs starting
+ /// with '~' will have PathBase prepended, where any other URL will be used as is.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="locationFormat"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseStatusCodePagesWithRedirects(this IApplicationBuilder app, string locationFormat)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ if (locationFormat.StartsWith("~"))
+ {
+ locationFormat = locationFormat.Substring(1);
+ return app.UseStatusCodePages(context =>
+ {
+ var location = string.Format(CultureInfo.InvariantCulture, locationFormat, context.HttpContext.Response.StatusCode);
+ context.HttpContext.Response.Redirect(context.HttpContext.Request.PathBase + location);
+ return Task.CompletedTask;
+ });
+ }
+ else
+ {
+ return app.UseStatusCodePages(context =>
+ {
+ var location = string.Format(CultureInfo.InvariantCulture, locationFormat, context.HttpContext.Response.StatusCode);
+ context.HttpContext.Response.Redirect(location);
+ return Task.CompletedTask;
+ });
+ }
+ }
+
+ /// <summary>
+ /// Adds a StatusCodePages middleware to the pipeline with the specified alternate middleware pipeline to execute
+ /// to generate the response body.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="configuration"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseStatusCodePages(this IApplicationBuilder app, Action<IApplicationBuilder> configuration)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ var builder = app.New();
+ configuration(builder);
+ var tangent = builder.Build();
+ return app.UseStatusCodePages(context => tangent(context.HttpContext));
+ }
+
+ /// <summary>
+ /// Adds a StatusCodePages middleware to the pipeline. Specifies that the response body should be generated by
+ /// re-executing the request pipeline using an alternate path. This path may contain a '{0}' placeholder of the status code.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="pathFormat"></param>
+ /// <param name="queryFormat"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseStatusCodePagesWithReExecute(
+ this IApplicationBuilder app,
+ string pathFormat,
+ string queryFormat = null)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseStatusCodePages(async context =>
+ {
+ var newPath = new PathString(
+ string.Format(CultureInfo.InvariantCulture, pathFormat, context.HttpContext.Response.StatusCode));
+ var formatedQueryString = queryFormat == null ? null :
+ string.Format(CultureInfo.InvariantCulture, queryFormat, context.HttpContext.Response.StatusCode);
+ var newQueryString = queryFormat == null ? QueryString.Empty : new QueryString(formatedQueryString);
+
+ var originalPath = context.HttpContext.Request.Path;
+ var originalQueryString = context.HttpContext.Request.QueryString;
+ // Store the original paths so the app can check it.
+ context.HttpContext.Features.Set<IStatusCodeReExecuteFeature>(new StatusCodeReExecuteFeature()
+ {
+ OriginalPathBase = context.HttpContext.Request.PathBase.Value,
+ OriginalPath = originalPath.Value,
+ OriginalQueryString = originalQueryString.HasValue ? originalQueryString.Value : null,
+ });
+
+ context.HttpContext.Request.Path = newPath;
+ context.HttpContext.Request.QueryString = newQueryString;
+ try
+ {
+ await context.Next(context.HttpContext);
+ }
+ finally
+ {
+ context.HttpContext.Request.QueryString = originalQueryString;
+ context.HttpContext.Request.Path = originalPath;
+ context.HttpContext.Features.Set<IStatusCodeReExecuteFeature>(null);
+ }
+ });
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesFeature.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesFeature.cs
new file mode 100644
index 0000000000..093ca59521
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesFeature.cs
@@ -0,0 +1,13 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// Represents the Status code pages feature.
+ /// </summary>
+ public class StatusCodePagesFeature : IStatusCodePagesFeature
+ {
+ public bool Enabled { get; set; } = true;
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs
new file mode 100644
index 0000000000..fca9e4dec7
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesMiddleware.cs
@@ -0,0 +1,55 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class StatusCodePagesMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly StatusCodePagesOptions _options;
+
+ public StatusCodePagesMiddleware(RequestDelegate next, IOptions<StatusCodePagesOptions> options)
+ {
+ _next = next;
+ _options = options.Value;
+ if (_options.HandleAsync == null)
+ {
+ throw new ArgumentException("Missing options.HandleAsync implementation.");
+ }
+ }
+
+ public async Task Invoke(HttpContext context)
+ {
+ var statusCodeFeature = new StatusCodePagesFeature();
+ context.Features.Set<IStatusCodePagesFeature>(statusCodeFeature);
+
+ await _next(context);
+
+ if (!statusCodeFeature.Enabled)
+ {
+ // Check if the feature is still available because other middleware (such as a web API written in MVC) could
+ // have disabled the feature to prevent HTML status code responses from showing up to an API client.
+ return;
+ }
+
+ // Do nothing if a response body has already been provided.
+ if (context.Response.HasStarted
+ || context.Response.StatusCode < 400
+ || context.Response.StatusCode >= 600
+ || context.Response.ContentLength.HasValue
+ || !string.IsNullOrEmpty(context.Response.ContentType))
+ {
+ return;
+ }
+
+ var statusCodeContext = new StatusCodeContext(context, _options, _next);
+ await _options.HandleAsync(statusCodeContext);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesOptions.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesOptions.cs
new file mode 100644
index 0000000000..c5640c2250
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodePagesOptions.cs
@@ -0,0 +1,48 @@
+// 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.Globalization;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.WebUtilities;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// Options for StatusCodePagesMiddleware.
+ /// </summary>
+ public class StatusCodePagesOptions
+ {
+ public StatusCodePagesOptions()
+ {
+ HandleAsync = context =>
+ {
+ // TODO: Render with a pre-compiled html razor view.
+ var statusCode = context.HttpContext.Response.StatusCode;
+
+ var body = BuildResponseBody(statusCode);
+
+ context.HttpContext.Response.ContentType = "text/plain";
+ return context.HttpContext.Response.WriteAsync(body);
+ };
+ }
+
+ private string BuildResponseBody(int httpStatusCode)
+ {
+ // Note the 500 spaces are to work around an IE 'feature'
+ var internetExplorerWorkaround = new string(' ', 500);
+
+ var reasonPhrase = ReasonPhrases.GetReasonPhrase(httpStatusCode);
+
+ return string.Format(CultureInfo.InvariantCulture, "Status Code: {0}{1}{2}{3}",
+ httpStatusCode,
+ string.IsNullOrWhiteSpace(reasonPhrase) ? "" : "; ",
+ reasonPhrase,
+ internetExplorerWorkaround);
+ }
+
+ public Func<StatusCodeContext, Task> HandleAsync { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeReExecuteFeature.cs b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeReExecuteFeature.cs
new file mode 100644
index 0000000000..8b25d24385
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/StatusCodePage/StatusCodeReExecuteFeature.cs
@@ -0,0 +1,14 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class StatusCodeReExecuteFeature : IStatusCodeReExecuteFeature
+ {
+ public string OriginalPath { get; set; }
+
+ public string OriginalPathBase { get; set; }
+
+ public string OriginalQueryString { get; set; }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cs b/src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cs
new file mode 100644
index 0000000000..991c601bb6
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cs
@@ -0,0 +1,373 @@
+namespace Microsoft.AspNetCore.Diagnostics.Views
+{
+#line 1 "WelcomePage.cshtml"
+using System
+
+#line default
+#line hidden
+ ;
+#line 2 "WelcomePage.cshtml"
+using Microsoft.AspNetCore.Diagnostics
+
+#line default
+#line hidden
+ ;
+ using System.Threading.Tasks;
+
+ [Obsolete("This type is for internal use only and will be removed in a future version.")]
+ public class WelcomePage : Microsoft.AspNetCore.DiagnosticsViewPage.Views.BaseView
+ {
+ #line hidden
+ public WelcomePage()
+ {
+ }
+
+ #pragma warning disable 1998
+ public override async Task ExecuteAsync()
+ {
+#line 3 "WelcomePage.cshtml"
+
+ Response.ContentType = "text/html";
+
+#line default
+#line hidden
+
+ WriteLiteral("<!DOCTYPE html>\r\n<html");
+ BeginWriteAttribute("lang", " lang=\"", 126, "\"", 208, 1);
+#line 7 "WelcomePage.cshtml"
+WriteAttributeValue("", 133, System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, 133, 75, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(">\r\n<head>\r\n <meta charset=\"utf-8\" />\r\n <title>");
+#line 10 "WelcomePage.cshtml"
+ Write(Resources.WelcomeTitle);
+
+#line default
+#line hidden
+ WriteLiteral(@"</title>
+ <style type=""text/css"">
+ @font-face {
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ }
+
+ body {
+ background-color: #00abec;
+ color: #fff;
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-size: 18px;
+ margin: 0;
+ padding: 0;
+ }
+
+ .content {
+ position: absolute;
+ left: 50px;
+ top: 38px;
+ width: 440px;
+ }
+
+ .content .azureLogo {
+ margin: 0 0 65px 0;
+ }
+
+ .content .bodyHeadline {
+ margin: 35px 0 0;
+ font-size: 40px;
+ line-height: 43px;
+ }
+
+ .content .bodyContent {
+ margin: 10px 0 30px 0;
+ line-height: 22px;
+ }
+
+ .content .bodyContent a {
+ ");
+ WriteLiteral(@" color: #fff;
+ text-decoration: none;
+ }
+
+ .content .bodyContent a:hover {
+ opacity: .7;
+ }
+
+ .content .bodyCTA {
+ color: #fff;
+ display: block;
+ line-height: 30px;
+ height: 29px;
+ width: 230px;
+ cursor: pointer;
+ text-decoration: none;
+ position: relative;
+ }
+
+ .content .bodyCTA.longer {
+ margin-top: 10px;
+ width: 440px;
+ }
+
+ .content .bodyCTA div {
+ position: absolute;
+ overflow: hidden;
+ width: 29px;
+ height: 29px;
+ float: right;
+ top: 0;
+ right: 0;
+ }
+
+ .content .bodyCTA div img {
+ ");
+ WriteLiteral(@" position: absolute;
+ top: 0;
+ left: 0;
+ border: 0;
+ }
+
+ .content .bodyCTA:hover div img {
+ left: -29px;
+ }
+
+ .content .bodyCTA:hover {
+ opacity: .7;
+ }
+
+ .wrapper {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ min-width: 1200px;
+ }
+
+ .innerwrapper {
+ width: 384px;
+ height: 100%;
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ .browser {
+ position: absolute;
+ display: block;
+ top: 400px;
+ width: 384px;
+ height: 305px;
+ cursor: default;
+ z-index: 10;
+ }
+
+ .browser div {
+ width: 384px;
+ height: 305px;
+ position: absol");
+ WriteLiteral(@"ute;
+ top: 40px;
+ left: 100px;
+ font-size: 200px;
+ text-align: left;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .bulb {
+ position: fixed;
+ margin-left: 20px;
+ top: 0;
+ }
+
+ .light {
+ position: fixed;
+ margin-left: 53px;
+ top: 0;
+ opacity: 1;
+ }
+
+ .bottom {
+ position: fixed;
+ bottom: 0;
+ margin-right: auto;
+ margin-left: -303px;
+ z-index: -1;
+ height: 202px;
+ }
+ </style>
+ <script>
+ </script>
+</head>
+<body>
+ <div class=""wrapper"">
+ <div class=""innerwrapper"">
+ <div class=""light first"">
+ ");
+ WriteLiteral(@" <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARIAAAESCAYAAAAxN1ojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACKhJREFUeNrs3TFPG0kYgOE1kou4IIVTUITiXJAiFFxBk/8vGopQQBEKKFyci7jAhRsX3E4YB3I5Asva3p2Z55EspJNOOq/3Xn8zu7arCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADI1MAhILi/v9+v/wwb/murwWCwcPQQknJCMa7/jOLj3X/+bsKqfiye/F3Gx6KOzcorICSkGY0P9WM/hmK/4/+kZYxLeHwXFyGh3+EYx0cK1lGZh4ewCAm7D0fYyziIjxCOYQZPaxajMqujsvQqCwnbj8dB5k83TCtTURESNheQdTgOCz0EsxiUqbNBSGg+fUzqx8dqc1dVUreKU8qtKUVI+HNAQjSOCp4+mkwpN3VQ5g6FkPAYkHGMh4A0E0LyTVCEpPSAhPs7jqt0LtkKCkJiCVNEUC7dti8kuQdkvYl65GhsVdiUvXKTm5DkGJFwCfdz5SrMroSIXNcxuXEohCSXZcxJZR+kK2GZ89VyR0hSjsh6GTN0NDp3EycUyx0hMYVgOhGSMiJyECNiCumvMJl8cxiEpI8BCeEIm6ku6aYhXCo+t9QRkr4tZU6r7r9AiGZCRM4sdYSkDxEZx4hYyqTrwqeLhaTLiIRlzIkjkYVpHZMLh0FIdh2REBD7IXmZxenEvomQbD0gNlXzFvZLzsRESLYdkS+VTdUSYnLuC5SERERoyxUdIRERxERIRAQxERIRQUzKsucQiAgvCufC3/GcQEga+SwiPBHOhS9iIiRNphE3m/FcTE4dBiF5TUT8LAR/Mo5vNAjJsxEJH8BzkvCSw/iGg5D8FpGRsZUGTuIbD0LyMyLDylcB0NypzVchecoVGt5iaIoVkvU");
+ WriteLiteral(@"0Er5j1XqXtwqbr5+EpOyIrL/tHdo4Kn2/pPSJxLe9s7FzqeT9kmJDEn+8yq47m7L+YXghKWxJ44e82bRJqUucUicSSxoscYSk1TRyYEnDlpc4EyHJOyLrL26GbTqKy2chyXUNG98xYBfLZyHJcBqxwcoujUvaeC1pIhERdu1YSPKaRsLnaNwGz67tl/J1A6VMJMfOaUzCQtJmGgnrVJd76cqohKmkhInEkgZTiZC0mkZGQoKpREi8E2AyFpJOp5GhaYQeyfq+kpwnkolzF1OJkLT10XlL30KS6yeDswxJ/ISvz9RgKhGSVg6cr/TUX0KSxjRik5U+G8WPbAiJaQQsb4QEnKNC8sKyRkiwvBESpcfyRkiEBIo7V3MLia8LIKXlTTb3OmUTkvg5Br9Vg6lESFr54LzEBC0kXhSEREi8KNDQMJfLwFmEpNQfbsaSXEi8GBCYSLwY0Np7IREScO5mFBJfYkSyctjj2/MigDdCE4lpBOewkHgRIP19khxC8s55SOKS/4yYiQS6Z7PVRAKYSKAHUv/MzZ6XEHoh6X2SpEOS4++DgJCoOBS5RLe0ASEREkBIACEBEBJASAAhAYQEQEgAIQGEBHi0EhIHH9q6E5KODAaDhfMPhASwtPECgOk6j5BY3oCQmEgoXvJvhiYS8GYoJLWl85DEzYVESMBEYmkDnbtL/QkkH5LBYLAyleDNUEi8EJRsGd8MhURIoOxzN5eQfHc+kqh5Dk/CRALdusvhSWQRkrjGFBNSPHdNJJY3YFmTW0jmzksSMxMSIQFTdG4hifskYkIqljl9w19u35A2c35ighYSIaEUWZ2rWYWkHhXDZ25cBqbvVvW5KiQ9N3WeYhoREi8SQiIkvVjeuHpDXy1zW9bkOpFY3mAaEZKNTCUhJL5dnj66FRJTCbQxj0tvIVF+eLObXJ9YtiGJ5XcFh77IcpO1hIkk63cAknOd85PLOiTxS2NcCqZrq3gBQEgS9s15TMey36/LPiSmErqeRkpYYu8V8mKaSuhsGsn");
+ WriteLiteral(@"hd2uExFRCd8KVmiLexPYKelFNJezadSlPtJiQxKnE3a7syiL3KzWlTiTrdwifwWEXLkt6skWFJN7t6tZ5tm2ayw9fCcnzMQl7Jb6OkW0JE+9VaU96r9AX+9L5zpZclXC5V0iqnxuvPofDps1L2mA1kTwIG69L5z4bXNJclPrkiw1JHD/Pnf9syEWuX1okJC/HZFEVdNMQWzPL+btGhOR1MQlXcdw+z1stS17SCMmvzis3qtHcj+VxiVdphOT/p5JwIpw5EjR0FZfHxROSx5gsjKg0MC31Uq+QvByTcGI4OXhJuF/Em46Q/DEm4QSx+cpzwuTqtgEheZXzyudx+J3NVSFpNJWsN1/FhKcROSv5pjMheXtMvlYuC/MYEW8sQvKmmCziZCImZRMRIRETWrkQkVf8f+IQvM79/f1+/edL/Rg6GsUsZ85L+6YzIdldTE7rx8jRyD4iljNCstWYDONksu9oiAhC0jYmYTIZOxpZWcTljEu8QrLToJzUfw4diSzMKzebCUmHMZnUfz47Ekmb+uyMkPQhJuO41HFFJy0/fjrCp3iFpE8xsQmblmVcythUFZJeBuVT/efIkei18P2qF/ZDhCSFpU7YiHW/iaWMkNB6qRMmk4mj0QvzqvCfjBAS0wmmECERkx/TyaSyd7Jr06rQ3+IVkryDMorTiTtitytcibn0gTshKWG5c1y5VLxpYf/j2jJGSEoLymFc7tg/aScsXW7jryYiJILiaDQPSP24sQ8iJPwalPCwh/KKJUz18OPdAiIkPBOUcQyKTxb/ah6nj5lDISS8PijDGJO/Cl72hOkjhOPWzWRCQvuo7MeoHBQQlVWMx8z0ISSIingICYlEZRSDMo6PlL4TZR4f//g4v5DQv2nlQ/Vws9v7qj83vYX9jUUMx527ToWE9OIyjkugUfV4aXlbl5gXcZkyj3/vwj9zmVZIyH+CWS+H3jdYGq0j8XAimTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ WriteLiteral("AAAAAAAAAAAAAAHL0rwADANq3ok68n5URAAAAAElFTkSuQmCC\"");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 7503, "\"", 7571, 1);
+#line 169 "WelcomePage.cshtml"
+WriteAttributeValue("", 7530, Resources.WelcomePageImageText_LightBulb, 7530, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 7572, "\"", 7621, 1);
+#line 169 "WelcomePage.cshtml"
+WriteAttributeValue("", 7580, Resources.WelcomePageImageText_LightBulb, 7580, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""274"" height=""274"" /></div>
+ <div class=""browser"" style=""opacity: 1; visible: true;"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAAExCAYAAAB1UXVvAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAGcxJREFUeNrsnUuPHcd5hqvJIU0bkcJQgkXGMOgYshHJsM0MhMBwEFlxsjDsjQBvsrQRL7LzT0j0C5xFFgogAzSQRTYGnEUCb+IwCWIohkFScSwnsayQkiwOb8O5cc45fS6d/nou6jOnL1Xd1dWXeh7iiNTMmZ4zfaa+t7737aoOVAZ/9q0/Px//9VL8uBI/vnT44ZcUAAB0nVupxxvx49p3X3v1ZtYTg4zC/5348XL8OM95BAAYBCIAr8RC8INMAYiLv8z2/5nCDwAwWK7GIvDNo/85nZr536D4AwAMmivr6y8EN67/9Jr8z6nDD36H4g8A4AV/EU/6P5EIwOHs/xucEwAAb/j2UQfwEucCAMArkrq/pg4u9WyMvQ//lrpz4XeO/z7Jhd076onRI3Vx8//UUzt3eFsAAJrnirg/IgBfsn3k8Mw59cuPrSePh09cKnxuWhTOzsbq8t031fpbP1K/EYsCAAA0JwIiANbCXyn8P7/8B+q/PvFFFa6dM//6tQ+E41LcEbz4s+8jBAAAzXD+9Pr6C6/aONLtZ55X//j731LvPf0pNT+1Vvt4Yhn9PBaSIP4jYgAAAFb5nzUbR3n9ua/FM/8vNvIKrz/75cQm+pMbf6vOTse8ZQAAljhV9wD/+tmvN1b8jxABkO5CLCYAAOiAAEjxF7/eBRImIwIAAB0QgBvP/rGz4n9SBAAAoCUBEEtGvPk2EBEQ8QEAgBYEQKyfNhHxefjkJd49AIAaGF8FJLNvuUSzbf7jd7+mvvqT17");
+ WriteLiteral(@"Se+/nPf453GgC84Y03/rMZAZBFXlW4cv60evm3z658/Afvh+rm1tz4eGJDyYM1AgAA1TCygCT0rbLC9wsX1qwW//TrAQAABwJw+5nnjL+BFP6vXDxjvfgfvJ7neQcBAJwIwEfNCq4Uf7F+mij+gnQjWTuMAgCARQEwLbRZxX88j9Srb0+sFP8jNi58kncRAKBJATC58iev+F+9HaqN8cLqD/DwyYu8iwAAFdC+CkhHAM6dDtQ3Lp9VF8+t6orU/awsQIertya5nwvXPsy7CADQpADUKf7C+TNB8gAAgG5wytaBvvLMmdzi3ySEwAAALQvAD+9O1X/vzp3/ACwEAwCohjULSELev3s3zL30c2saJQ8AAOiZAOjem1eu8VdqVQTEHXr94cx6l3B2NuJdBACogLYFdGH3jvZBsxZ6SUj8px/P7g7q8NTOBu8iAECTAvDUzp14tq1/T14Rgdc3Zysfz7OIqnJx823eRQCAJgVAuHz3TaOD/3BjemgJNSMCIkiEwAAALgTg3i+Mv4FYQXkiILuE1sFUkAAA4AOMKrAUXAmDTW8Ic5AHhCtbQsvK4PNng6RTqML6Wz/Sep7uzREAABCAkqJb5ZaQIgI3t+xdsfOpX1/XvjIJAABWMV4IJoX3KYMrgppAvH/d2T8AAFgSAOEPf/b9Vl/0+lv/xOwfAKANAZBLQl9sSQQu33tTfebWj3nnAADaEABBrCB5uESspxdb7j4AALwXAEGKsSsRkOL/1Z+8ps5Ox7xrAABtC8CRCDQ9KxeRofgDANjFym6gUqBlr6B/++zX1cMnLll7cXK1zxd+8Q/OrSYAAATAAAmGX/73v1a//Ni6uv7sl40Xi53kM7d/nFztw6wfAKDjApDuBuQhQnD7mefU7Y8+ry8icRdx9PUUfgCAngnASSEQ5LaNGxc+efzvI8Q2+lBc6OXvS5tvU/QBAIYgAGlkx86jXTt/j3MOANAJTnEKAAAQAAAAQAAAAAABAAAABAAAAIaD1lVAp55dV+O1c4f/F8");
+ WriteLiteral(@"R/UgQ6RwhWnxaUfYXSfkKg8dWB5jcJ9H4gAKjJaDZX79x9yIlogI/vvqM+Mtu3IwB3PnJJbX3o/ErhLCuVQXGlNTyGqQisfjQoOY5u6UciAOoxjyL1vw8eq/D805yMBnh69MCeAKwQpf4K8gtilPpXcLJ0Gh8j4zmpT0aZYhAtfST3WFHRMQxfEwCU8u72SIXzBSeiZeovBItSBbG0kEepohlUPEYVMVj9yqhACBADgOa4uzdR2+MZJ6IvAhAlfzQKXfTBjL7oedFSV9CuGJR1BYgBgD32wrnaiAUAetwBlBa6E/ZO3vNWLaIcMdASlOLXIp9cfV51MQg0zxFCAHCA+P63tvY5EX0XAKNCR15AVwAQ86vNfTVfRJyI3gmAzMSjQ/8+CKoXOvICxAC85P3dsRpN55yIPgpAunAdCYE1MbCRF9iwiFSWvUNeAFCXzdFU3X8cciJ62wGo7LbNihjYyAuMj1HyWgotInMxIC8AX5HFXjL7h14LwEGRKpzRGogBeQFdAQwfCX3f3R7j+w9BAExmquQFWEQA7+/g+w9KAEyLE3kBYgB+cn8/TLx/GIAAJHU8pzI6FYPKeUFQ4xh18gI9iyh7jUK9LgygLRLffwfff1AdQJT6R6DciYGdvCDfItI/Rs7rqWgRLR2LrgAGQrLY69GIEzE0ASgUg6D6TJW8AIsIhoMUfzZ5G5gARGWfi1LFp02LKF3IyQsQA3CKbPK2F7LJ2yA7gLLLQNNi0D+LKC0G5AUApmxPZmzyNlwBOEiBU2VZbxbqQAxcWkRmgoJFBH4glo/s7w9DFYDoZAsQ5ZXJcjEgLzAWAywi6DK3tkYs9hqyABzX/5XqUVgms4sPeUHhiyUvgD4hM38Wew29A8gqGCViQF5AXgDDRhZ6sdjLkw5ASwxWLKJyMSAvyHk95AXQYdjkzbMOoGiGGOV2BWkxIC8gL4AhcLTYC9/fIwHQKQomFpFW8SEvKHyx5AXQBuL7s9jLJwHI2QvIlhiQF5AXQD+QTd62xyz28k");
+ WriteLiteral(@"oAIo2qpGURZT6xRl4QVJ+pkhfQFYAZeyGbvPnZASwV6YxNzKx0BenvU98iMpmpWs0LbFhEjsQAiwh0SXz/rX1OhJ8CkG1qrMzqrYlBj/MCG7e4NBYUt3kBFpF/EPp6LABRh8SAvKD9vICuwC/kck82efO8A8gfzNHKLDIo+SLyAvIC6Aeyydv9xyEnwmsBiHQGc4mpQV5Q0d4hL4B2kMVebPKGAFSYzZMXGNky2vaOg7wg83JQ8gLfkND33e0xvj8CkLkZqEEBJy/QKnRdyQtKZ/TkBT4gl3uyyRsCUDxIjQs4eQF5AWLQddjkDQHQFoNqBZy8wFgMyAsQAwfg+yMA5RZQ2QDN+GAXLSJdMXBpEeUei7yAvKBhjjZ5AwQgUwEizaJRfTbfzbzA5ZbVWoWugbzAflew+pOQF3QbNnlDADQqjt5AJS+oLwZD3bI6973CImqNu3sTNnlDAJTeUEuNThMxIC8oFwPyAsTANbLJ20YsAIAAFDlA2YPYQAyGahGlxYC8IP+T5AXdQywfNnlDAIz7gECzM2jeInIjBl28xaVWoSMvoCso4NYWm7whABUo3g4iIC9Q5AU2LKLc9wqLqDayyRuLvRCA8mIf5V8GWjxkFXnBCYvo+HnkBcZiQF5gD1noxSZvoH8VUOXBTl6QJwbkBfmfJC9oDlnsJbN/gGoWUOXB3p28wIZFtCQGNiwiR2JAXuBvV8Amb1BfACrPHruTFxRaRKkPlhWFSMsiqiAGFvICKxaRViEnL+gLstgL3x+MBcD+7JG8oMwiOn5exbzAikVk3PGRF3SV+/shi73AXACiRmeP9cWAvMCRGJAX9DYvSHz/HXx/qNIBRAfz1yC3bNqaPZbkBUH5ACQvsCsG5AX97wrE9//VJou9oLIFFB2LwMFArXAduo28oKJFVFcM3OYFgX7xiTp2SalWIScvcI3s8EnoCzUEYLUb+GCgBg5mjz7lBT2+xaVxx+fAIsq0d/zJC2STt70Q3x9qCEBUJgZBtolBXmBXDLzLC2");
+ WriteLiteral(@"zc4rLU3hluXrA9mbHJG9jpAHRab2tiQF5AXpBz4sgL9JBN3rizF9i3gAzFgLzA3CJa+hLyAvICQ5I7e7HJG1izgKLqYkBe4M4i0io+5AWFL3YIeYFc7sliL7BrAckMMtDrDILcwU5e4EoM/MgLAktdo669Y2YR6diQpl1YGbLJmzwArFtA6U6gSAzIC/TEgLygbseXbxFV6xrrW0R576uLroBN3qBRAWhaDNxaRNnTPfICRV5gSQxcWkSJ78/1/tCEAESHf/LmkrbEoCgv6KJFlDnYyQvsW0TGHZ9/eYFc8SNX/gA0ZwFpGAtH4528QJEXtG4RZRpug8sL2OQNmhWAKF8Mmu4K0hZRbTEgL+hNXuDSIjITlG5ZRI9DNnkDFx1Azj0hTboCm2JAXqDICxoQgz5tWX1wvT+bvEHDAhCdtAGC+haRrhgUWkTHA5W8oEmLSEsMyAsKX2wTecHbm/uEvuCoA9Ac7FXEoHJXkBYD8gLygtodX3/yAhZ7QSsCYDLYV40F8oKmLKLM8kVeYN0iMhOUZvKCnfFMPdgPqV7gyAKKNItGiRiQF7gRA/ICN2LQRl4wns3Ve2zyBq1aQDpFLHJjEZW+JvICpxaRlhiQFxS+2Ly8YB7/9d72OAl/AVq1gHQGl628wEpXkBaDrlhEqnxvefICu2LQ57xgY3ecdAAAbi2gkt9aEzEYTF5gY8vq1Ch3aRFllq8aeUGQ07JUzguCirN5Ndy84MEoVI/Y5A1a6QCi6HA4B8szvwoDlbwgRwx6nBcUdwXLsl7XItJ6P06IQWcsoopiMJ4t4tk/d/aC1i2gEy1+yQgiLyAvKLKI6opB725xaSwoSi3i1/4OoS+0bQFFGYVjqSxbsoiKZn46YkBeUGc2T17QTMdXPS+QTd6mbPIGXegAigrH0qyOvKDm7DFnkZDBvWjt5QVBzQ4j43PkBUonL7j/OEz2+gHoiAWkZydkigF5QcXZoz2LqJoYlJga5AUVO75ii2h/Oo8FAN8fuiAAUf5IKg8Zyy");
+ WriteLiteral(@"0inYFKXmBXDMgL6ltE9jq+5bMplg+LvaAzArBU/yvZO+QFdmeP5AWlwt3jvOC9nRGLvaCbFlB5C68rBsPPC4osInuzR/KC0veqR3nBnd1JctknQCcFQH+g6tkJQ84LiiyiZmaPHbaIjDsM//ICuavX9pjFXtA5Cygy30HSsCtYavHJCyrNHm2KQaFFlPpgFy0iXTFwaRGVdXwy67+7R+gLPbGAtAoQeUG+GJAXtJ4XuNyyumjsyGKvO3vj5G+A7glAtFoBtYvvyYFqZO+QF+jMHskL+p0XiO8/wfeH7lpAJ1YC1xCDavYOeQF5wTDzgs3RVO2FMyoRdN8Cyh5Xyya5dvFV5AVe5AVcUprbFYxY7AX9EIBIwwpYnQqTF5AX2NiyukkxaOsWl+L3/3pnTAWC/nQA2jNa8gLrFtHx9yIvqGA3ZXyu5bzg/d0JoS/0RwB0PM9Ci6iCGNjIC6qtUWjWIqorBuQFdeym9vMC2eRN7B+AfghAKgXWbnOLxMBhXtBFiyjjVFQTXpsWkSMx8D0vkN09t1jsBX0SgChnoFYWA/KCRi2i2mLQ47zAhkW09DtmwyI6PM5sEbHYCwZgARWsCA1MjlPTIsoeqCZiQF4wtLyg0CIymLxEWhaRvhgs4uJ/Z4fFXtBTAdAdqC4tovKBWjbzIy8gL3CTFzzYD9WEO3tBPwUgqjRQm7CIjNr3AVhEGbqo311liQF5gfO8YHcySx4AvRSASHuY5Q9U8oL6YjDYvCAwfE9rWERpMXCRF4TzSN1jsRf0ugOIDovP8Xg1EAPygkwxIC9If7HLW1ympzTN5gXi92/sstgLBtEB5A32/KpKXkBe0KRFlD1JMJnNN5sX3N8Lkyt/APrdAagTm8FlDvbiqtpGXuCbRZShi/rdVZYYkBdUzgsejafqMYu9YCgCUPgLvzLYu5EXVLaIeiQGLi2i2mLgSV4gN3d5NGKxFwxMAIzEgLzASV7g0iJKiwF5QfYvp9zMncVeMCgBiDQ/H5QOdvIC8oLs349e5w");
+ WriteLiteral(@"Wp42ywyRsMrgNIXQVkXHwzBzt5gSuLSEcMyAuUlbzg0ShUIYu9YLAWkOZA1bKINMRguHnB8C8prS0GPcsL9sK52mGxFwxTAFZuCqk9ULXEwLu8oOAWlxXEoIu3uEyLgVuLKP3FbvICmfUT+sJgBSBSJ6OwwHigFg588gJv84IuWkTZk4TsFyqh78P9Kb4/eGABZc9drVtEy4JCXlDXIjp+DnmBsRiU2ZAy88f3B28EIEsMgpw+mbzAhhiQF3Q1L9gdz5IbvAAM2wKK9GZ9pWJAXqCVFwRlPVelNQo5FlHuccgLir5n4vtzZy/wpQMwH+zkBYU/U8WuIC0G1TKHAjFwmBcUWkTHvx/dtIjE77/3OKR6gJ8WkNlgJy9o1iIqF4Mu5gU6q4a7mhfIzV0IfcEfC0hDDPTCQQcWkYYYkBfoiQF5waoYyLX+kxmhL/hmAZUMsuoW0WrVJS9QFS8HJS9oMi8YzWSxF74/+G4BlRUx8oKMDsMsL7DRFaTFoIt5gZVLSo9/P5q1iOaLiMVegAAY2wnkBYUWkVHxVdzisvG8IKfzfDhisRcgAFbEgLyAvMCFRVRJDDK6xq24+E9Z7AUIgB0xaMIiqi0G5AXkBRlP2J/OkwcAAlBDDJq2iJbtnQqvibyAvODE108XETt8AgLg2iKqKwbkBTYsonIxGHJeIN9vc8T1/oAAtCoG5AXkBS4sopPfb2s8Ta78AUAAemMRpcWAvCD3GOQFhWKwH87VeIbvDwhAJ7uCuhbRsr1TQaAyj1GeF1i3iDJaJfKCenlBOFvg+wMC4IsYdGbL6rpiQF5QuysQx2eLHT4BAeinGJAXkBfUEQMp/tj+gAB0XAzICwzOEXmB1u/H7mTGnb0AAeiTENgTA/ICn/MC2d2TxV6AACAGqhd5gWWLyKj4qmHlBdN5pLbZ4RMQgP7jTV5QYBHVFYMmbnGZLwbt5gXyo8v2zqz1AgRgYJAXOM4LKtk77eYFu+FMzUh9AQEYLuQFFYVp4HnBeLZIHgAIgCc4t4");
+ WriteLiteral(@"g0xIC8IPsnajIvmMU/0y6LvQD8EgAjO8HGLS7Thdxzi2hJDFrMC8Tx2RlT/AG8FgAjO6EreUHhltXmYmDFIqogBjbygmprFOLiH87UnNQXAAEw7QrMxYC8oKm8oIpFNJrOubMXAAJgVwzIC7qfF8jNXVjsBYAAWBcD8oJu5wWLJPRlsRcAAmBJDMgLTF6LmUW0dJ4t5AVyvT+2PwAC4KwrMBcDD/KCAosot7vK6goynpz3qf0pi70AEICOiAF5gbu8QHb3ZLEXAALQGTEgL3CTF8wJfQEQgC6Igcu8wIZFtCwGgcZsfvkDbecF8tfjuPjj+wMgAJ3vCszFoNktq5cFpfjVt5EXlAmBFP85vj8AAtBnMSAvMN+yejJfsNgLAAHovxiQF5jlBXK9/wjfHwAB6KsYkBcYCFOqVZJ/7oVs8gaAAAy4KzAXAz/ygn1CXwAEwFcx8DkvEN+fxV4ACIC3YuBrXiCFf8JiLwAEwDcx8D0vkEk/oS8AAkBXYEUM+pMXyJw/8f35NQBAABADfTEYQl4gts+C1BcAAQAzMeh7XiC+/3SB7w+AAICWGAwlL5BZ/2SG7w+AAIC1rsBcDNznBfJf2d4Z4wcAAQAHYmAlL7BhEamD6/3x/QEQAHAkBlbyAgu3uJSburPYCwABgIbFoGt5gcz6Q3b4BEAAoP2uwFwMqucFyVYPrPQFQACg22LQRF4wIfQFQACg+2Jg2yKSa/0JfQEQAOioGDRlEc0jQl8ABAB63RVUEQN5Prd1BEAAYKBiUCQEYv0w9wdAAKDnYmDaFcj1/jg/AAgAeNYViO8/p/oDIADglxiICBD6AiAA4JkYCGzvDOCeU5wCaBuZ+TP3B0AAwDPE92exFwACAJ6xIPQFQADAP6TsE/oCIADgITNCXwAEAHws/pHC9gdAAMAzFoS+AAgA+AeLvQAQAPCUWYTvD4AAgH/FH98fAAEA/2CxFwACAB4idZ/FXgAIAHgIm7wBIADgIVzxA4AAgIfMF/j+AAgAeEeyyRvFHw");
+ WriteLiteral(@"ABAL9gkzcABAA8hU3eABAA8LL4s9gLAAEA72CTNwAEADyETd4AEADwFHx/AAQAvCz+kWLuD4AAgGewyRsAAgAewiZvAAgA+Fj8FZu8ASAA4CWEvgAIAHjInMVeAAgA+AebvAEgAOAhLPYCQADAU2YRvj8AAgD+FX98fwAEAPyDxV4ACAB4CIu9ABAA8BQWewEgAOAhXPEDgACAh+D7AyAA4CHJYi9m/wAIAPiFlH2sHwAEADyETd4AEADwsviz2AsAAQDvWBD6AiAA4B9s8gaAAICn4PsDIADgZfGPFHN/AAQAPIPFXgCAAHgIm7wBAALgKWzyBgAIAMUfABAA8IE5i70AAAHwj2STN6o/ACAAfsFiLwBAADxlFuH7AwAC4F/xx/cHAATAP9jkDQAQAA/B9wcABMBT2OQNABAAL4s/m7wBAALgHWzyBgAIgIewyRsAIAA+Fn/FPj8AgAB4CaEvACAAHsImbwCAAHgIm7wBAALgISz2AgAEwFPY5A0AEAAfiz++PwAgAP7BYi8AQAA8hMVeAIAAeAqLvQAAAfAQrvgBAATAQ8T2wfcHAATAM1jsBQAIgIdI2cf6AQAEwEPY5A0AEAAviz+LvQAAAfCOBYu9AKBh1nSe9Ol7b6jZqTOcLQCAHvBkuG1PAH5T82AAANAfsIAAADwWgGucBgAA77hJBwAA4CdbIgD/wnkAAPCL77726jURgJucCgAAr0jq/lEGsMX5AADwhr9PBCBuA6T4X+V8AAB4w9WjDkB4hS4AAMALXokn/reOBeCwC/ij+HGLcwMAMFj+Kq73f3n0P6eP/nHj+k831tdf+F78z0n8uBI/znGuAAAGwbX48c24+P9N+oP/L8AAx5G6SMzC+fMAAAAASUVORK5CYII=""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 16709, "\"", 16776, 1);
+#line 172 "WelcomePage.cshtml"
+WriteAttributeValue("", 16737, Resources.WelcomePageImageText_Browser, 16737, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 16777, "\"", 16824, 1);
+#line 172 "WelcomePage.cshtml"
+WriteAttributeValue("", 16785, Resources.WelcomePageImageText_Browser, 16785, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""384"" height=""305"" /><div>:-)</div>
+ </div>
+ <div class=""light second"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARIAAAESCAYAAAAxN1ojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAEf1JREFUeNrsne1LHFkWh2+3otGxscGQTHSCMkLCZtj9MB/2//+8HxZ2QjJEcDE4ZlaJ0KFNxzQ22TrmVKY1/VbVt6ruPfd5oOjJQKL10k/9zrm3brUcmOHLly9r2cd6tnWybSXbNvRzM5BfcZhtn7PtU7aNsq0vn61Wa8DZi5sWhyBaaWyqKPLPTuS71FfRiFQ+ZXLpc5YRCfiVxoqKomNEGosiUrnSz34mlyFXAyKBcuLYCqg0CaE06ucbYkEkMLlUEWl0E0ocvhLLJX0WRJK6PHZUHmsckaWQ5m1PtkwqPQ4HIkEegFQQCUyQhwjjoQoEedQvlUvKH0QSs0B2VB70PMJARHKhSWXE4UAkoaePxyqQFY5I0KXPBSkFkYQmEEkdj9zX3gfEQ1/LnksOBSJpunwRgTDXI25kTso7yh5E0oRAdh3NU4tlj/RRzhEKIkEggFAQCQIBhIJIbAlEmqdPEQhCybZTmrKIpKhAZBTmiWMOCNxFmrInLHOASOYJZEUTyA5HA2bQV6HwBDIi+U4i0gORoVwmksGiSP/kXer9E0TyVxnzk2MuCJQvd05TfkAwaZFoGZOnEIBl6alQkit3khUJozFQESMtdS4Qif0UQjMVqiapZmxSItFeyAEpBGpMJ0nMPUlGJJlEJIXQC4Em6Gk6MTuyY14kuj7IoWNEBppFSpxjq+ufmBaJNlSllGFeCITCqcVGrFmRUMoApQ4iWUYgkj6eUcoApQ4iKSuRTZUIpQzEgJlRHTMi0fVCDrg2IUL+");
+ WriteLiteral(@"zGTyDpE0LxH6IRA7l5pOouybRC0SZqmCMaRfchSjTKIVCU1VMEqUTdgoRcIkMzDOSJNJNDKJTiSMzEBCMjmOZUnHqESCRCBBTmIYHo5GJEgEkAkiQSIAhmUSvEiQCED4MglaJEgEIA6ZBCsSJAIQj0zaSAQgOg502VBEMkMiK0gEYC6HesNFJEgEoDS335VQZBKMSHh2BqCUTA71u9MowTRbs4Nx4HiKt2m2sm214N+5ybYrDl2jNP7UcBAiYT2R2pDFsNez7cHYlv/ZByKVj2Nyuc62z/rfNxz+SullIjlOViSsbFapNLY1ZazrZ5Ncq2REKh+QSyVcZDI5TU4k2ij6G+ffqzjyzxjIpdLTT8SyPI3MMWlMJNog+rtjhKYs0suQNPdQxbFqYJ8uVSqXmmCgHL/XvZZJkyKRJMIITXl5WG9MS1o5RyqlkFXWXtfZfG1EJDRXC5PL43Gi+y8yea9igcXoZyI5MisSfY3mIed5ofSxp/J4wOG45UZlckZKWYjaXnNRq0h0rdUXjr7ILEQa+wmnjyIpRYTS41DM5KiO5RrrFgl9kel0VR4IpBgy2vMWoUxF+iQvq+6X1CYS+iJT2dJSb5tDgVAqovJ+SS0i0Ueen3E+KWFqEorM8GTa/l3kLX4X0YpE54tIX2SNc3lL3kTd51BUyrkKhUluf5U4MiQ8jFUkB46H8XJ2tIxhFKYebrTcOeNQ3DLIRPJ7dCJhqPdOGfPc0QdpCilzjih3bqlkSLgykVDSfCMvY1a5hhvnTBNK6uXOS98lTpUiSX2UhhRCOgkV76M4lYiEUZrbXshzUkjQvNUtVbyO4lQlklQnnok4pCfEkG4cyFDxq0RLHa8T1byv2ZpJ5FGiEpFS5h9IJCqk7Pyna37RpyaQHuZTX/+YV5Fog3U3wZMio1O/JnpBWkiRvyZ6A9jx9X4c34nkqUvvgbzHmkToh8TNc91S40lQIlGz7XDxQeQ3hV8Suyl0dN3kYBLJk4QO/qoKhH6IwbifYMLcXfbdOF5E");
+ WriteLiteral(@"ojNYO4kc9FVHU9U6W3qOU3mUYW3Z69lXInmayAHPJUJTNQ2ZpNRAf7RMKllaJFpfpTANHomkR0rnfGWZVOIjkexyQQHn3gRPdDnUekWSSBpBIpDSNVAqGLSb+KFcQBDptfDM2R/N2SmTSkqLJJE0cohEYIx8NMe6TAoHhHadPywymCcC02Tyi/F97BYdwSklkgTSCK+FgFlsO9szmguP4JRNJJYXLOo6pr0DN5tC80oKi0SfqbG6TIDMZHzBdwQKlL9dw6lk4X0rk0isppFVlQhP8UIRLF8zC/dBC4lEh4WsGpgRGih7A7LafF3T5+i8JxKrNeGOo7kK5ZHm677h74Z3kVhcbyRf7R1gGfaNpvXuIhPUFhaJDvlaXP2M1d7BF1Znvj70mUgsppE9x3tnwG+6tVjizP3uLyQSjTYdTjrAQjcnayXO3KbroonkocETTkkDlDieUknbxz8S6UGhpIEq0+6esX2a+fzNXJFkf1lmsVp6riZ/Gx5Alew7e2u+dpdJJNbSyJ5LZ1FfaL58RiTz/nKkkZMGK9TFtrHvz9Typp1YWYNEoG5+TiGVzEsklsoaeY6GafDAddeASCzFMhqsQBKuqLxpJ1LWdB3DvdAcD6ynkvacSGYFShoglfijU0QkVsoaa3cD4DqMI5FoDWTl2RpGaiAUfjSyHyva+pibSKxIZJU0AgFhaV5JNyWR7HHtQmBYubFtLSKSLU4aQGXXpIUngzszRaL9EQuvm5DJdDxTA9zgKkJfTTM1kVgpax5yvQIldz2pxKpIaLJCyDww0j7YmiWSDSNlDQDlTbVsWk8klDXAza56vs0nuSOS+5NMIi5rSCRAeVMPG5MSCWUNAOVN4fKmPa3moawB4KaXciJhuQCIqbyJfa5TZ5JIYm+0yvx/3lUDpJIakRfotcf/QBoBaOTmFzvr44lknZMCwM2vTHnTNlTWkEggRqQUj30YeGVcJCukEQBugCXYGBfJBicDoBFiTyTrlhLJFtcjRMoPkf/+a+Mi2eRkAHAT");
+ WriteLiteral(@"LMOtSKa9zzMyWMQIYibqHl/bSBqh0QqxE/X0izYnAYBE7UskG5wEgEaJuk+SiyT251MQCcRO1N9BK6UNIoHYiXoelJXShh4JQAAiiX34l0QCFoi2T9Lm3AEEQ7R9EgsiYWo8ACJJ1+IA94i212dlZiuABaLt9VlptgIApQ0AIBIAQCQAAIgEABAJACASAEAkAACIBAACEUmfQwHQODckEg4+wLJ8RCTNccX1B4BIAMBIaTPiBACQrpcVySdqSwBIvbQhkQBpBJGkfRIAXOTtBSvzSK65DiFyeiSS5vnMdQiU582LZEBpA9AoUQ8Y3Iqk1WpZGP6lvAFuhgGUNkOMDtAI11ZKGyH2PgPlDXATbIbRuEhin5T2gesRIqUX+e8/GBdJ7H0SEgmQSJphOC6S2OeS3CATIJE0L5KRgRNCeQNcs02WNq1Wa2Bgh3pclxAZ7w3sw/D+zNbYyxsSCXDN1oyEkPsiiX0uyQ0ygYiQ+SOx9/Vuw8d9kVgob95zfQJppL6yZpJIPhnYsUuuT+CmVxuD70SS1ToWXkthIS6CfW6M3PSuJiUSK+XNOdcpkJyrJx/tbU8zDCcJgLJmDt8qGKuJRMobRm8g5OvTTFkzTSRWXt/5P65XIDFXymCqSLKaR4ZzhgZ2UvokrC4PIXJmZD9mljaWUglNVwiND87Gan6D8ZUVrYvkjOsWAuMPI/txZ1DGukisNLWA6zE0enNFon2SgZEdJpVAKLy1siP3J6+2F40ukZuToWBoGmn8nxv6TrlFRWKpJHjLdQwk4wZEolNfR4Z2nFQCTaYRSyLpF0kkE81DKgEolUaszGkaaA81WZGQSqAJro3dxCa2PGaKJDNPz1B5QyoBrjk/N+RiIjGaSpjtCnVxZex6m1jWpCiS/A7BMzhQB/81tj9TR3LnisRgeSM1K5PUoGrODd6Ee6VFMs9EEacSlmOEqpDEe2xsn/rTypqURWIxdkI4HBssn2c6YCGR6OS0gbED06PEgQr44Ow19EfzyrR2gX/swuBJ");
+ WriteLiteral(@"lxLnmmsfPJY0bwzuV2987ZFlRWKt6Zqf+Ndc/+CJN0ZvTHNDxMIiUSNZfEn3lWOiGvjpIVjsJQ7yV074SiRWy5u8xGH6PJTl2mhJs/B3vpBI1Ex9owfslWOiGpQvjy1eO6PsO79QymqX+McvDV8Qv/G9gIIcO7tzkhauQAqLRA01NHrgrgxHVPDPubP97NbC+9Yu+QPecXFA4nwwftO5nDfk60MkFoeCx3njaL7C7OT6yvg+FgoLpUSiprowfiBfOZ7Hge+x3FwdTyOF2hftJX7YufFUkjdfkQncvyasz4Yu3LooLZJEUolcOEeOYWFI58ZSOI0sm0hSSCV5PfwbMkmeVNJpqYGUpUSSSCpBJvAmEYmUSiM+EkkqqQSZpF3OpDIdoPS0jqVFoqnkNJEDncuEpQfSkUgvkf0tnUZ8JRLrs10nyeTfjtGcFCSSyjleOgy0Pf4ypwleaExa40ZhgYsis1grFYmuNt9P6OCLTP7jmE5viQ8Jlq7D7Lu79CMvbc+/1EmCF5909I/5DkbPud4YUmume6kkvIpEmzUXCV6EZ44RnZiT5RuX5lPffa0kwhKJIjFplOBJkRPyL0cTNiauXVrDu5VVEK0qfrsvX750s4/DhC/Qfd0gXC41haSaIv/00RupVCQqk2fZRyfhC1VkKsfgAd/Z4EqZY5d2k1warC99/oNVimQt+3iRbSsJn7BVTSZ7fH+DIF+MKPUJhUeZSLyOsLaq/G0zmTzKPp5y/ZJOSCHBIHNGvM/5alX9W1Pi3Ekne47eSd2cO5vv4i1V0mTb62UnnzUlEkqcu0gqeZ5t2xyKSpHRM3lRfI9DUV1JU5tIVCY72ccB5/G7cufnbNviUHhF+h9vKWPqKWlqFYnK5FC/PHCXx1ru0D9ZDildzhyvX52EvHbz9yp/QJ0iWdESZ43zilAqEMiZow8yiZGWNIMqf0irzj3KZCJN12ec27lC+dHRQ1m0hLlEIDM5WfS1m9GIRGWym3084fzOpatSecyhuIPMBfnD2X11");
+ WriteLiteral(@"rE9ksaKTOn5Qq4m9o19SiFWVyV7CZc+1iuPMMZlsUQZa0tTy3FtTIqFfUo4tlcpOAlK5UXm8J30Uppa+SOMiUZlsuq/9EuaXIBXk4ZdjX8sDBC8SlQnzS/zwQIUi5eK2lkOxID2PnoqDJRiWx+tTvVGIRGUiz+I84vx7Tyvb+vmDC2fSm/Q3Pqo48k/wRy+TSCOr9bVC2PtMJgd6R4XqkLSyruklb3RXNcR8pXV6T8uVj/r/GKatjlqbq6GKRPok0i/Z5HpoLMHk5dAPBUqjXBLf7ogcykYQebxsSiLBiGRMJozkABSXSK0jNJNoh3I01KbHLs31XgHKcty0RIISicrkts5DJgALcVLVsgBRi2RMJrwnBmC+RIKZa9MO8QipZU+4VgAmchqSRIIVicrkEpkAfIc8iBfcS+haoR81Zr8C3JFIkDfXVgxHD5kAhCuRaESCTACJhCuRqESCTCBRTkPsiUQtEpVJV2XC8gNgnZPQRmfMiERlwlomgEQQCTIBmMLtoyKhzFg1LxKVCU8NgzWJHIXw7ExSIhmTyYFjMWmIm0bXE0leJGNCYaU1iBVZx+UkVomYEonKhOFhiI1G1lhFJPNlQhMWYmCkKcTEqnIti2dI+ybyEq4O1ysEiPRD3sbYVE1KJGNC4fWgEBoyN+Q05n5IciJRmXQ0nVDqQNOlzGlMk8wQCaUOUMogkgqFIsPDu6QTqJGLTCCn1neyldpZ1VGdfcdsWKiWoQtocWZEUp1QaMRCZSkk295Za6gikukykZdxHTh6J0AKQSQehELvBJbFxAxVRLK8TEQi8rwOLzOHIvQ1hQxTPgiI5HuhSJnzk6MZC/PLmFMrU9wRSXVC2dFyh5eawzjSQL1IuYxBJOXKncfu6/IE9E/A5PR2RFKvUHYd652kLJB3qfdBEIk/oaypUGjIIhBAJN6E0qXkQSCASHyUPPRQEAgi4RB4E0rXMcoTI7ejMNl2ThMVkYQkFRHKjmNV+9CRR/ovrK4PgkjsCGVNy54uKSWo9NFT");
+ WriteLiteral(@"gQw4HIiElAJFkGnskjx6lC+IxIJQ8l5KF6nUUrrk8qB5ikiQCiAPRAJFpNJxzE0pSk+3PvJAJHBXLJsqlC3HokuTUseVpo4+hwORwOJi6ahQRCybiSWWvopjoKmDZikiAY+JZUOlsmEotYg0hnnqYIgWkUD9cpG5KusqlTXdQkwvIxXFcEwaQ6SBSCCOBLOiclnX/70xJpl1t/ykOZHC57E/X+nnTbZ9EoEgC4DE044mHoCJ/F+AAQAgl3zNeDGxuQAAAABJRU5ErkJggg==""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 23246, "\"", 23314, 1);
+#line 176 "WelcomePage.cshtml"
+WriteAttributeValue("", 23273, Resources.WelcomePageImageText_LightBulb, 23273, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 23315, "\"", 23364, 1);
+#line 176 "WelcomePage.cshtml"
+WriteAttributeValue("", 23323, Resources.WelcomePageImageText_LightBulb, 23323, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""274"" height=""274"" /></div>
+ <div class=""bulb"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVoAAAKSCAYAAACTAhMyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2OEMyQkI4M0Q4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2OEMyQkI4NEQ4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiI+IDx4");
+ WriteLiteral(@"bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY4QzJCQjgxRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY4QzJCQjgyRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+zKbSDwAAExlJREFUeNrs3b1uXFkBwPF7UbaABiNBB9qJQEABWruEJuMSsSxOgaBix0BBl+QJ4jyBsx0FaLxPYKdAovPkCexIFIhF8tADMd0WKJdz7Du7E68/Z+bcz99PupnsJvHHmev/nDlz5968KIoMgHS+ZAgAhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWoCvuxV/yPDcSQOsURTG8+P9CzyYN+xrLXwDaEdZB2B6H7ai4Xvzznfj3hRbgdrFaC9u4WMy4zuAKLdCGyG6F7XWxnPjvR0IL8MVI7RarNRZagHSRrSW2Qgs0NbKjIq1Rhd+L0AKNi+xgBWuyt1mzHVQV2nvuVqBhnoZtLfHniB8/LiFsXhHH4cX/t9TxuWa0QMNms1Van49r2PZvmAWPL4uwpQOgTaHdrTi04zLuh3f8dzHIa7f8nrL87BdvwQWaEdqTcDOo8FOezi0lLPJvN0M/j4UWaM2yQbg5admXfWNsY2OdvQtoikELv+azF9VuWkYQWqDqmetaeZzs/vxhXOGPDlv6LcUX1B5f9xcsHQBVRjYGqYrDt+pYQrgfWnpq6QCoM7LxuNXdDkZ2toSwZekAqDuyo45/mw+EFqhz");
+ WriteLiteral(@"uWDUg291cNUfWKMFUkY2PqU+6ehywUWnoaVfu2QMzGiBpLZ6EtlrCS2Q0oc9+l6PhRaow3qPvtep0AJ16NOywWjuhDPD+T/wYhiQTNHv0wNuh7buOakMILRpxROLTywdAClNev79x7cbW6MFknrR8+9/GH+xdAAk07M3LFwlN6MF0hXm/GxWH/V+HMxogQpmtoezp9E9E9/EsGFGC1ThYXbNO6c67Fn8RWiB9E+d8zyecGVjFp6e2Avf88HZ92/pAKhSeRHGR9n5UkIX36IbZ+7PZpE9O5TY8cQASR9YLB0ApCa0AEILILQACC1Afe4ZAtqiKIp4KFB8z/w0z/OpEUFo4W4RjQGNIR2G7d3s/NLNs+2yvz//n/G4xdPy9p/xNoR4YlRpCm9YoM64xiukPsjSHbgeYxtP0zcJ+/ixEaem/dwbFqh8pxuGbRy210W1TsK2U74rCYSW7i0LhG1Uxq4JDi9ePA+EljYHdqeG2etdZrkj9xRCS1t3rlGDA2uGi9DS6p1qPWxHRTuNy6MfQGhp7A61U7Tf6/JoCBBazGLNbmlLaB1Hy7I7UZz9jbNuXuU0Hnv70LvQWDa0znXAMjvQ43Czn3X3UtLxTRRH5Vt/YWFCy6KRjbPY3R58q/FB5NBhYCzD0gGLRraP4dkOPyt79gAsHSCy6YzNbDGjRWSrseEkNZjRkmJnGYnsZw69QIYZLauO7DDGxUi8Jc5oN8PPzqmh4KYZrdBy004SX3U/ybp7CNcyDsLPzkPDgKUDltXl42SXtVUeSwyWDlj4kThGZNdIXCsuHWx49xiWDlhk5xiEm6PmzmbjOToas9/GS+Vs2muwdMBd7TZ7ySCfC27tho6vxdIBd30EHmaOMrirabmE4CgEzGi5laeG4M4GYfPCGGa0mM0mFmez981qMaPFbDadNbNazGi56ZH37PyrRmIp0/DzdN8wYEbLVR4ZgqUNXHOMi4SW2aNufNorEKvxoSFAaLlMjKy32q5oLF3YEaHlMj83BCt/4AKhRRg8");
+ WriteLiteral(@"cCG0VMKLNx64EFrSe2AIkjyADY0CQsuMy7KkIbQILYKQ2HuGAKElc5HBpAaGAKElcrxnOh7EEFrODA2BZwwILXjGgNDSal6wAaHFjKuCp/jprj02NLoILcTIFoVhQGghHSe+R2gh8Xz2TcqlAxBayMOPQW5Wi9BC6toKLUJLOi6NnRUpZ7QT44vQ8soQgNBCYkmXDabGF6HFU9uUCc9zoUVosUab0LEhQGiJMy4xSMdsFqHlMxNDkIQXGhFaPMX1AIbQUpWXhmDlTvM8F1qElnMhCAdGwWwWoSU9sV2tF4YAoUUYPHAhtNQQBsfUrmgs8zw3lggtbyvDYBa2Gh8bAt76+SqKIv6QGQlml8Y+MhJLmYafp/uGgbmfKzNa3prVxuNpJzXukg35GEv5yJ6EGS03PfoOw82hkVhIXH65b30WM1pumtVOMseALjybFVnMaDGrTWcatg2hxYyW24oz2j3DcCdPRBYzWu70CByshe2kvOWGB6bwM7RpGDCj5a7i7OyZYbjVOG0bBq4jtFzneeZNDDd55nI1WDpg0aWDGUsIV4tvtX1oGLB0wCqeGlt//KJjSwZYOkBU0j74bDvKAKFl1fYyL459NsN3UUuEllR2MsfXPhFZ7uqeIeCiG14c3S5fLBv1cGjicsGePQShpYoQ9y22cbngoYstsvDPjMO7WFTYd2Joxz2IrDVZlvk5EVqW3omG4WY/6+ZxtsdlZB1dwFKh9WIYyy4jxKfTG2WUuuR5+N6cjYuVEFpWEdt4+ZYY2y4c/jVbKnjinmVlPyOWDljx06RR1t51W+uxWDqgFbPbvez8ZDRtJLJYOqA1sY1Puyct+7K9EQFLB7Tu6dIgO790eRuORnDibiwd0MpZ7TRrz6W3nTAHM1pa/Wgez2U7aPCX+NwRBpjR0nZNPuTL5XqohNCS9inT+VEI04Z+eQfekIDQ0hUfm23T6wmHNVpSC/tYPPLgdcO+LEcaUNX+b0ZLBY/m50/Pm3Y13Y/dM1RFaKnKi4Z9PS6jjtDS");
+ WriteLiteral(@"OU0KmxfBEFq6pwzbpCFfzkv3CEJLVzUlcBN3BUJLVzUhcKdOHoPQ0lkNubihyCK0dF7dobM+i9DSeVMzWoQW0nrV89AjtNDt0HkhDKFFaNPyJgWEFqFNzGwWoaX7ykvcgNBCR1k6QGghsVeGAKGlL6yVIrTgKTwILUBr3DMEVO0n739gEDCjBUBoAYQWAKEFEFoAoQXgRg7vonI/+P7/avvcf/mz8Udo6YG//s1uR79YOgAQWgChBUBoAerjVQkq56gDhBYSc9QBfWPpAEBoAYQWAKEFEFqAzvLyL3X4OGwvL/uDf3zy9wf/+fe/hot+4B++t/7sy1/+ylV/PDH01KIoCoNAkwzibrngdmj4aGRjhZYGGi8Y2qGhQ2jhdoYLRPbEsNHU0N6zfHC5PM8NQn0m5XaXGeozw0ZTeTGMpvpo9puvf/0b6++8887a/B++fv2f408//fR07n/tGTIaO3EzmzWjbbrf/u73h5fMbjf/9Mc/TIwObVg6cBwtQGJCCyC0AEILgNACCC2A0AIgtABCCyC0AAgtgNACILQAQgsgtAAILYDQAggtAEILILQAQmsIAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWQGgBEFoAoQUQWgCaEto8z5fagkFRFI/Ddhh+X1zcgv1wOwrb2h0+JoAZbQjoWth2wm9PQhx3wza8IuRb4WYc/u7JmzdvHrurAKG9XWQH5Qz26R1mznFGuxtiux8j7S4D2uZexZE9iuFccKliK36M8NsNdxtgRnt5aPcXjexcbNfDzHbX3QYI7RcjuxMjuYqPFT5OfAFt6K4DhPbzyMYXvx6t+GM+ddcBbbGyNdoyqKPw2wdhW5v7/6fLLhlcMqsdho+7Hm6P3YVAL0IbAxu23VUH9YbPGZcPhBbo/tJBeYzruMrIlh64+4DOz2jDrDK+qaCuowAcUwt0f0YblwsMIUCiGW05mx0YQoB0M9r1mr92");
+ WriteLiteral(@"L4QB3Q5tmNG+V+cXHt+48ObNm3jCmbFzIABdndHWLsR2ELZ4aNlJeR4EgO6ENgTuVYOCu+aFOaCLM9pGrZGW568F6NSM9iDMIqeGEOB6S71hIcR2O9wcGkb47G3hcXs3u/rQx5dhixOUSfj5MVER2luFdhJ2rhjbcQN28j3XDaOG/W4Ubn4ettsuXQ3n/m0M7UHYPhJdSwc3xXYv3DyscxkhfO7j8HU8cXdS4T63E492KScZi74+EGe98Vwh8aiZeKmmdSNrRntdbOOj8kF5Fq8HWbXvGHsRYx+20/C53aOkDuzs/B6r3sfjx42Xa4oTlydxfzbaHQvtqp5yl7Pbvbq+GUsHJAzsWhnYUeJPNSqD+zAuzRl5SwfQl8jG2ethBZGdiVE/DJ/3sdEXWuhDZOO66VFWz7k9dsPnH7sXhBa6HtnDrN5zH4/EVmihq5GNcR1nzTjB/Kg8jAyhhU4ZZ/WfCvStr8fhX0ILXZrNxtljE8+dse/eEVroypJBU88EN4hvlHAvtc89QwBviYdUNflE8o9CbJ9f94aG8sEiLjMMw/bV7PMlkPhvXpW38VwLrlIitFDLbPZRw7/MtfLBYOeSr3+U3Xzeha25vz/NnGvB0gFUbJS14zL2jy4EdmvB8y4Mss/PtTB2lRKhhSp82JKvc62Ma7yNx/nuZ8ufeyE+yBx5N5qlA7js6X4MzDBss5MZDeaiMy23uBYZ1yYPrlrbLD9Omw6fepSt/jjfsxcC44VXy3NNY0ZLn33wwfvDcjY3e8o8KoM7uPDUeFg+PY5/53V5OsLhJR9y2LIhGGbpljm8G82Mlj771re+mf3ql7/Ivve97z5d8EPMTkc4Cbfbcy8CPTC6X4htZmYrtPRvFpt98LOfrnJGGF8Eiud+fZ5Vew7lNsX2VTk+CC1d95vtX2c//vGPUnzoszXJrF3rs1V6GsbnwOFfy8nLpwdGgsb65JN/HH3nO98WwvrsWUJY3FljhZaG76SzF7qo132z2sVD66gDmryDbolsYzwy");
+ WriteLiteral(@"BJYO6F5k46FL8dCtNaPRCNPQifuGwYyWbmn6yV36ZuB8uIsTWpo6m/VUtXmGhmAxDu+ijpAOsvM3DsTDqgYX/vhldn5qP7PZ5nGfCC0tCWx8R9fIrKmV3jMEQkuzIxvjumtWZEYrtJAmso6Fpde8GEbqyO6IbGe49I3Q0sDIDrPzNVm64b+GQGhpHpHtlokhWIx3hpFyNntoJDoZ2ngI3p5zH9z6Z0FoSbZzeQGs++IVdLevu/Q5QkvanSuep2BgJDovRnYzNMQLZUJLDTtXYRR6Iy4hbJjZXh1aL4YBy4rPXHYNw9XMaDGjZVWcHNyMFkhsyxBcTmhJxcymf941BEJLtSaGoHecGFxoqdgLQ9A7jjoQWqqU53k8mH1qJHrllSEQWqq3bQh65cAQCC3Vz2on4ea5keiFiXeHXfOz4DhaUnPeg17YENor938zWiqZ2W6b2XbatshaOqAZsX0SbjYzh311STzK4GG4b/cMhaUDmvdUKh5v+WF2ftxl3Fz0r13i7DUevvfciWRut3QgtDTeb3/3+3gC8eGF/735pz/+YbLkD8AgcyrHOwVWWBcLravg0t+nc+cnQJkaCVKzRgsgtABCC4DQAggtgNACILQAQgsgtAAILYDQAiC0AEILILQACC2A0AIILQBCCyC0AEJrCACEFkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWQGgBEFoAoQUQWgCE");
+ WriteLiteral(@"FkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQUQWgCEFkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAGE1hAACC2A0AIgtABCCyC0AAgtgNACCC0AQgsgtAAILYDQAggtAEILILQAQguA0AIILYDQAiC0AEILgNACCC2A0AIgtABCCyC0AAgtgNACMCcvisIoACT0fwEGAL+BBlr+j4JHAAAAAElFTkSuQmCC""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 31214, "\"", 31283, 1);
+#line 179 "WelcomePage.cshtml"
+WriteAttributeValue("", 31242, Resources.WelcomePageImageText_LightBulb, 31242, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 31284, "\"", 31333, 1);
+#line 179 "WelcomePage.cshtml"
+WriteAttributeValue("", 31292, Resources.WelcomePageImageText_LightBulb, 31292, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""346"" height=""658"" /></div>
+ <div class=""bottom"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABLwAAADKCAYAAACv61n1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAKsZJREFUeNrs3VtsXPed2PEzc87ceBlxJFKKR6Iskk5sL1U1m6hoyVYdF4VhAoMt7D64LwEmfUqQPGwKC32owTgbgy/BGNuXBA7ysNA2C7RddJ3dxQBFXTQQ7I4LrNEkgp2E3shkZVqOJVO8Dzn3nj95KFPk3M7M/9zmfD/A0VDkmXPO/M9l5vzm9//9g4qPZfOpaQUAAAAAAAB9Jejz1/+vOAQAAAAAAAD6i28DXkZ2V0J/nOIwAAAAAAAA6B9+zvA6DHTRrREAAAAAAKCP+DngNX3sEQAAAAAAAH3AlwGvbD6V1B8Sxn9Ft8YEhwIAAAAAAEB/8GuG13Sb/wMAAAAAAMCjCHgduMyhAAAAAAAA0B98F/Ayui8mj/16Uv99jMMBAAAAAADA+/yY4TXV5Pd0awQAAAAAAOgDfgx4NQtsTXI4AAAAAAAAeB8Br/a/BwAAAAAAgIf4KuCVzadaBbVi+t+nOCQAAAAAAAC8zW8ZXtM9/h0AAAAAAAAu57eAV7sMLgJeAAAAAAAAHuebgFc2n0rqD4k2syX0+RIcFgAAAAAAAN6l+ei1Xu1wPpHl9TaHBgDguGuZXN3M/G/dSAdoNQB+vQZyPQQAOMlPXRonO5zvMocFAAAAAACAd/ki4GV0U0x2OPukPn+MQwMAAAAAAMCb/JLhNW3x/AAAAAAAAHAJvwS8pkzOP8mhAQAAAAAA4E19H/AyuieS4QUAAAAAAOATfsjw6iZbK5bNp6Y4PAAAAAAAALzHDwGvaZufBwAAAAAAAAcR8JL/PAAAAAAAADiorwNeRrfEWJdPT+");
+ WriteLiteral(@"jPT3CIAAAAAAAAeEu/Z3hNO/x8AAAAAAAA2IyAV2uXOUQAAAAAAAC8pW8DXkZ3xF67JE7qy4lxmAAAAAAAAHhHP2d4TbtsOQAAAAAAALBBPwe8ZHVHJOAFAAAAAADgIX0Z8DK6IU5KWtwkhwkAAAAAAIB39GuGl8ysrFg2nyLLCwAAAAAAwCP6NeAlOytrikMFAAAAAADAG8jwcmZ5AAAAAAAAsEjfBbyM7ocxyYtN6MtNcrgAAAAAAAC4Xz9meFnV/ZDi9QAAAAAAAB7QjwEvq7ofXuVwAQAAAAAAcL++CngZ3Q4TFi0+qS8/xiEDAAAAAADgbv2W4WV1t0OK1wMAAAAAALhcvwW8rO52SMALAAAAAADA5bR+eSFGd0OrR1KkcD0AwPOuZXJ1L2//WzfSAfYiAL9eB7kGAkBn+inDy47sq1g2nyLLCwAAAAAAwMUIeJk3xWEDAAAAAADgXgS83LseAAAAAAAAdKEvAl42dzNM6OtLcugAAAAAAAC4U79keNmddUXxegAAAAAAAJfql4CX3XW1rnLoAAAAAAAAuJPnA15G98KEzatN6uuNcfgAAAAAAAC4Tz9keE37bL0AAAAAAABogYCX99YLAAAAAACAFjwd8MrmU6Iro1MjJlK4HgAAAAAAwIW8nuHlZJZVLJtPkeUFAAAAAADgMl4PeE35fP0AAAAAAAA4xrMBL2OURKczrMjwAgAAAAAAcBkvZ3i5oYZWIptPJTmMAAAAAAAA3MPLAS+3ZFdRvB4AAAAAAMBFCHj17iqHEQAAAAAAgHt4MuBldCOMuWRzkkY9MQAAAAAAALiAVzO83JZVRfF6AAAAAAAAl/BqwMttdbMIeAEAAAAAALiE5wJe2XwqoT+4bWRECtcDAAAAAAC4hObBbXZjNlUsm09NX5+9+T6HVG/SuUw8l76xSUtAlmuZXP347966kQ7QMgCsvM6YZeV16fj2cQ0E4LfrIAB/8mKXxssu3a4pDicp/k06l5mjGQAAAAAAQLc8FfAyRkN0a/");
+ WriteLiteral(@"dB6njJ8aI+zdAMAAAAAACgW17L8HJzUCmRzaeSHFLdE90ZjX1MhhcAAAAAAOia12p4TXpg++5yWHXtMNA1ns5lpnPpG9REA/qcjJofVqCOCACug1wHAQDeRoaXXFc5pHoy2+RnAAAAAACAjnkm4JXNp0RR+JjLNzNp1BlDd+aa/AwAAAAAANAxL2V4TbOd/Ut0YdQf4kd+NWPU9AIAAAAAADCFgJd/t9NtXmzwO7o1AgAAAAAA0zwR8DJGP0x4pE0nOay60ii4RbdGAAAAAABgmlcyvLwURIpl8ymyvExI5zLjSuPMOAJeAAAAAADANK8EvLw2+uEUh5YpM01+HzdqewEAAAAAAHTM9QEvY9TDpMfalSCNOXNd/g0AAAAAAOAEL2R4eTF4lDDqjqEzrYrTE/ACAAAAAACmEPCyDsXrO5DOZURAK95q/xs1vgAAAAAAADrihYCXVwNHVzm8OjIjaR4AAAAAAIB9mps3zhjtMObRtk2K+mPXZ2/ucpi1NNfhPP+VpgIAuMm1TO4Z/SFDSwDw8XXwkv7wCi0BwI00l2+f10c7FAG7dznMGjO6KnbSXXGW1kKbD1t1WfO/dSMdoEUBtLmGPGPc4D3jxetgu3m5DgLo4DpyybgOfp3WAOBWbg94eX20QwJerXVakD6ezmVmc+kbeZoMAODgDd4zissCXQBg83VwRH/4jj79sT6N0CIA3My1AS9jlMOEx9uXwvWtmRmB8Tl9IuAFAHDiBu8ZhUAXAK6F31MIdAHwEDdneE33QfvGRB2y67M33+dQe1Q6lxEjM5opRj+nUB8AAGDvzd0zCoEuAFwLv25cCy/RGgC8hICX9UQdMgJeJ5mtyzUuan7l0jc+oukAeOgmoe70NlCPqav99oxCoAvoi+sh18Cer4V/phDoAuBRQTduVDafEl0Zk33SxtMcZg3N2fQcAADM3OD9XH8Q0zO0BgCuhQS7AHhX0KXbNdVHbZww6pHhUTM2PQcAADOeoQkAgGshAO9za8Cr37KiKF5/RD");
+ WriteLiteral(@"qXEft3vIunzhm1vwAAAAAAAJoi4GWPqxxqj+ila+IszQcAAAAAAFpxXcBLjGrYh+2c1F9XjMPtoTmHngsAAAAAAHzAjRle/VrkneL1yn53xniPbUEdLwAAAAAA0BIBL16X3XrN0Bo3aoABAAAAAAA05KqAlzGaYb92/aNw/QEZXRKp4wUAAAAAAJrSXLY9/VzcPSbqk12fvfm+z485GcGqF/XpJ5y+/eVaJlfvh+1460Y6wN4E4OVrYS/bwDUQAAC4hdu6NPZ7FtSUnw+2dC4jgl1xCYuaNmqBAQAAAAAAnOCagFc2n0roD8k+b2+/1556TuKyGK0RAAAAAAA05KYMLz8EgxJGnTK/mnXpsgAAAAAAQB9xU8DLL939fFm8Pp3LjCtyg5pkeAEAAAAAgIZcUbQ+m0+JkRn90t1PFOZ/24fHmuwAVTydy0zn0jfe5zSG38gsak2BaQBcB7kOAgDQj9yS4eWn2lZJI8DnNzMWLPNFTmEAAAAAAHCcWwJefuvm58fi9VZ0QaSOFwAAAAAAOEFzyXb4LQAkXu+7fnmx6VzGqnpb06I2WC594yNOZXdq1eWELiQAAAAAAKs4nuGVzadEsXq/dfHzW0ablQXmZziNAQAAAADAUW7o0ujH7n2xbD5l3etemB/Qp6dc9HqtDEo5Nlrj4vLElJi4jAAAAAAA4C5u6NI47dO2F4ESq0YY/Io+Pa5Pv3X6RYqRFPWHcQtXMeeCY/c2lxIA8B6ZI/0BANdBAHAXRzO8svlUQn9I+LTtrQz0fVU5CHq5geWF5S2sEdbJPvRrwBYAAAAAANdyukujn4MFiWw+lbRo2aI746iyMH/RBa/TjmCU7XW8FpcnDoO1CeNnAAAAAADgEk4HvC77vP3lF69fmBeZXQPG/xyt45XOZeKKPcEoJzK8ppv8DAAAAAAAHOZYwCubT4mRGSd93v5XLVjm00d+vubw67MrEDWezmXGbX5tl5v8DAAAAAAAHOZkhhdZMYqSNAJ/Mh2t3XVxf8RG58zauC");
+ WriteLiteral(@"7bsrwWlyeOB2snjd8BAAAAAAAXIODlPHntcFCza/TYb50sXj/Xp+ua5ngGAAAAAMC9nAx4TdL8+2QGShrV7PqqEy8qncuI1xW3cZUzRs0wp45djmcAAAAAAFzCkYBXNp8SwRC6gB2QGShpVLPLqQyvFx1Yp11dKMnwAgAAAADAxZzK8Jqi6R+KGQHA3hzU6rrY5G9OBL1mHVin5d0aF5cnxLHbKFgbM/4GAAAAAAAc5lTAi2yYR8kIlLQKatnardEYMdGJfTzn8LHLcQ0AAAAAgAtodq8wm08l9YcETf8IESj5mx6X0Sqo9ZTNr2fGoXaMi9phufSN9y3eV1buRwAucy2TG9EfvqNPf0xrAPDpdfB5/eFPaQkAgJc4keFFce+TEkYgsBetglqjxgiOdplzsC0tW/fi8oQI1LYK1iaMeQD0xw3eiD59T/9xSZ9e0acRWgWAz66Dl/Tp5/qPb+jTJVoEAOAlTgS8rtLsDXUfCDyo0TXQZi47uzXOOtiOVgbbpiXNA8D9N3nfUwh0AfD3dfBPjevgM7QGAMCLbA14ZfMpkf2SpNkb6iUQ+HQH89hSuD6dy4iAU9zBdpzWt8Gq9V+WNA8A9yPQBcDvvkMTAAC8zO4ML0axay6ZzadiXT63k2DWRWVhftSG1zHjgraUnuW1uDwh9k0nWXiTxrwAAAAAAMAhdge86O4lu30OanN1Gsiyo3j9nAvacc7hfcNxDgAAAACAgwh4uUs37WMmiGVpHa90LjOuP4y7oB2tqCE2adG8AAAAAABAMtsCXtl8imBXe9200TUT81pdx2vOJe0YT+cysw7uG451AAAAAAAcZGeGF0GADpgKDC7Mi5EZL5pawcGIjlaZcVFTPidrQYvLE6L2nJm6XDHjOQAAAAAAwAGajesiANAZEfB6v8N5uwleiW6N/1f2RhsjI865qB3FtrwicZ9085zbHM7+cy2Tq9MKALgGAgAAOMuWDK9sPpXUHxI0d0fMBAa7qcllVeH6WZe147hRU0yGaZueAw");
+ WriteLiteral(@"AAAAAAJLCrSyM3/51LGAHCTnQTvBo1RnaUbc6FbdnzNi0uT4hAbTfB2oTxXAAAAAAAYDMCXu7Uvr0OanENdLl8K0ZrnHFhO844fOxy3AMAAAAA4ADLA17ZfEpkuSRpalM6CZQ83cPypRauT+cyYnvHXdiOMrLOLjv0XAAAAAAA0CU7MrzIcjEvaQQKW+klaHVRWZgflbi9c25tyHQu0/W2LS5PiJEZJ3tY/aSxDAAAAAAAYCM7Al6Mzii73Q5qcPUasJJZvH7Oxe3Yy7bJCNYS8AUAAAAAwGaWBryy+VSMG/6utWo3GcEqKXW80rlM3OX7uJc6XpMS1j/JoQwAAAAAgL2szvDiZr97rYJI1yQsX1YdrzmXt+O4UWNM9j6wcxkAAAAAAMAEqwNe3Oz3IJtPnWy/hXkxMuNFKSs4GOmxV7MeaErT27i4PCG6lMqovxUzlgUAAAAAAGxCwMvdGrWfzBEWZXRrnPNAO845fOxyHgAAAAAAYCPLAl7ZfCqpyMmQ8bNGmUFflbj8nmqBpXMZkTkV90A7zhi1xswg4AUAAAAAgEdZmeF1lebtWcIIHB4lc3TFUWPEx24956G27DjLa3F5IiHaXuZ+NJYJAAAAAABsYGXAi6wW2e14UHNrQPLye8kYm/VQO846fOxyPgAAAAAAYBPNioVm8ynZGTJ+JgIlbxo/P23B8kUQ7Q2zT0rnMuOKt4I4Zup4XbZg/WKZb3M4w4uuZXJ1WgEA10EAAOAlVmV4kc0iT9IIIApfsWD5F5WF+dEunjfnsXaMp3OZtsfl4vKEqDs3acH6J41lAwAAAAAAi2kWLfcyTSvVlLIwf08RNbesIeqCmc0+mvFgO76oT6+0mcfKYK1Y9rtuaIhsPnVOORgUQQT3zhnTUTv6dNt4vHV99uZtTkMAAACg6efrIeOz9RV9Ovz5uFuHn7P1z9e3aDXAWpoFJ7pVGTJ+JgIlpy1cvqjjZTbgNefBduykjpeVx65YtmMBL/3cFAGuZ5WDYOW5Dp4yc+S54o");
+ WriteLiteral(@"05r0/v6G/OeU5JAAAA+J3xJfKs8Rm7k/uIK0eeKx7eOfIZe5sWBeSyIsOL7ozWtOkTFi7fVFfJdC4z59V2FLXHcukbHzl0/DpybuhvpuKN9WtH32C7MGi8kT9rBL9E3bef8cYMAAAAvzE+X7+g9N7rZcaYdvRl8vkakMyKGl5kd0lWKGvaYKhsbbDkYARIMxdmr2q67YvLEyIDyso6WzFjHXa9EU/p0w/0H8V0ReKiRfBLBNBu6Mt/gTMUAAAAfiAyuo58vpZ5T3T08/XXjO6RAHpkRcCLDC/Jfr16ZjSmVYYtXs1XTcw75+HmnHP42LXl/BBvlPrDDxW5ga5Gb8zf0Nf1I6O7JAAAANCXjC96f2TD5+v9z/F8vgZ6F5R8ERA384xEJ9nSenw0rFYHLF7NU53MZIx0OO7h5mxVx8vzAS/xbZAIQBlvlHYRWZ0/0Nf7LGcrAAAA+onx+VoMfPUN5SAgZQdRG+yHfL4GeiM7w4sotAXuFwZG1EA9FFGrUQtXM6oszF/sYL5ZjzdnvFENssXliYT+kLBh/QljXVa8GYvz74biTLdi8eb/Em/KAAAA6BdG10LZ3RfNEJ+vX2JPAN2RHfCiO6Nk790fHS3XgvuDC7ikW+NcHzTrjMPHrvR1GcEu8WY86HDbEvQCAACA5x0Jdjldo/pZgl5Ad4ISLwhJxZ4MGV9Z2RoaOfw5qlWsLl7YsnB9OpeJK94uWH+oUdDuso3rl7quI2/Ggy5pX4JeAAAA8CwXBbsOiaDXN9kzgDkyM7wYndECH20Njx7+HArWolqwFrJwdReVhfnRFn+f65NmHU/nMg/rkC0uT8RsPn4njXXK4qZg16GXKLQJAAAAj3rJhfe3z/OlMmCOzIDXVZpTrpWtoaFCWXukbldUqzhZvH62j5r3aPDOia64UtZpfNPj1mDzd7VQmRMZAAAAnmGMxujWXi3f1LfvHHsJ6IyUgJd+0olslSTNKdcHDxInsq0cruM110fN63TAa1LCeSeGRH7exW187vGnf8");
+ WriteLiteral(@"mJDAAAAE8wgklfc/Em7g8UxZ4COiMrw4ti9Ra4szl8IuAVVatWB7wa1vFK5zJiH8f7qHlnjJpkghMZUjLOGdf34x+7sKzEz9znZAYAAIAXiM/Xgy7fxivZfGqWXQW0R8DLpVZ3o9GNYqRhkfrBUNnaoNfCfKOg11wfNvPs4vKEOHZjDqw7pq+76xpXRv99T9TNu/DF9zmhAQAA4GpG7wmvDND1DfYY0J6sgBcF6yVb2jg10uxvDnVr7MeAl3hNThZW7yVQ/IJXGjl++r4yGF/npAYAAICbveChbT1HAXugvZ4DXvqJ5lSGTF9bWo83HS0xrFZtLVxvjGjYj1l8Mw6/rukuzzkRpPNUkPkLl/6ekxoAAACuZNTumvHYZhPwAtqQkeFFd0YL3N0eahrwUgP1UEStRi1c/aiyMH/xyP9n+rGNR0LF8fVyxMnAUWJxeSLRxfOe91pbnz73MSc1AAAA3MqLNbGuMGIj0JomYRlTNKNc790fHW03j+jWWKyqexZuhujWeMf4uR+7Mypj4V3ls1JsfCRUXHNwM0TA+G2zb25ubtfdihb8/c5gZH0vEnqw/vk4B4FzK8rug1ElWo5ykgMAAMB5C/OiZvLk//74d/+6UgueOfz1xfhWIapWao8N7RRd/gpEoO4NdiTQWE8Br2w+ldQfEjSjXLfXT7UNeEW1ypBSjFg5/N1Xjlw8+3IUkEsDm8pmJSy6a95ycDMuKyYCXkZ3Rtd9k7O2Fw393SdfGPl4eyi2uhuNPLzAFD8PbpWHPlVK2pai1lRlsDSgPP3tP//F6PaZf/vWjfQvOesBAABgi4V58Vn6WeMeZzIYqAff+Tj5SBKH/rn29MN7hlOb21Mj6zt/eO7epgtfzRWFgBfQVK8ZXnRntMC9wsBIu3lCwVpUC9ZClVqwbNFmXNTfDEbTX77zJf3neL+1sd5+okujUqypiVJNDYeD1ZJDmzK5uDwRe/LS0m6n87upHT/ZHoz8rzsXx/THtnX8gmp1/7EarCqb0S0xffnB0N");
+ WriteLiteral(@"ovLrz0oz9Zee1b3+PMB+Bn1zK5P9Mf/h0tAQAWOQh0fU05VvtKvy+ItHra8kZ8SExvrZwf+4dn76+nxldWXfSqrrBjgRb3oD0+n4CXZCtbQ0OFstZRn6+oVrGjeP1z/djO56PbD3++VxoYd3hzzJxHrsnu+h9Ll8b+4tdPX+wk2CWoWuXE70pqWdmJFF5J/IfsL/ZCe1wAAPjZ1/Xp5zQDAFhgYV4Eum4oDQq9twt4PfzcWlWDIvPr9V9eubS0ccotg7YNZvOpIXYw0FjXGV76iSW6MiZpQrk+eJAY7XReUcdruxTesHBzRB2vvqzflYzuPPx5rRy5cCG6ddvBzRFZW+92OK/j3+KIGl3/+TdPXTjadbFTgUBdqdcDjf705d+dXVIurCWVkcIpLgQAWrqWydX79KV9mb0LwMfXQPkOanR9t9Vn6EBAUc0sUr//Cv23xS9e+GcXPr7/T5KfrLvgVYp7iVvsbOCkXro0UqzeAnc2hzsOeEXV6rClB0ew9k/1h/F+bOez4cLDnwtVzemsKc9kSooujH+5+KUL4huubp4vujVWK80vOyuJu/uPBL0AAADQk4Ng1w+UNiVBwmq1q2ytt1fOj90vDET+6Inbn9LYgDv10qWR7oySre5GoxvFiKmU1MFQ2bKgVzRUHVvfifRdO4vujKKG16FaPRD+rBRzMugVW1yecH0AWWR2/fXvph7rNtjVKRH0Wh/Y4IIAAACA7nQY7OrV4oNE/OZHF87Q4IA7dXXjms2nRBScgJdkSxunRsw+R3RrtGp7QlotsV4I9107j0VO1odfLUW9VMfLEX/1wReTIoXbjnWJoBc1vQAAANCllxSbBnsSdb1+s3qaOlqAC3WbqTFJ08m3tB4fNfucsFq1pHB9MFDXwmrt7NZuqO/a+Wj9rkM71RDdGlsQ31x1WpxeljtnVvZHdAQAAAA6tjAvCtPP2LnKN5cfPyd6Q9D4gLt0e1KS3WWBu9tDpgNeaqAeiqjVqOxtiYSqp8VjsaIqhZLWN208Ei");
+ WriteLiteral(@"oqg2r5xO+LNTWxUw05+c1MYnF5IuHGNlvbi4Z+dW9sRMaymhSsb0iM4Lg6tMaFAQAAAJ056Mr4TTNPqdYC5V5XK0p+/M/lx8fYAYC7EPByiffuj452+1wrujVGtNrDC3Y/1fEaC+82/dv9YuyCw5vXyXlle1HMt1fOn5ZVt6tWNTUIjrI6+IAsLwAAAHTqeX0aNPOEaj1YkbFiUc9LfFHswGveYbcDjZm+ic3mU0n9IUbTyXV7/VTXAa+oVpGemRRSaw+zjdZ3+qeO16WBzaZ/26qGne7WeLmDeWwNeInU7KWNuJTjq14zHzMTwS4K2AMAAKBDz5r+vFnvPcPr0N998oURu1/w9dmbt9ntQGPd9FW7SrPJd68w0PXFMRSsRbVgLVSpBaVcrMNqbTgYqD8MaooujaWKqoQ1b2faiJEZRZfGZrYrIacL108uLk/Enry0tNtinlt2btBvV08POZXddWhtYEM5s32aiwTQpWuZXJ1WAMA1EH1vYX5W/9f0F9jlarAoaxM+3IiL7LL7Nr7qD9nxQHPd3MhSsF6yla2hoUJZ66kOV1SrSCteH9aqJ2pJ9UPx+vPR7bbzfFIcdPtojba+qd1eHxmUtaxqtbtjSIzWWFbLXCgAAADQypVunlSuBYv1eqAmYwPEiOafbA9aVg+mVg8Ei1U1djjd2RxeVRbmr7DrgcZMZXhl8ykRCEnSbHJ98CAx2usyRB0v/QIrpe9XNFQ9sY/XCmHlzPCep9u50eiMx22Ww+cei+x85OBmioDyu83+eH325rZ+Hn6o2BR4/mw3Ku0Nu1rpfvCD3dCeEqqGuFgAAACg1eforhSrwe2oVo3L2Ij/txmPPTa0Iy1rTAS59irqUKGijVRqwUc+m7+5/Pi/0B/+kbIwL/77jj69obz86i0OBeCA2TtQitVb4M7mcM8Br6halVK4Phioa2qwfmJZ/VC4/my40HaerUpYZHi96+BmdnKOvalP37BjY8S3VDKWI+p3ddulUdgLF5X43jAXCwvQ1QMA10");
+ WriteLiteral(@"Gug0Cf6DrTaa+q7sgKeG0WI9K+pd2raEMbpdC5ej1womdWqarWfrN6+mgXlpn9aWFeBLx+rLz8KrW94HtmuzRO0WRyre5GoxvFiJSi4IOhcs8RgVioerbZ37wc9BLdGUUNr3bK9eDgejmScHBTY4vLE+3Oszft2BCZ6djlUk89dpVqgJEaAQAAYI29irYtq3j9/d2YlM/Q68XIufVi+LFGwS5haSPerF6LCPz9UFmYf5Y9C7/rOOCVzadEEXMyvCRb2jglbSQP0a2x12WEtFrTYM96wbujNY5Fdjt/cylHnB6tseV5Jro1KjYEvfaqWk/F6gP1wH4Gqf4mrVRKvR07oo4XAAAAYNln34q25ZZtEcGuvYraMuPs7ZXzD9os5iWCXvA7Mze0BLsssLQeH5W1rLBaHZCwjKYZXl4uXN9J/a5DG5WI2wvXCz91b2sHgsFK6IxailwU/6uUovtBLwAAAMCtdsramqzi9b3YKIbH2gW7Fh8kNtf2op1kpL1EUXv4mZmAF6MzWuDu9pC0gJcaqIciarXrvmORUDURCDSv61asqEqhpHmujUdCRWXQxCh/hap2rlRTnUxnSywuT7TsVnl99uan+sPPrNyIqFox/YYfrGqntGLkYrCqnhL/F4GucrH3rO7B0iAXCwAAAFimVg/UdsraqpPbIEZe3K1oLXsAidpdHWR3HfWKsjA/xB6GH5Hh5aD37o+Oyl5mL90ao1rz7K5DXqzjNRbeNf2ce6UBr2R5fWrVBpgZXSZQC8a0UuRisKKdOXpdKe4Okt0FAAAAT9guh9YrtWBPIyxeGN7a7fa5W6XQWLt5fnVvbL3D7K5D4pvj59m78KOOAl7ZfEoU0Y7RXHLdXj8lPeAV1SpdR+9Daq1tsfb1He/V8bo0sGn6OZuVkNN1vK62m8Go5fWalRsxFC61fDMVdbrUcjipT48pRs2uQ6JuV7UspxtstBThggEAAIBWbslYyEYx/GkvXRsjarWr0ZZEdlelFmz5ofeT7cHdmx9d6CYL7Q");
+ WriteLiteral(@"UOD/hRpxleZHdZ4F5hYET2MkPBWlQL1kxHGfTnxNRgvW12mOjSWKqonmljMTKj6NJo1lYlfMHhTU8uLk+0DTJfn715MOywRc4P7TT5hioQVMuhMVGnK1ALnuhGW6uqSmlvQNp2DJYGuGAAAACglQ9lLKRcCxa3yqH73T7/8fhmVxleuxWtZd0u0ZXxrz744t1uP04rC/OzHCLwGwJeDlnZGhoqlLWoFcuOahXT0YGI1j6765CXitefj2539bxaPRBeL0cSDm9+R+fd9dmbbygWjdrYKCU7WNESok5XoKY2DJCKLox7O8PSujJGy1FFralcNAAAANDKLVkL0u/TNrdL5oNeoneEmbIgR1VqgabZXSLY9ZeLX1rZrWi9FNWnJjd8p23AK5tPiZv+BE0l1wcPEqNWLbubOl7hUHWs03nXCt7p1mhmdMbjPivFvFDHa9/12Zuia6P0oNdTZx5sh9Xq/htroKYO7tfpqmqJZtcO2cEuIVE4xQUDAAAArb38al6RWN9W1PParWjrZp7z9JkHW92ur1l3xsNg1yfbg8UeXxKjNcJ3OsnwIrvLAnc2hy0LeEXVqvmAl1o72+m8XipcfzZc6Pq5m5Ww0wEvU9/CGEEvqSM3xrRK7crptb2DOl2hc8frdB0lujHubsf3H2URmV0jBLwAAADQGalfAG8Uw/c3S+GOg2hXxj7blLl+icEuwJc6CXhdpZnkWt2NRjeKEUuHhh0MlTsOesXClbNml++FoJfozihqeHWrWFMTO9WQk0P4xhaXJ0wFnK/P3nxdOShkvyNrI6aC6mV9b7fsfiuK0+9ndtWCUhvgzM5pujMCAACgUz+T+TlYEN0b1/YiK+0K2V8Z+2w9Ed0ry1qvKFD/k1/9gyWCXUD3Wt6dZvMpUTQ7STPJtbRxasTqdZjp1hhWa6a7rK57oFvjWGS352WslaNOj9Y4ZfYJ12dvim+2vqVIqmMQDtaUyyONs7P3uzAWhvYnmd0Y99dbDSlnN0e5YAAAAKAzL79qySjmxa");
+ WriteLiteral(@"q6e383uqQ/bje+n6rWro2vrMpYl8jqenvl/P2/+PXTvdbsAnyvXToG3RktsLQet/wuXr/odly4PqzVxswu3wuF63up33VorRxxerTGrs7B67M3P9Wnf6//+H1FQi2DJ+Pbytno518uieBWuRhTdrdO7Wd3WeHi6gUuFgAAADDnoJaX9Nq2tXqgtrYX+URke5VrwUe+Wf+jJz68K0qB9LJ8NVAvLz5IbP6n95++83/uPrZuQct8yMEBvyHg5YC720OWB7z0C2YoolbbjgIZVmvDwUA9Znb5xYqqFEqaa9t4JFRUBtXeM4oLVc3pDK/E4vJE11mW12dv5vUpoxx80/VOLxty7ewD5ZRWfRjoKu1FpWd1HbqwltwfnREAAADowo8ViwI8IttrdTe6IgJfIuPrXz5+59OJUxu9dC0R39L/7M/f/4P/8re/m/p0bS9atqhNbnFYwG/aRSwYulSy9+6P2tZHS3Rr1C/Ce63mCWvVrkfgFHW8BsIVV7bzWHhXynJq9UD4k+Lg+GORnY8cfDniPLzbywKMbo5vZvMpEcATI7TMGsttG9DbfDCmbK6eVdY+TSqntoeVB6N3lHpoz7IXK4JdFKoHAABA10TXxoV50dvhB1bd04rAlz795A/P3RN1w2aMz9hXOvl8rRwEn0RATnw5fRCIWvjn4vP5P7aoRXYUAl7woabpGfqNscjuytBEcv313089tbwR/4Id6yrXgnu/3xlcajXPmcHiVa2LGl6CCHb9wYU1V7bzs2N39rO8ZDgd2vvtk0Nr7zr4cu4+eWnpP1q1cP1c/1GTDwI/1d+Af3otk6sf/WU1WFWWRu8oe5KDXqI4/WMb5wh2AQAA+NBbN9Lyuw4szIsBqF5SDgJSsr2mvPzqm00+X4tA25UGf/q+6IHRYntvKJ0FzMx6U9/W1zjK4DetujRO0Tzy3SsMjNi1rlCwFtWCtaYFloKButZtsEsQXRpLFfeNoCdGZpQV7BK2KuFxh19ScnF5Imbh8rfNzCwCU0/cm1DObr");
+ WriteLiteral(@"Uu/VbV98PRqR6oN513sDigTHx2kWAXAAAA5BGZXi+/+ifKQRfHVgV+h45NrUboEplZ324W7Orxc/ePLWiFHYuWC7heq4AX9bskW9kaGiqUNVsLE0W1StPi9ZFQ9XSvy3dj8frz0W2pyyvXg4M71dCQwy/LdeejGEHxyd8/oSSaBKlEBtjRqaKe7P4qRmIUXRgnPnucml0AAACwxsuvvqEcjGLeLEg1eWxqlBQgBoISWV3f0qfbFm2nyP56R/JSXzdGrwR8p2ENr2w+lWxykqMHHzxIjNq9TlHHa7sU3mj0t0gXozMet1YIK2eG91zVzjJGZzzufjF2YXCg/FsHX5YIeL3rtmM6VA0p59eSytnNMWUztqWsDWy07eooglwio2t4d1iJ7w1zYQAAAID1Xn71IGC1MC+ynZ5VPq+71Yq4sTgIQh0Eo+wguh7Kqj32ZpeZaEBf0FrcXEOyO5vDtge8omq1aUQhrFbP9rp8Ubjebc6GC9KXuVUNi770Tga8XD2AhAh8ndk+vT/tfzKIFJT3zv/mkXlGdk7tZ3OJeQEAAABHHGQ7vWFMom6WCHr992NziSDRa0aQzP7tk1Nw/2f6sl5nh8PPmnVpJOAl2epuNLpRjDjSLW4wVD4R9AqrteFAoO0onR1xU9BLdGcUNbxk266Exks1NezgS4stLk945rwUGVwnjrlqmGAXAAAA3OXlVxuNXvipI8Guz7dpe7/rpKL8tMFfxT3JuSPTyW1XlO8T7AIaBLyy+ZToypikaeRa2jg14tS6RbfG47+LhnrP7jq0Xgi7pp3HIruWLXu1HD3n8MtjIAkAAADAL15+VQS8MorI1vq86H6zgJcopi+6a37bxu6XgKtp3FTbY2k9PurUusNq9US6TVirjslavpsK11tRv+vQWjky/lhk5yMHX57I8PobziYAAADAJw4yzV7fnxbmxb26qD/23SNzfF+fbjuakQa4VLDJTTUku7s95FjASw3UQxG1+nAIvGCgrqnBurRq4cWKqhRKmuNtPB");
+ WriteLiteral(@"IqKoNq2bLlFyohpzO8EovLE2RfAgAAAH50MDrkrWO/yxPsAhoj4GWD9+6Pjjq9DUe7NcYkdmc85IY6XmPhXUuXX64HB9fLEadHL53kjAIAAAAAoLVHAl7ZfIpglwVur59yPOAV1SoPC+aHQ/K6Mx5a33G+jtelgU3L1/FZKTbu8Mu8yhkFAAAAAEBrxzO8CHhZ4F5hYMTpbQgFa1EtWAsZP5+WvXzRpbFUUZ18fftdGq22WQk7HfBKLi5PxDirAAAAAABo7njAi4L1kq1sDQ0VylrUDdsS1SoDkVA1EQgolhTccrJ4/fnoti3rKdbURKmmOp3ORmAaAAAAAIAWHga8svmUKIadoEnk+uBBYtQt2yLqeEU1+fW7Dq0VnIsDWTk643H3SgNOZ3kR8AIAAAAAoIWjGV7UBrLAnc1h1wS8omp1OKzVxqxavihcX60FHHltZ8MF29a16fxojRSuBwAAAACghf8vwABfcA5F9k0oGQAAAABJRU5ErkJggg==""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 46181, "\"", 46247, 1);
+#line 182 "WelcomePage.cshtml"
+WriteAttributeValue("", 46208, Resources.WelcomePageImageText_Skyline, 46208, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 46248, "\"", 46295, 1);
+#line 182 "WelcomePage.cshtml"
+WriteAttributeValue("", 46256, Resources.WelcomePageImageText_Skyline, 46256, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" width=\"1212\" height=\"202\" /></div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"content\">\r\n <div class=\"bodyHeadline\">");
+#line 187 "WelcomePage.cshtml"
+ Write(Resources.WelcomeHeader);
+
+#line default
+#line hidden
+ WriteLiteral("</div>\r\n <div class=\"bodyContent\">");
+#line 188 "WelcomePage.cshtml"
+ Write(Resources.WelcomeStarted);
+
+#line default
+#line hidden
+ WriteLiteral("</div>\r\n <a class=\"bodyCTA longer\" href=\"http://go.microsoft.com/fwlink/?LinkID=398596&amp;clcid=0x409\">");
+#line 189 "WelcomePage.cshtml"
+ Write(Resources.WelcomeLearnMicrosoftAspNet);
+
+#line default
+#line hidden
+ WriteLiteral(@"<div>
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAAdCAYAAAD7En+mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMjVCMDEwM0FBQkExMUUyQjdGNEEwODg0RjhFODY4OCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMjVCMDEwNEFBQkExMUUyQjdGNEEwODg0RjhFODY4OCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyNUIwM");
+ WriteLiteral(@"TAxQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMyNUIwMTAyQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+I1MZRAAAA4FJREFUeNq8md1x2kAQx3UaCoAKAg0YkgYQHTgV2KoAeMqjncfkBbsCSAd0AK5AOA1Y6YAOyJ7mf5rV6T4l8M3c2ALd/fZ/u/exh0gCy+VyyeiPrHPtqzPVd6p7IcTJ0rbxTO8FMe++/vIy/xY/TiF9CY+4If1ZUV1SHQb0V1L9Q/WFxJy7CCVxnZkk+hwtlIyTsCcGk53sMZJ8FGcY8Ux7NydB+xihJLI3k8Tug4TCi1uq92zEfpJxu4DwfoShY3y0o3a5Tyi82GKS0buA8G4xqV3uFAqRB4xYAoHPSURBH08IvwosR9omFCIbTDI0iok+GkxdrC5UjuqjHnpdCry7xaOcs2uLkQ2mLfQCBTeY1Ne6JRRzcoPH731EWsQuqM+jYU7WzD4iLWIX1GfFTLVwU+HaG4gQ3WExSRhcDzcVrldhYl63mAJCnwEtybiJx0tjNvETtCk9c/YDq2OuFjUSWjPJuInHSy0mtSk9c7ZmSvEpvntQIxswaGeE2wG1IDEzh1fl+694XLKvejFJzMzh1RZTwMhCdkZGjSJWVr5SnjEHT44o+MDjaPrt91gxyahRxMraYtpORoiCmpmyvesYMfcqCNvEKyNsnkVoq3ezLkx4qcW0eRahXTOl0C94eI9caGxiM0uTEzvVdGI6xHqZAzbJT4aQO8ADoUWJzQ0nqX/sfyuTjO7EpHa54SRVM1Ntwl+rbALf+zTmwDDKJtf7SqYZvwhs92nMATrOTFDbsY2F9gwrITdywVM0Vqbae0YmP7ZZVlMj05KiTXnoqjieRx7vFHAYIDJh28KxK5OJH");
+ WriteLiteral(@"AaIbDCFvsc5DO0sku3VMkqEvse5EuauIvF+gSgRqbbHrW7gSX4i2hn2uNUNPNliCi3LkA0nIV6NDPFCz2BYllExQ7waGeIFz2BSlmWUGLHtFUXy/o48TcOed3Umu62omI00DUVl5PdIwK+1t81UUm34vmYiAb8ZUzgS5eq+p4cnN7g5cCbyWqJsvO+J8GSDyXNc0+XYlr18RA5ZRt7/btjSnqsFwXE51mK68k3L/W+DqR8HRcAViFq5Xm1pGBP4wAyu751Crjs1z9ZM1wU1BLaYptsK4VktN9pRq0R9Y5/NMZL8slmC1ioSIu51ezNtkSACQ3HJckjXAX1v8nzsTxLwVBTT99OEiFxkMsNIVpu/J6yjhBpEG5mhv7vI8l+AAQB7WiwH/DuungAAAABJRU5ErkJggg==""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 49127, "\"", 49191, 1);
+#line 191 "WelcomePage.cshtml"
+WriteAttributeValue("", 49150, Resources.WelcomePageImageText_LearnMore, 49150, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 49192, "\"", 49241, 1);
+#line 191 "WelcomePage.cshtml"
+WriteAttributeValue("", 49200, Resources.WelcomePageImageText_LearnMore, 49200, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" width=\"58\" height=\"29\" /></div>\r\n </a>\r\n </div>\r\n\r\n</body>\r\n</html>\r\n");
+ }
+ #pragma warning restore 1998
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cshtml b/src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cshtml
new file mode 100644
index 0000000000..9cfb311cbd
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/WelcomePage/Views/Obsolete/WelcomePage.cshtml
@@ -0,0 +1,196 @@
+@using System
+@using Microsoft.AspNetCore.Diagnostics
+@{
+ Response.ContentType = "text/html";
+}
+<!DOCTYPE html>
+<html lang="@System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName">
+<head>
+ <meta charset="utf-8" />
+ <title>@Resources.WelcomeTitle</title>
+ <style type="text/css">
+ @@font-face {
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ }
+
+ body {
+ background-color: #00abec;
+ color: #fff;
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-size: 18px;
+ margin: 0;
+ padding: 0;
+ }
+
+ .content {
+ position: absolute;
+ left: 50px;
+ top: 38px;
+ width: 440px;
+ }
+
+ .content .azureLogo {
+ margin: 0 0 65px 0;
+ }
+
+ .content .bodyHeadline {
+ margin: 35px 0 0;
+ font-size: 40px;
+ line-height: 43px;
+ }
+
+ .content .bodyContent {
+ margin: 10px 0 30px 0;
+ line-height: 22px;
+ }
+
+ .content .bodyContent a {
+ color: #fff;
+ text-decoration: none;
+ }
+
+ .content .bodyContent a:hover {
+ opacity: .7;
+ }
+
+ .content .bodyCTA {
+ color: #fff;
+ display: block;
+ line-height: 30px;
+ height: 29px;
+ width: 230px;
+ cursor: pointer;
+ text-decoration: none;
+ position: relative;
+ }
+
+ .content .bodyCTA.longer {
+ margin-top: 10px;
+ width: 440px;
+ }
+
+ .content .bodyCTA div {
+ position: absolute;
+ overflow: hidden;
+ width: 29px;
+ height: 29px;
+ float: right;
+ top: 0;
+ right: 0;
+ }
+
+ .content .bodyCTA div img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ border: 0;
+ }
+
+ .content .bodyCTA:hover div img {
+ left: -29px;
+ }
+
+ .content .bodyCTA:hover {
+ opacity: .7;
+ }
+
+ .wrapper {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ min-width: 1200px;
+ }
+
+ .innerwrapper {
+ width: 384px;
+ height: 100%;
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ .browser {
+ position: absolute;
+ display: block;
+ top: 400px;
+ width: 384px;
+ height: 305px;
+ cursor: default;
+ z-index: 10;
+ }
+
+ .browser div {
+ width: 384px;
+ height: 305px;
+ position: absolute;
+ top: 40px;
+ left: 100px;
+ font-size: 200px;
+ text-align: left;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .bulb {
+ position: fixed;
+ margin-left: 20px;
+ top: 0;
+ }
+
+ .light {
+ position: fixed;
+ margin-left: 53px;
+ top: 0;
+ opacity: 1;
+ }
+
+ .bottom {
+ position: fixed;
+ bottom: 0;
+ margin-right: auto;
+ margin-left: -303px;
+ z-index: -1;
+ height: 202px;
+ }
+ </style>
+ <script>
+ </script>
+</head>
+<body>
+ <div class="wrapper">
+ <div class="innerwrapper">
+ <div class="light first">
+ <img src="data:image/png;base64,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"
+ alt="@Resources.WelcomePageImageText_LightBulb" title="@Resources.WelcomePageImageText_LightBulb" width="274" height="274" /></div>
+ <div class="browser" style="opacity: 1; visible: true;">
+ <img src="data:image/png;base64,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"
+ alt="@Resources.WelcomePageImageText_Browser" title="@Resources.WelcomePageImageText_Browser" width="384" height="305" /><div>:-)</div>
+ </div>
+ <div class="light second">
+ <img src="data:image/png;base64,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"
+ alt="@Resources.WelcomePageImageText_LightBulb" title="@Resources.WelcomePageImageText_LightBulb" width="274" height="274" /></div>
+ <div class="bulb">
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVoAAAKSCAYAAACTAhMyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2OEMyQkI4M0Q4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2OEMyQkI4NEQ4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY4QzJCQjgxRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY4QzJCQjgyRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+zKbSDwAAExlJREFUeNrs3b1u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"
+ alt="@Resources.WelcomePageImageText_LightBulb" title="@Resources.WelcomePageImageText_LightBulb" width="346" height="658" /></div>
+ <div class="bottom">
+ <img src="data:image/png;base64,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"
+ alt="@Resources.WelcomePageImageText_Skyline" title="@Resources.WelcomePageImageText_Skyline" width="1212" height="202" /></div>
+ </div>
+ </div>
+
+ <div class="content">
+ <div class="bodyHeadline">@Resources.WelcomeHeader</div>
+ <div class="bodyContent">@Resources.WelcomeStarted</div>
+ <a class="bodyCTA longer" href="http://go.microsoft.com/fwlink/?LinkID=398596&amp;clcid=0x409">@Resources.WelcomeLearnMicrosoftAspNet<div>
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAAdCAYAAAD7En+mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMjVCMDEwM0FBQkExMUUyQjdGNEEwODg0RjhFODY4OCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMjVCMDEwNEFBQkExMUUyQjdGNEEwODg0RjhFODY4OCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyNUIwMTAxQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMyNUIwMTAyQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+I1MZRAAAA4FJREFUeNq8md1x2kAQx3UaCoAKAg0YkgYQHTgV2KoAeMqjncfkBbsCSAd0AK5AOA1Y6YAOyJ7mf5rV6T4l8M3c2ALd/fZ/u/exh0gCy+VyyeiPrHPtqzPVd6p7IcTJ0rbxTO8FMe++/vIy/xY/TiF9CY+4If1ZUV1SHQb0V1L9Q/WFxJy7CCVxnZkk+hwtlIyTsCcGk53sMZJ8FGcY8Ux7NydB+xihJLI3k8Tug4TCi1uq92zEfpJxu4DwfoShY3y0o3a5Tyi82GKS0buA8G4xqV3uFAqRB4xYAoHPSURBH08IvwosR9omFCIbTDI0iok+GkxdrC5UjuqjHnpdCry7xaOcs2uLkQ2mLfQCBTeY1Ne6JRRzcoPH731EWsQuqM+jYU7WzD4iLWIX1GfFTLVwU+HaG4gQ3WExSRhcDzcVrldhYl63mAJCnwEtybiJx0tjNvETtCk9c/YDq2OuFjUSWjPJuInHSy0mtSk9c7ZmSvEpvntQIxswaGeE2wG1IDEzh1fl+694XLKvejFJzMzh1RZTwMhCdkZGjSJWVr5SnjEHT44o+MDjaPrt91gxyahRxMraYtpORoiCmpmyvesYMfcqCNvEKyNsnkVoq3ezLkx4qcW0eRahXTOl0C94eI9caGxiM0uTEzvVdGI6xHqZAzbJT4aQO8ADoUWJzQ0nqX/sfyuTjO7EpHa54SRVM1Ntwl+rbALf+zTmwDDKJtf7SqYZvwhs92nMATrOTFDbsY2F9gwrITdywVM0Vqbae0YmP7ZZVlMj05KiTXnoqjieRx7vFHAYIDJh28KxK5OJHAaIbDCFvsc5DO0sku3VMkqEvse5EuauIvF+gSgRqbbHrW7gSX4i2hn2uNUNPNliCi3LkA0nIV6NDPFCz2BYllExQ7waGeIFz2BSlmWUGLHtFUXy/o48TcOed3Umu62omI00DUVl5PdIwK+1t81UUm34vmYiAb8ZUzgS5eq+p4cnN7g5cCbyWqJsvO+J8GSDyXNc0+XYlr18RA5ZRt7/btjSnqsFwXE51mK68k3L/W+DqR8HRcAViFq5Xm1pGBP4wAyu751Crjs1z9ZM1wU1BLaYptsK4VktN9pRq0R9Y5/NMZL8slmC1ioSIu51ezNtkSACQ3HJckjXAX1v8nzsTxLwVBTT99OEiFxkMsNIVpu/J6yjhBpEG5mhv7vI8l+AAQB7WiwH/DuungAAAABJRU5ErkJggg=="
+ alt="@Resources.WelcomePageImageText_LearnMore" title="@Resources.WelcomePageImageText_LearnMore" width="58" height="29" /></div>
+ </a>
+ </div>
+
+</body>
+</html>
diff --git a/src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.Designer.cs b/src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.Designer.cs
new file mode 100644
index 0000000000..bfba4d72c3
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.Designer.cs
@@ -0,0 +1,366 @@
+// <auto-generated/>
+#pragma warning disable 1591
+namespace Microsoft.AspNetCore.Diagnostics.RazorViews
+{
+ #line hidden
+#line 1 "WelcomePage.cshtml"
+using System;
+
+#line default
+#line hidden
+ using System.Threading.Tasks;
+#line 2 "WelcomePage.cshtml"
+using Microsoft.AspNetCore.Diagnostics;
+
+#line default
+#line hidden
+ internal class WelcomePage : Microsoft.Extensions.RazorViews.BaseView
+ {
+ #pragma warning disable 1998
+ public async override global::System.Threading.Tasks.Task ExecuteAsync()
+ {
+#line 3 "WelcomePage.cshtml"
+
+ Response.ContentType = "text/html; charset=utf-8";
+
+#line default
+#line hidden
+ WriteLiteral("<!DOCTYPE html>\r\n<html");
+ BeginWriteAttribute("lang", " lang=\"", 141, "\"", 223, 1);
+#line 7 "WelcomePage.cshtml"
+WriteAttributeValue("", 148, System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, 148, 75, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(">\r\n<head>\r\n <meta charset=\"utf-8\" />\r\n <title>");
+#line 10 "WelcomePage.cshtml"
+ Write(Resources.WelcomeTitle);
+
+#line default
+#line hidden
+ WriteLiteral("</title>\r\n <style type=\"text/css\">\r\n ");
+ WriteLiteral(@"@font-face {
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ }
+
+ body {
+ background-color: #00abec;
+ color: #fff;
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-size: 18px;
+ margin: 0;
+ padding: 0;
+ }
+
+ .content {
+ position: absolute;
+ left: 50px;
+ top: 38px;
+ width: 440px;
+ }
+
+ .content .azureLogo {
+ margin: 0 0 65px 0;
+ }
+
+ .content .bodyHeadline {
+ margin: 35px 0 0;
+ font-size: 40px;
+ line-height: 43px;
+ }
+
+ .content .bodyContent {
+ margin: 10px 0 30px 0;
+ line-height: 22px;
+ }
+
+ .content .bodyContent a {
+ color: #fff;
+ text-decorat");
+ WriteLiteral(@"ion: none;
+ }
+
+ .content .bodyContent a:hover {
+ opacity: .7;
+ }
+
+ .content .bodyCTA {
+ color: #fff;
+ display: block;
+ line-height: 30px;
+ height: 29px;
+ width: 230px;
+ cursor: pointer;
+ text-decoration: none;
+ position: relative;
+ }
+
+ .content .bodyCTA.longer {
+ margin-top: 10px;
+ width: 440px;
+ }
+
+ .content .bodyCTA div {
+ position: absolute;
+ overflow: hidden;
+ width: 29px;
+ height: 29px;
+ float: right;
+ top: 0;
+ right: 0;
+ }
+
+ .content .bodyCTA div img {
+ position: absolute;
+ ");
+ WriteLiteral(@" top: 0;
+ left: 0;
+ border: 0;
+ }
+
+ .content .bodyCTA:hover div img {
+ left: -29px;
+ }
+
+ .content .bodyCTA:hover {
+ opacity: .7;
+ }
+
+ .wrapper {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ min-width: 1200px;
+ }
+
+ .innerwrapper {
+ width: 384px;
+ height: 100%;
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ .browser {
+ position: absolute;
+ display: block;
+ top: 400px;
+ width: 384px;
+ height: 305px;
+ cursor: default;
+ z-index: 10;
+ }
+
+ .browser div {
+ width: 384px;
+ height: 305px;
+ position: absolute;
+ top: 40px;
+ ");
+ WriteLiteral(@" left: 100px;
+ font-size: 200px;
+ text-align: left;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .bulb {
+ position: fixed;
+ margin-left: 20px;
+ top: 0;
+ }
+
+ .light {
+ position: fixed;
+ margin-left: 53px;
+ top: 0;
+ opacity: 1;
+ }
+
+ .bottom {
+ position: fixed;
+ bottom: 0;
+ margin-right: auto;
+ margin-left: -303px;
+ z-index: -1;
+ height: 202px;
+ }
+ </style>
+ <script>
+ </script>
+</head>
+<body>
+ <div class=""wrapper"">
+ <div class=""innerwrapper"">
+ <div class=""light first"">
+ <img src=""data:image/png;base64,iVBORw");
+ WriteLiteral(@"0KGgoAAAANSUhEUgAAARIAAAESCAYAAAAxN1ojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACKhJREFUeNrs3TFPG0kYgOE1kou4IIVTUITiXJAiFFxBk/8vGopQQBEKKFyci7jAhRsX3E4YB3I5Asva3p2Z55EspJNOOq/3Xn8zu7arCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADI1MAhILi/v9+v/wwb/murwWCwcPQQknJCMa7/jOLj3X/+bsKqfiye/F3Gx6KOzcorICSkGY0P9WM/hmK/4/+kZYxLeHwXFyGh3+EYx0cK1lGZh4ewCAm7D0fYyziIjxCOYQZPaxajMqujsvQqCwnbj8dB5k83TCtTURESNheQdTgOCz0EsxiUqbNBSGg+fUzqx8dqc1dVUreKU8qtKUVI+HNAQjSOCp4+mkwpN3VQ5g6FkPAYkHGMh4A0E0LyTVCEpPSAhPs7jqt0LtkKCkJiCVNEUC7dti8kuQdkvYl65GhsVdiUvXKTm5DkGJFwCfdz5SrMroSIXNcxuXEohCSXZcxJZR+kK2GZ89VyR0hSjsh6GTN0NDp3EycUyx0hMYVgOhGSMiJyECNiCumvMJl8cxiEpI8BCeEIm6ku6aYhXCo+t9QRkr4tZU6r7r9AiGZCRM4sdYSkDxEZx4hYyqTrwqeLhaTLiIRlzIkjkYVpHZMLh0FIdh2REBD7IXmZxenEvomQbD0gNlXzFvZLzsRESLYdkS+VTdUSYnLuC5SERERoyxUdIRERxERIRAQxERIRQUzKsucQiAgvCufC3/GcQEga+SwiPBHOhS9iIiRNphE3m/FcTE4dBiF5TUT8LAR/Mo5vNAjJsxEJH8BzkvCSw/iGg5D8FpGRsZUGTuIbD0LyMyLDylcB0NypzVchecoVGt5iaIoVkvU0Er5j1XqXtwqbr5+EpOyIrL/tHdo4Kn2/pPSJxLe9s7Fzqe");
+ WriteLiteral(@"T9kmJDEn+8yq47m7L+YXghKWxJ44e82bRJqUucUicSSxoscYSk1TRyYEnDlpc4EyHJOyLrL26GbTqKy2chyXUNG98xYBfLZyHJcBqxwcoujUvaeC1pIhERdu1YSPKaRsLnaNwGz67tl/J1A6VMJMfOaUzCQtJmGgnrVJd76cqohKmkhInEkgZTiZC0mkZGQoKpREi8E2AyFpJOp5GhaYQeyfq+kpwnkolzF1OJkLT10XlL30KS6yeDswxJ/ISvz9RgKhGSVg6cr/TUX0KSxjRik5U+G8WPbAiJaQQsb4QEnKNC8sKyRkiwvBESpcfyRkiEBIo7V3MLia8LIKXlTTb3OmUTkvg5Br9Vg6lESFr54LzEBC0kXhSEREi8KNDQMJfLwFmEpNQfbsaSXEi8GBCYSLwY0Np7IREScO5mFBJfYkSyctjj2/MigDdCE4lpBOewkHgRIP19khxC8s55SOKS/4yYiQS6Z7PVRAKYSKAHUv/MzZ6XEHoh6X2SpEOS4++DgJCoOBS5RLe0ASEREkBIACEBEBJASAAhAYQEQEgAIQGEBHi0EhIHH9q6E5KODAaDhfMPhASwtPECgOk6j5BY3oCQmEgoXvJvhiYS8GYoJLWl85DEzYVESMBEYmkDnbtL/QkkH5LBYLAyleDNUEi8EJRsGd8MhURIoOxzN5eQfHc+kqh5Dk/CRALdusvhSWQRkrjGFBNSPHdNJJY3YFmTW0jmzksSMxMSIQFTdG4hifskYkIqljl9w19u35A2c35ighYSIaEUWZ2rWYWkHhXDZ25cBqbvVvW5KiQ9N3WeYhoREi8SQiIkvVjeuHpDXy1zW9bkOpFY3mAaEZKNTCUhJL5dnj66FRJTCbQxj0tvIVF+eLObXJ9YtiGJ5XcFh77IcpO1hIkk63cAknOd85PLOiTxS2NcCqZrq3gBQEgS9s15TMey36/LPiSmErqeRkpYYu8V8mKaSuhsGsnhd2uExFRCd8KVmiLexPYKelFNJezadSlPtJiQxKnE3a7syi");
+ WriteLiteral(@"L3KzWlTiTrdwifwWEXLkt6skWFJN7t6tZ5tm2ayw9fCcnzMQl7Jb6OkW0JE+9VaU96r9AX+9L5zpZclXC5V0iqnxuvPofDps1L2mA1kTwIG69L5z4bXNJclPrkiw1JHD/Pnf9syEWuX1okJC/HZFEVdNMQWzPL+btGhOR1MQlXcdw+z1stS17SCMmvzis3qtHcj+VxiVdphOT/p5JwIpw5EjR0FZfHxROSx5gsjKg0MC31Uq+QvByTcGI4OXhJuF/Em46Q/DEm4QSx+cpzwuTqtgEheZXzyudx+J3NVSFpNJWsN1/FhKcROSv5pjMheXtMvlYuC/MYEW8sQvKmmCziZCImZRMRIRETWrkQkVf8f+IQvM79/f1+/edL/Rg6GsUsZ85L+6YzIdldTE7rx8jRyD4iljNCstWYDONksu9oiAhC0jYmYTIZOxpZWcTljEu8QrLToJzUfw4diSzMKzebCUmHMZnUfz47Ekmb+uyMkPQhJuO41HFFJy0/fjrCp3iFpE8xsQmblmVcythUFZJeBuVT/efIkei18P2qF/ZDhCSFpU7YiHW/iaWMkNB6qRMmk4mj0QvzqvCfjBAS0wmmECERkx/TyaSyd7Jr06rQ3+IVkryDMorTiTtitytcibn0gTshKWG5c1y5VLxpYf/j2jJGSEoLymFc7tg/aScsXW7jryYiJILiaDQPSP24sQ8iJPwalPCwh/KKJUz18OPdAiIkPBOUcQyKTxb/ah6nj5lDISS8PijDGJO/Cl72hOkjhOPWzWRCQvuo7MeoHBQQlVWMx8z0ISSIingICYlEZRSDMo6PlL4TZR4f//g4v5DQv2nlQ/Vws9v7qj83vYX9jUUMx527ToWE9OIyjkugUfV4aXlbl5gXcZkyj3/vwj9zmVZIyH+CWS+H3jdYGq0j8XAimTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHL0rwADANq3ok68n5URAAAAAElFTkSuQm");
+ WriteLiteral("CC\"");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 7518, "\"", 7586, 1);
+#line 169 "WelcomePage.cshtml"
+WriteAttributeValue("", 7545, Resources.WelcomePageImageText_LightBulb, 7545, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 7587, "\"", 7636, 1);
+#line 169 "WelcomePage.cshtml"
+WriteAttributeValue("", 7595, Resources.WelcomePageImageText_LightBulb, 7595, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""274"" height=""274"" /></div>
+ <div class=""browser"" style=""opacity: 1; visible: true;"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAAExCAYAAAB1UXVvAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAGcxJREFUeNrsnUuPHcd5hqvJIU0bkcJQgkXGMOgYshHJsM0MhMBwEFlxsjDsjQBvsrQRL7LzT0j0C5xFFgogAzSQRTYGnEUCb+IwCWIohkFScSwnsayQkiwOb8O5cc45fS6d/nou6jOnL1Xd1dWXeh7iiNTMmZ4zfaa+t7737aoOVAZ/9q0/Px//9VL8uBI/vnT44ZcUAAB0nVupxxvx49p3X3v1ZtYTg4zC/5348XL8OM95BAAYBCIAr8RC8INMAYiLv8z2/5nCDwAwWK7GIvDNo/85nZr536D4AwAMmivr6y8EN67/9Jr8z6nDD36H4g8A4AV/EU/6P5EIwOHs/xucEwAAb/j2UQfwEucCAMArkrq/pg4u9WyMvQ//lrpz4XeO/z7Jhd076onRI3Vx8//UUzt3eFsAAJrnirg/IgBfsn3k8Mw59cuPrSePh09cKnxuWhTOzsbq8t031fpbP1K/EYsCAAA0JwIiANbCXyn8P7/8B+q/PvFFFa6dM//6tQ+E41LcEbz4s+8jBAAAzXD+9Pr6C6/aONLtZ55X//j731LvPf0pNT+1Vvt4Yhn9PBaSIP4jYgAAAFb5nzUbR3n9ua/FM/8vNvIKrz/75cQm+pMbf6vOTse8ZQAAljhV9wD/+tmvN1b8jxABkO5CLCYAAOiAAEjxF7/eBRImIwIAAB0QgBvP/rGz4n9SBAAAoCUBEEtGvPk2EBEQ8QEAgBYEQKyfNhHxefjkJd49AIAaGF8FJLNvuUSzbf7jd7+mvvqT17");
+ WriteLiteral(@"Se+/nPf453GgC84Y03/rMZAZBFXlW4cv60evm3z658/Afvh+rm1tz4eGJDyYM1AgAA1TCygCT0rbLC9wsX1qwW//TrAQAABwJw+5nnjL+BFP6vXDxjvfgfvJ7neQcBAJwIwEfNCq4Uf7F+mij+gnQjWTuMAgCARQEwLbRZxX88j9Srb0+sFP8jNi58kncRAKBJATC58iev+F+9HaqN8cLqD/DwyYu8iwAAFdC+CkhHAM6dDtQ3Lp9VF8+t6orU/awsQIertya5nwvXPsy7CADQpADUKf7C+TNB8gAAgG5wytaBvvLMmdzi3ySEwAAALQvAD+9O1X/vzp3/ACwEAwCohjULSELev3s3zL30c2saJQ8AAOiZAOjem1eu8VdqVQTEHXr94cx6l3B2NuJdBACogLYFdGH3jvZBsxZ6SUj8px/P7g7q8NTOBu8iAECTAvDUzp14tq1/T14Rgdc3Zysfz7OIqnJx823eRQCAJgVAuHz3TaOD/3BjemgJNSMCIkiEwAAALgTg3i+Mv4FYQXkiILuE1sFUkAAA4AOMKrAUXAmDTW8Ic5AHhCtbQsvK4PNng6RTqML6Wz/Sep7uzREAABCAkqJb5ZaQIgI3t+xdsfOpX1/XvjIJAABWMV4IJoX3KYMrgppAvH/d2T8AAFgSAOEPf/b9Vl/0+lv/xOwfAKANAZBLQl9sSQQu33tTfebWj3nnAADaEABBrCB5uESspxdb7j4AALwXAEGKsSsRkOL/1Z+8ps5Ox7xrAABtC8CRCDQ9KxeRofgDANjFym6gUqBlr6B/++zX1cMnLll7cXK1zxd+8Q/OrSYAAATAAAmGX/73v1a//Ni6uv7sl40Xi53kM7d/nFztw6wfAKDjApDuBuQhQnD7mefU7Y8+ry8icRdx9PUUfgCAngnASSEQ5LaNGxc+efzvI8Q2+lBc6OXvS5tvU/QBAIYgAGlkx86jXTt/j3MOANAJTnEKAAAQAAAAQAAAAAABAAAABAAAAIaD1lVAp55dV+O1c4f/F8");
+ WriteLiteral(@"R/UgQ6RwhWnxaUfYXSfkKg8dWB5jcJ9H4gAKjJaDZX79x9yIlogI/vvqM+Mtu3IwB3PnJJbX3o/ErhLCuVQXGlNTyGqQisfjQoOY5u6UciAOoxjyL1vw8eq/D805yMBnh69MCeAKwQpf4K8gtilPpXcLJ0Gh8j4zmpT0aZYhAtfST3WFHRMQxfEwCU8u72SIXzBSeiZeovBItSBbG0kEepohlUPEYVMVj9yqhACBADgOa4uzdR2+MZJ6IvAhAlfzQKXfTBjL7oedFSV9CuGJR1BYgBgD32wrnaiAUAetwBlBa6E/ZO3vNWLaIcMdASlOLXIp9cfV51MQg0zxFCAHCA+P63tvY5EX0XAKNCR15AVwAQ86vNfTVfRJyI3gmAzMSjQ/8+CKoXOvICxAC85P3dsRpN55yIPgpAunAdCYE1MbCRF9iwiFSWvUNeAFCXzdFU3X8cciJ62wGo7LbNihjYyAuMj1HyWgotInMxIC8AX5HFXjL7h14LwEGRKpzRGogBeQFdAQwfCX3f3R7j+w9BAExmquQFWEQA7+/g+w9KAEyLE3kBYgB+cn8/TLx/GIAAJHU8pzI6FYPKeUFQ4xh18gI9iyh7jUK9LgygLRLffwfff1AdQJT6R6DciYGdvCDfItI/Rs7rqWgRLR2LrgAGQrLY69GIEzE0ASgUg6D6TJW8AIsIhoMUfzZ5G5gARGWfi1LFp02LKF3IyQsQA3CKbPK2F7LJ2yA7gLLLQNNi0D+LKC0G5AUApmxPZmzyNlwBOEiBU2VZbxbqQAxcWkRmgoJFBH4glo/s7w9DFYDoZAsQ5ZXJcjEgLzAWAywi6DK3tkYs9hqyABzX/5XqUVgms4sPeUHhiyUvgD4hM38Wew29A8gqGCViQF5AXgDDRhZ6sdjLkw5ASwxWLKJyMSAvyHk95AXQYdjkzbMOoGiGGOV2BWkxIC8gL4AhcLTYC9/fIwHQKQomFpFW8SEvKHyx5AXQBuL7s9jLJwHI2QvIlhiQF5AXQD+QTd62xyz28k");
+ WriteLiteral(@"oAIo2qpGURZT6xRl4QVJ+pkhfQFYAZeyGbvPnZASwV6YxNzKx0BenvU98iMpmpWs0LbFhEjsQAiwh0SXz/rX1OhJ8CkG1qrMzqrYlBj/MCG7e4NBYUt3kBFpF/EPp6LABRh8SAvKD9vICuwC/kck82efO8A8gfzNHKLDIo+SLyAvIC6Aeyydv9xyEnwmsBiHQGc4mpQV5Q0d4hL4B2kMVebPKGAFSYzZMXGNky2vaOg7wg83JQ8gLfkND33e0xvj8CkLkZqEEBJy/QKnRdyQtKZ/TkBT4gl3uyyRsCUDxIjQs4eQF5AWLQddjkDQHQFoNqBZy8wFgMyAsQAwfg+yMA5RZQ2QDN+GAXLSJdMXBpEeUei7yAvKBhjjZ5AwQgUwEizaJRfTbfzbzA5ZbVWoWugbzAflew+pOQF3QbNnlDADQqjt5AJS+oLwZD3bI6973CImqNu3sTNnlDAJTeUEuNThMxIC8oFwPyAsTANbLJ20YsAIAAFDlA2YPYQAyGahGlxYC8IP+T5AXdQywfNnlDAIz7gECzM2jeInIjBl28xaVWoSMvoCso4NYWm7whABUo3g4iIC9Q5AU2LKLc9wqLqDayyRuLvRCA8mIf5V8GWjxkFXnBCYvo+HnkBcZiQF5gD1noxSZvoH8VUOXBTl6QJwbkBfmfJC9oDlnsJbN/gGoWUOXB3p28wIZFtCQGNiwiR2JAXuBvV8Amb1BfACrPHruTFxRaRKkPlhWFSMsiqiAGFvICKxaRViEnL+gLstgL3x+MBcD+7JG8oMwiOn5exbzAikVk3PGRF3SV+/shi73AXACiRmeP9cWAvMCRGJAX9DYvSHz/HXx/qNIBRAfz1yC3bNqaPZbkBUH5ACQvsCsG5AX97wrE9//VJou9oLIFFB2LwMFArXAduo28oKJFVFcM3OYFgX7xiTp2SalWIScvcI3s8EnoCzUEYLUb+GCgBg5mjz7lBT2+xaVxx+fAIsq0d/zJC2STt70Q3x9qCEBUJgZBtolBXmBXDLzLC2");
+ WriteLiteral(@"zc4rLU3hluXrA9mbHJG9jpAHRab2tiQF5AXpBz4sgL9JBN3rizF9i3gAzFgLzA3CJa+hLyAvICQ5I7e7HJG1izgKLqYkBe4M4i0io+5AWFL3YIeYFc7sliL7BrAckMMtDrDILcwU5e4EoM/MgLAktdo669Y2YR6diQpl1YGbLJmzwArFtA6U6gSAzIC/TEgLygbseXbxFV6xrrW0R576uLroBN3qBRAWhaDNxaRNnTPfICRV5gSQxcWkSJ78/1/tCEAESHf/LmkrbEoCgv6KJFlDnYyQvsW0TGHZ9/eYFc8SNX/gA0ZwFpGAtH4528QJEXtG4RZRpug8sL2OQNmhWAKF8Mmu4K0hZRbTEgL+hNXuDSIjITlG5ZRI9DNnkDFx1Azj0hTboCm2JAXqDICxoQgz5tWX1wvT+bvEHDAhCdtAGC+haRrhgUWkTHA5W8oEmLSEsMyAsKX2wTecHbm/uEvuCoA9Ac7FXEoHJXkBYD8gLygtodX3/yAhZ7QSsCYDLYV40F8oKmLKLM8kVeYN0iMhOUZvKCnfFMPdgPqV7gyAKKNItGiRiQF7gRA/ICN2LQRl4wns3Ve2zyBq1aQDpFLHJjEZW+JvICpxaRlhiQFxS+2Ly8YB7/9d72OAl/AVq1gHQGl628wEpXkBaDrlhEqnxvefICu2LQ57xgY3ecdAAAbi2gkt9aEzEYTF5gY8vq1Ch3aRFllq8aeUGQ07JUzguCirN5Ndy84MEoVI/Y5A1a6QCi6HA4B8szvwoDlbwgRwx6nBcUdwXLsl7XItJ6P06IQWcsoopiMJ4t4tk/d/aC1i2gEy1+yQgiLyAvKLKI6opB725xaSwoSi3i1/4OoS+0bQFFGYVjqSxbsoiKZn46YkBeUGc2T17QTMdXPS+QTd6mbPIGXegAigrH0qyOvKDm7DFnkZDBvWjt5QVBzQ4j43PkBUonL7j/OEz2+gHoiAWkZydkigF5QcXZoz2LqJoYlJga5AUVO75ii2h/Oo8FAN8fuiAAUf5IKg8Zyy");
+ WriteLiteral(@"0inYFKXmBXDMgL6ltE9jq+5bMplg+LvaAzArBU/yvZO+QFdmeP5AWlwt3jvOC9nRGLvaCbFlB5C68rBsPPC4osInuzR/KC0veqR3nBnd1JctknQCcFQH+g6tkJQ84LiiyiZmaPHbaIjDsM//ICuavX9pjFXtA5Cygy30HSsCtYavHJCyrNHm2KQaFFlPpgFy0iXTFwaRGVdXwy67+7R+gLPbGAtAoQeUG+GJAXtJ4XuNyyumjsyGKvO3vj5G+A7glAtFoBtYvvyYFqZO+QF+jMHskL+p0XiO8/wfeH7lpAJ1YC1xCDavYOeQF5wTDzgs3RVO2FMyoRdN8Cyh5Xyya5dvFV5AVe5AVcUprbFYxY7AX9EIBIwwpYnQqTF5AX2NiyukkxaOsWl+L3/3pnTAWC/nQA2jNa8gLrFtHx9yIvqGA3ZXyu5bzg/d0JoS/0RwB0PM9Ci6iCGNjIC6qtUWjWIqorBuQFdeym9vMC2eRN7B+AfghAKgXWbnOLxMBhXtBFiyjjVFQTXpsWkSMx8D0vkN09t1jsBX0SgChnoFYWA/KCRi2i2mLQ47zAhkW09DtmwyI6PM5sEbHYCwZgARWsCA1MjlPTIsoeqCZiQF4wtLyg0CIymLxEWhaRvhgs4uJ/Z4fFXtBTAdAdqC4tovKBWjbzIy8gL3CTFzzYD9WEO3tBPwUgqjRQm7CIjNr3AVhEGbqo311liQF5gfO8YHcySx4AvRSASHuY5Q9U8oL6YjDYvCAwfE9rWERpMXCRF4TzSN1jsRf0ugOIDovP8Xg1EAPygkwxIC9If7HLW1ympzTN5gXi92/sstgLBtEB5A32/KpKXkBe0KRFlD1JMJnNN5sX3N8Lkyt/APrdAagTm8FlDvbiqtpGXuCbRZShi/rdVZYYkBdUzgsejafqMYu9YCgCUPgLvzLYu5EXVLaIeiQGLi2i2mLgSV4gN3d5NGKxFwxMAIzEgLzASV7g0iJKiwF5QfYvp9zMncVeMCgBiDQ/H5QOdvIC8oLs349e5w");
+ WriteLiteral(@"Wp42ywyRsMrgNIXQVkXHwzBzt5gSuLSEcMyAuUlbzg0ShUIYu9YLAWkOZA1bKINMRguHnB8C8prS0GPcsL9sK52mGxFwxTAFZuCqk9ULXEwLu8oOAWlxXEoIu3uEyLgVuLKP3FbvICmfUT+sJgBSBSJ6OwwHigFg588gJv84IuWkTZk4TsFyqh78P9Kb4/eGABZc9drVtEy4JCXlDXIjp+DnmBsRiU2ZAy88f3B28EIEsMgpw+mbzAhhiQF3Q1L9gdz5IbvAAM2wKK9GZ9pWJAXqCVFwRlPVelNQo5FlHuccgLir5n4vtzZy/wpQMwH+zkBYU/U8WuIC0G1TKHAjFwmBcUWkTHvx/dtIjE77/3OKR6gJ8WkNlgJy9o1iIqF4Mu5gU6q4a7mhfIzV0IfcEfC0hDDPTCQQcWkYYYkBfoiQF5waoYyLX+kxmhL/hmAZUMsuoW0WrVJS9QFS8HJS9oMi8YzWSxF74/+G4BlRUx8oKMDsMsL7DRFaTFoIt5gZVLSo9/P5q1iOaLiMVegAAY2wnkBYUWkVHxVdzisvG8IKfzfDhisRcgAFbEgLyAvMCFRVRJDDK6xq24+E9Z7AUIgB0xaMIiqi0G5AXkBRlP2J/OkwcAAlBDDJq2iJbtnQqvibyAvODE108XETt8AgLg2iKqKwbkBTYsonIxGHJeIN9vc8T1/oAAtCoG5AXkBS4sopPfb2s8Ta78AUAAemMRpcWAvCD3GOQFhWKwH87VeIbvDwhAJ7uCuhbRsr1TQaAyj1GeF1i3iDJaJfKCenlBOFvg+wMC4IsYdGbL6rpiQF5QuysQx2eLHT4BAeinGJAXkBfUEQMp/tj+gAB0XAzICwzOEXmB1u/H7mTGnb0AAeiTENgTA/ICn/MC2d2TxV6AACAGqhd5gWWLyKj4qmHlBdN5pLbZ4RMQgP7jTV5QYBHVFYMmbnGZLwbt5gXyo8v2zqz1AgRgYJAXOM4LKtk77eYFu+FMzUh9AQEYLuQFFYVp4HnBeLZIHgAIgCc4t4");
+ WriteLiteral(@"g0xIC8IPsnajIvmMU/0y6LvQD8EgAjO8HGLS7Thdxzi2hJDFrMC8Tx2RlT/AG8FgAjO6EreUHhltXmYmDFIqogBjbygmprFOLiH87UnNQXAAEw7QrMxYC8oKm8oIpFNJrOubMXAAJgVwzIC7qfF8jNXVjsBYAAWBcD8oJu5wWLJPRlsRcAAmBJDMgLTF6LmUW0dJ4t5AVyvT+2PwAC4KwrMBcDD/KCAosot7vK6goynpz3qf0pi70AEICOiAF5gbu8QHb3ZLEXAALQGTEgL3CTF8wJfQEQgC6Igcu8wIZFtCwGgcZsfvkDbecF8tfjuPjj+wMgAJ3vCszFoNktq5cFpfjVt5EXlAmBFP85vj8AAtBnMSAvMN+yejJfsNgLAAHovxiQF5jlBXK9/wjfHwAB6KsYkBcYCFOqVZJ/7oVs8gaAAAy4KzAXAz/ygn1CXwAEwFcx8DkvEN+fxV4ACIC3YuBrXiCFf8JiLwAEwDcx8D0vkEk/oS8AAkBXYEUM+pMXyJw/8f35NQBAABADfTEYQl4gts+C1BcAAQAzMeh7XiC+/3SB7w+AAICWGAwlL5BZ/2SG7w+AAIC1rsBcDNznBfJf2d4Z4wcAAQAHYmAlL7BhEamD6/3x/QEQAHAkBlbyAgu3uJSburPYCwABgIbFoGt5gcz6Q3b4BEAAoP2uwFwMqucFyVYPrPQFQACg22LQRF4wIfQFQACg+2Jg2yKSa/0JfQEQAOioGDRlEc0jQl8ABAB63RVUEQN5Prd1BEAAYKBiUCQEYv0w9wdAAKDnYmDaFcj1/jg/AAgAeNYViO8/p/oDIADglxiICBD6AiAA4JkYCGzvDOCeU5wCaBuZ+TP3B0AAwDPE92exFwACAJ6xIPQFQADAP6TsE/oCIADgITNCXwAEAHws/pHC9gdAAMAzFoS+AAgA+AeLvQAQAPCUWYTvD4AAgH/FH98fAAEA/2CxFwACAB4idZ/FXgAIAHgIm7wBIADgIVzxA4AAgIfMF/j+AAgAeEeyyRvFHw");
+ WriteLiteral(@"ABAL9gkzcABAA8hU3eABAA8LL4s9gLAAEA72CTNwAEADyETd4AEADwFHx/AAQAvCz+kWLuD4AAgGewyRsAAgAewiZvAAgA+Fj8FZu8ASAA4CWEvgAIAHjInMVeAAgA+AebvAEgAOAhLPYCQADAU2YRvj8AAgD+FX98fwAEAPyDxV4ACAB4CIu9ABAA8BQWewEgAOAhXPEDgACAh+D7AyAA4CHJYi9m/wAIAPiFlH2sHwAEADyETd4AEADwsviz2AsAAQDvWBD6AiAA4B9s8gaAAICn4PsDIADgZfGPFHN/AAQAPIPFXgCAAHgIm7wBAALgKWzyBgAIAMUfABAA8IE5i70AAAHwj2STN6o/ACAAfsFiLwBAADxlFuH7AwAC4F/xx/cHAATAP9jkDQAQAA/B9wcABMBT2OQNABAAL4s/m7wBAALgHWzyBgAIgIewyRsAIAA+Fn/FPj8AgAB4CaEvACAAHsImbwCAAHgIm7wBAALgISz2AgAEwFPY5A0AEAAfiz++PwAgAP7BYi8AQAA8hMVeAIAAeAqLvQAAAfAQrvgBAATAQ8T2wfcHAATAM1jsBQAIgIdI2cf6AQAEwEPY5A0AEAAviz+LvQAAAfCOBYu9AKBh1nSe9Ol7b6jZqTOcLQCAHvBkuG1PAH5T82AAANAfsIAAADwWgGucBgAA77hJBwAA4CdbIgD/wnkAAPCL77726jURgJucCgAAr0jq/lEGsMX5AADwhr9PBCBuA6T4X+V8AAB4w9WjDkB4hS4AAMALXokn/reOBeCwC/ij+HGLcwMAMFj+Kq73f3n0P6eP/nHj+k831tdf+F78z0n8uBI/znGuAAAGwbX48c24+P9N+oP/L8AAx5G6SMzC+fMAAAAASUVORK5CYII=""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 16724, "\"", 16791, 1);
+#line 172 "WelcomePage.cshtml"
+WriteAttributeValue("", 16752, Resources.WelcomePageImageText_Browser, 16752, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 16792, "\"", 16839, 1);
+#line 172 "WelcomePage.cshtml"
+WriteAttributeValue("", 16800, Resources.WelcomePageImageText_Browser, 16800, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""384"" height=""305"" /><div>:-)</div>
+ </div>
+ <div class=""light second"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARIAAAESCAYAAAAxN1ojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAEf1JREFUeNrsne1LHFkWh2+3otGxscGQTHSCMkLCZtj9MB/2//+8HxZ2QjJEcDE4ZlaJ0KFNxzQ22TrmVKY1/VbVt6ruPfd5oOjJQKL10k/9zrm3brUcmOHLly9r2cd6tnWybSXbNvRzM5BfcZhtn7PtU7aNsq0vn61Wa8DZi5sWhyBaaWyqKPLPTuS71FfRiFQ+ZXLpc5YRCfiVxoqKomNEGosiUrnSz34mlyFXAyKBcuLYCqg0CaE06ucbYkEkMLlUEWl0E0ocvhLLJX0WRJK6PHZUHmsckaWQ5m1PtkwqPQ4HIkEegFQQCUyQhwjjoQoEedQvlUvKH0QSs0B2VB70PMJARHKhSWXE4UAkoaePxyqQFY5I0KXPBSkFkYQmEEkdj9zX3gfEQ1/LnksOBSJpunwRgTDXI25kTso7yh5E0oRAdh3NU4tlj/RRzhEKIkEggFAQCQIBhIJIbAlEmqdPEQhCybZTmrKIpKhAZBTmiWMOCNxFmrInLHOASOYJZEUTyA5HA2bQV6HwBDIi+U4i0gORoVwmksGiSP/kXer9E0TyVxnzk2MuCJQvd05TfkAwaZFoGZOnEIBl6alQkit3khUJozFQESMtdS4Qif0UQjMVqiapZmxSItFeyAEpBGpMJ0nMPUlGJJlEJIXQC4Em6Gk6MTuyY14kuj7IoWNEBppFSpxjq+ufmBaJNlSllGFeCITCqcVGrFmRUMoApQ4iWUYgkj6eUcoApQ4iKSuRTZUIpQzEgJlRHTMi0fVCDrg2IUL+");
+ WriteLiteral(@"zGTyDpE0LxH6IRA7l5pOouybRC0SZqmCMaRfchSjTKIVCU1VMEqUTdgoRcIkMzDOSJNJNDKJTiSMzEBCMjmOZUnHqESCRCBBTmIYHo5GJEgEkAkiQSIAhmUSvEiQCED4MglaJEgEIA6ZBCsSJAIQj0zaSAQgOg502VBEMkMiK0gEYC6HesNFJEgEoDS335VQZBKMSHh2BqCUTA71u9MowTRbs4Nx4HiKt2m2sm214N+5ybYrDl2jNP7UcBAiYT2R2pDFsNez7cHYlv/ZByKVj2Nyuc62z/rfNxz+SullIjlOViSsbFapNLY1ZazrZ5Ncq2REKh+QSyVcZDI5TU4k2ij6G+ffqzjyzxjIpdLTT8SyPI3MMWlMJNog+rtjhKYs0suQNPdQxbFqYJ8uVSqXmmCgHL/XvZZJkyKRJMIITXl5WG9MS1o5RyqlkFXWXtfZfG1EJDRXC5PL43Gi+y8yea9igcXoZyI5MisSfY3mIed5ofSxp/J4wOG45UZlckZKWYjaXnNRq0h0rdUXjr7ILEQa+wmnjyIpRYTS41DM5KiO5RrrFgl9kel0VR4IpBgy2vMWoUxF+iQvq+6X1CYS+iJT2dJSb5tDgVAqovJ+SS0i0Ueen3E+KWFqEorM8GTa/l3kLX4X0YpE54tIX2SNc3lL3kTd51BUyrkKhUluf5U4MiQ8jFUkB46H8XJ2tIxhFKYebrTcOeNQ3DLIRPJ7dCJhqPdOGfPc0QdpCilzjih3bqlkSLgykVDSfCMvY1a5hhvnTBNK6uXOS98lTpUiSX2UhhRCOgkV76M4lYiEUZrbXshzUkjQvNUtVbyO4lQlklQnnok4pCfEkG4cyFDxq0RLHa8T1byv2ZpJ5FGiEpFS5h9IJCqk7Pyna37RpyaQHuZTX/+YV5Fog3U3wZMio1O/JnpBWkiRvyZ6A9jx9X4c34nkqUvvgbzHmkToh8TNc91S40lQIlGz7XDxQeQ3hV8Suyl0dN3kYBLJk4QO/qoKhH6IwbifYMLcXfbdOF5E");
+ WriteLiteral(@"ojNYO4kc9FVHU9U6W3qOU3mUYW3Z69lXInmayAHPJUJTNQ2ZpNRAf7RMKllaJFpfpTANHomkR0rnfGWZVOIjkexyQQHn3gRPdDnUekWSSBpBIpDSNVAqGLSb+KFcQBDptfDM2R/N2SmTSkqLJJE0cohEYIx8NMe6TAoHhHadPywymCcC02Tyi/F97BYdwSklkgTSCK+FgFlsO9szmguP4JRNJJYXLOo6pr0DN5tC80oKi0SfqbG6TIDMZHzBdwQKlL9dw6lk4X0rk0isppFVlQhP8UIRLF8zC/dBC4lEh4WsGpgRGih7A7LafF3T5+i8JxKrNeGOo7kK5ZHm677h74Z3kVhcbyRf7R1gGfaNpvXuIhPUFhaJDvlaXP2M1d7BF1Znvj70mUgsppE9x3tnwG+6tVjizP3uLyQSjTYdTjrAQjcnayXO3KbroonkocETTkkDlDieUknbxz8S6UGhpIEq0+6esX2a+fzNXJFkf1lmsVp6riZ/Gx5Alew7e2u+dpdJJNbSyJ5LZ1FfaL58RiTz/nKkkZMGK9TFtrHvz9Typp1YWYNEoG5+TiGVzEsklsoaeY6GafDAddeASCzFMhqsQBKuqLxpJ1LWdB3DvdAcD6ynkvacSGYFShoglfijU0QkVsoaa3cD4DqMI5FoDWTl2RpGaiAUfjSyHyva+pibSKxIZJU0AgFhaV5JNyWR7HHtQmBYubFtLSKSLU4aQGXXpIUngzszRaL9EQuvm5DJdDxTA9zgKkJfTTM1kVgpax5yvQIldz2pxKpIaLJCyDww0j7YmiWSDSNlDQDlTbVsWk8klDXAza56vs0nuSOS+5NMIi5rSCRAeVMPG5MSCWUNAOVN4fKmPa3moawB4KaXciJhuQCIqbyJfa5TZ5JIYm+0yvx/3lUDpJIakRfotcf/QBoBaOTmFzvr44lknZMCwM2vTHnTNlTWkEggRqQUj30YeGVcJCukEQBugCXYGBfJBicDoBFiTyTrlhLJFtcjRMoPkf/+a+Mi2eRkAHAT");
+ WriteLiteral(@"LMOtSKa9zzMyWMQIYibqHl/bSBqh0QqxE/X0izYnAYBE7UskG5wEgEaJuk+SiyT251MQCcRO1N9BK6UNIoHYiXoelJXShh4JQAAiiX34l0QCFoi2T9Lm3AEEQ7R9EgsiYWo8ACJJ1+IA94i212dlZiuABaLt9VlptgIApQ0AIBIAQCQAAIgEABAJACASAEAkAACIBAACEUmfQwHQODckEg4+wLJ8RCTNccX1B4BIAMBIaTPiBACQrpcVySdqSwBIvbQhkQBpBJGkfRIAXOTtBSvzSK65DiFyeiSS5vnMdQiU582LZEBpA9AoUQ8Y3Iqk1WpZGP6lvAFuhgGUNkOMDtAI11ZKGyH2PgPlDXATbIbRuEhin5T2gesRIqUX+e8/GBdJ7H0SEgmQSJphOC6S2OeS3CATIJE0L5KRgRNCeQNcs02WNq1Wa2Bgh3pclxAZ7w3sw/D+zNbYyxsSCXDN1oyEkPsiiX0uyQ0ygYiQ+SOx9/Vuw8d9kVgob95zfQJppL6yZpJIPhnYsUuuT+CmVxuD70SS1ToWXkthIS6CfW6M3PSuJiUSK+XNOdcpkJyrJx/tbU8zDCcJgLJmDt8qGKuJRMobRm8g5OvTTFkzTSRWXt/5P65XIDFXymCqSLKaR4ZzhgZ2UvokrC4PIXJmZD9mljaWUglNVwiND87Gan6D8ZUVrYvkjOsWAuMPI/txZ1DGukisNLWA6zE0enNFon2SgZEdJpVAKLy1siP3J6+2F40ukZuToWBoGmn8nxv6TrlFRWKpJHjLdQwk4wZEolNfR4Z2nFQCTaYRSyLpF0kkE81DKgEolUaszGkaaA81WZGQSqAJro3dxCa2PGaKJDNPz1B5QyoBrjk/N+RiIjGaSpjtCnVxZex6m1jWpCiS/A7BMzhQB/81tj9TR3LnisRgeSM1K5PUoGrODd6Ee6VFMs9EEacSlmOEqpDEe2xsn/rTypqURWIxdkI4HBssn2c6YCGR6OS0gbED06PEgQr44Ow19EfzyrR2gX/swuBJ");
+ WriteLiteral(@"lxLnmmsfPJY0bwzuV2987ZFlRWKt6Zqf+Ndc/+CJN0ZvTHNDxMIiUSNZfEn3lWOiGvjpIVjsJQ7yV074SiRWy5u8xGH6PJTl2mhJs/B3vpBI1Ex9owfslWOiGpQvjy1eO6PsO79QymqX+McvDV8Qv/G9gIIcO7tzkhauQAqLRA01NHrgrgxHVPDPubP97NbC+9Yu+QPecXFA4nwwftO5nDfk60MkFoeCx3njaL7C7OT6yvg+FgoLpUSiprowfiBfOZ7Hge+x3FwdTyOF2hftJX7YufFUkjdfkQncvyasz4Yu3LooLZJEUolcOEeOYWFI58ZSOI0sm0hSSCV5PfwbMkmeVNJpqYGUpUSSSCpBJvAmEYmUSiM+EkkqqQSZpF3OpDIdoPS0jqVFoqnkNJEDncuEpQfSkUgvkf0tnUZ8JRLrs10nyeTfjtGcFCSSyjleOgy0Pf4ypwleaExa40ZhgYsis1grFYmuNt9P6OCLTP7jmE5viQ8Jlq7D7Lu79CMvbc+/1EmCF5909I/5DkbPud4YUmume6kkvIpEmzUXCV6EZ44RnZiT5RuX5lPffa0kwhKJIjFplOBJkRPyL0cTNiauXVrDu5VVEK0qfrsvX750s4/DhC/Qfd0gXC41haSaIv/00RupVCQqk2fZRyfhC1VkKsfgAd/Z4EqZY5d2k1warC99/oNVimQt+3iRbSsJn7BVTSZ7fH+DIF+MKPUJhUeZSLyOsLaq/G0zmTzKPp5y/ZJOSCHBIHNGvM/5alX9W1Pi3Ekne47eSd2cO5vv4i1V0mTb62UnnzUlEkqcu0gqeZ5t2xyKSpHRM3lRfI9DUV1JU5tIVCY72ccB5/G7cufnbNviUHhF+h9vKWPqKWlqFYnK5FC/PHCXx1ru0D9ZDildzhyvX52EvHbz9yp/QJ0iWdESZ43zilAqEMiZow8yiZGWNIMqf0irzj3KZCJN12ec27lC+dHRQ1m0hLlEIDM5WfS1m9GIRGWym3084fzOpatSecyhuIPMBfnD2X11");
+ WriteLiteral(@"rE9ksaKTOn5Qq4m9o19SiFWVyV7CZc+1iuPMMZlsUQZa0tTy3FtTIqFfUo4tlcpOAlK5UXm8J30Uppa+SOMiUZlsuq/9EuaXIBXk4ZdjX8sDBC8SlQnzS/zwQIUi5eK2lkOxID2PnoqDJRiWx+tTvVGIRGUiz+I84vx7Tyvb+vmDC2fSm/Q3Pqo48k/wRy+TSCOr9bVC2PtMJgd6R4XqkLSyruklb3RXNcR8pXV6T8uVj/r/GKatjlqbq6GKRPok0i/Z5HpoLMHk5dAPBUqjXBLf7ogcykYQebxsSiLBiGRMJozkABSXSK0jNJNoh3I01KbHLs31XgHKcty0RIISicrkts5DJgALcVLVsgBRi2RMJrwnBmC+RIKZa9MO8QipZU+4VgAmchqSRIIVicrkEpkAfIc8iBfcS+haoR81Zr8C3JFIkDfXVgxHD5kAhCuRaESCTACJhCuRqESCTCBRTkPsiUQtEpVJV2XC8gNgnZPQRmfMiERlwlomgEQQCTIBmMLtoyKhzFg1LxKVCU8NgzWJHIXw7ExSIhmTyYFjMWmIm0bXE0leJGNCYaU1iBVZx+UkVomYEonKhOFhiI1G1lhFJPNlQhMWYmCkKcTEqnIti2dI+ybyEq4O1ysEiPRD3sbYVE1KJGNC4fWgEBoyN+Q05n5IciJRmXQ0nVDqQNOlzGlMk8wQCaUOUMogkgqFIsPDu6QTqJGLTCCn1neyldpZ1VGdfcdsWKiWoQtocWZEUp1QaMRCZSkk295Za6gikukykZdxHTh6J0AKQSQehELvBJbFxAxVRLK8TEQi8rwOLzOHIvQ1hQxTPgiI5HuhSJnzk6MZC/PLmFMrU9wRSXVC2dFyh5eawzjSQL1IuYxBJOXKncfu6/IE9E/A5PR2RFKvUHYd652kLJB3qfdBEIk/oaypUGjIIhBAJN6E0qXkQSCASHyUPPRQEAgi4RB4E0rXMcoTI7ejMNl2ThMVkYQkFRHKjmNV+9CRR/ovrK4PgkjsCGVNy54uKSWo9NFT");
+ WriteLiteral(@"gQw4HIiElAJFkGnskjx6lC+IxIJQ8l5KF6nUUrrk8qB5ikiQCiAPRAJFpNJxzE0pSk+3PvJAJHBXLJsqlC3HokuTUseVpo4+hwORwOJi6ahQRCybiSWWvopjoKmDZikiAY+JZUOlsmEotYg0hnnqYIgWkUD9cpG5KusqlTXdQkwvIxXFcEwaQ6SBSCCOBLOiclnX/70xJpl1t/ykOZHC57E/X+nnTbZ9EoEgC4DE044mHoCJ/F+AAQAgl3zNeDGxuQAAAABJRU5ErkJggg==""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 23261, "\"", 23329, 1);
+#line 176 "WelcomePage.cshtml"
+WriteAttributeValue("", 23288, Resources.WelcomePageImageText_LightBulb, 23288, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 23330, "\"", 23379, 1);
+#line 176 "WelcomePage.cshtml"
+WriteAttributeValue("", 23338, Resources.WelcomePageImageText_LightBulb, 23338, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""274"" height=""274"" /></div>
+ <div class=""bulb"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVoAAAKSCAYAAACTAhMyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2OEMyQkI4M0Q4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2OEMyQkI4NEQ4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiI+IDx4");
+ WriteLiteral(@"bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY4QzJCQjgxRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY4QzJCQjgyRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+zKbSDwAAExlJREFUeNrs3b1uXFkBwPF7UbaABiNBB9qJQEABWruEJuMSsSxOgaBix0BBl+QJ4jyBsx0FaLxPYKdAovPkCexIFIhF8tADMd0WKJdz7Du7E68/Z+bcz99PupnsJvHHmev/nDlz5968KIoMgHS+ZAgAhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWoCvuxV/yPDcSQOsURTG8+P9CzyYN+xrLXwDaEdZB2B6H7ai4Xvzznfj3hRbgdrFaC9u4WMy4zuAKLdCGyG6F7XWxnPjvR0IL8MVI7RarNRZagHSRrSW2Qgs0NbKjIq1Rhd+L0AKNi+xgBWuyt1mzHVQV2nvuVqBhnoZtLfHniB8/LiFsXhHH4cX/t9TxuWa0QMNms1Van49r2PZvmAWPL4uwpQOgTaHdrTi04zLuh3f8dzHIa7f8nrL87BdvwQWaEdqTcDOo8FOezi0lLPJvN0M/j4UWaM2yQbg5admXfWNsY2OdvQtoikELv+azF9VuWkYQWqDqmetaeZzs/vxhXOGPDlv6LcUX1B5f9xcsHQBVRjYGqYrDt+pYQrgfWnpq6QCoM7LxuNXdDkZ2toSwZekAqDuyo45/mw+EFqhz");
+ WriteLiteral(@"uWDUg291cNUfWKMFUkY2PqU+6ehywUWnoaVfu2QMzGiBpLZ6EtlrCS2Q0oc9+l6PhRaow3qPvtep0AJ16NOywWjuhDPD+T/wYhiQTNHv0wNuh7buOakMILRpxROLTywdAClNev79x7cbW6MFknrR8+9/GH+xdAAk07M3LFwlN6MF0hXm/GxWH/V+HMxogQpmtoezp9E9E9/EsGFGC1ThYXbNO6c67Fn8RWiB9E+d8zyecGVjFp6e2Avf88HZ92/pAKhSeRHGR9n5UkIX36IbZ+7PZpE9O5TY8cQASR9YLB0ApCa0AEILILQACC1Afe4ZAtqiKIp4KFB8z/w0z/OpEUFo4W4RjQGNIR2G7d3s/NLNs+2yvz//n/G4xdPy9p/xNoR4YlRpCm9YoM64xiukPsjSHbgeYxtP0zcJ+/ixEaem/dwbFqh8pxuGbRy210W1TsK2U74rCYSW7i0LhG1Uxq4JDi9ePA+EljYHdqeG2etdZrkj9xRCS1t3rlGDA2uGi9DS6p1qPWxHRTuNy6MfQGhp7A61U7Tf6/JoCBBazGLNbmlLaB1Hy7I7UZz9jbNuXuU0Hnv70LvQWDa0znXAMjvQ43Czn3X3UtLxTRRH5Vt/YWFCy6KRjbPY3R58q/FB5NBhYCzD0gGLRraP4dkOPyt79gAsHSCy6YzNbDGjRWSrseEkNZjRkmJnGYnsZw69QIYZLauO7DDGxUi8Jc5oN8PPzqmh4KYZrdBy004SX3U/ybp7CNcyDsLPzkPDgKUDltXl42SXtVUeSwyWDlj4kThGZNdIXCsuHWx49xiWDlhk5xiEm6PmzmbjOToas9/GS+Vs2muwdMBd7TZ7ySCfC27tho6vxdIBd30EHmaOMrirabmE4CgEzGi5laeG4M4GYfPCGGa0mM0mFmez981qMaPFbDadNbNazGi56ZH37PyrRmIp0/DzdN8wYEbLVR4ZgqUNXHOMi4SW2aNufNorEKvxoSFAaLlMjKy32q5oLF3YEaHlMj83BCt/4AKhRRg8");
+ WriteLiteral(@"cCG0VMKLNx64EFrSe2AIkjyADY0CQsuMy7KkIbQILYKQ2HuGAKElc5HBpAaGAKElcrxnOh7EEFrODA2BZwwILXjGgNDSal6wAaHFjKuCp/jprj02NLoILcTIFoVhQGghHSe+R2gh8Xz2TcqlAxBayMOPQW5Wi9BC6toKLUJLOi6NnRUpZ7QT44vQ8soQgNBCYkmXDabGF6HFU9uUCc9zoUVosUab0LEhQGiJMy4xSMdsFqHlMxNDkIQXGhFaPMX1AIbQUpWXhmDlTvM8F1qElnMhCAdGwWwWoSU9sV2tF4YAoUUYPHAhtNQQBsfUrmgs8zw3lggtbyvDYBa2Gh8bAt76+SqKIv6QGQlml8Y+MhJLmYafp/uGgbmfKzNa3prVxuNpJzXukg35GEv5yJ6EGS03PfoOw82hkVhIXH65b30WM1pumtVOMseALjybFVnMaDGrTWcatg2hxYyW24oz2j3DcCdPRBYzWu70CByshe2kvOWGB6bwM7RpGDCj5a7i7OyZYbjVOG0bBq4jtFzneeZNDDd55nI1WDpg0aWDGUsIV4tvtX1oGLB0wCqeGlt//KJjSwZYOkBU0j74bDvKAKFl1fYyL459NsN3UUuEllR2MsfXPhFZ7uqeIeCiG14c3S5fLBv1cGjicsGePQShpYoQ9y22cbngoYstsvDPjMO7WFTYd2Joxz2IrDVZlvk5EVqW3omG4WY/6+ZxtsdlZB1dwFKh9WIYyy4jxKfTG2WUuuR5+N6cjYuVEFpWEdt4+ZYY2y4c/jVbKnjinmVlPyOWDljx06RR1t51W+uxWDqgFbPbvez8ZDRtJLJYOqA1sY1Puyct+7K9EQFLB7Tu6dIgO790eRuORnDibiwd0MpZ7TRrz6W3nTAHM1pa/Wgez2U7aPCX+NwRBpjR0nZNPuTL5XqohNCS9inT+VEI04Z+eQfekIDQ0hUfm23T6wmHNVpSC/tYPPLgdcO+LEcaUNX+b0ZLBY/m50/Pm3Y13Y/dM1RFaKnKi4Z9PS6jjtDS");
+ WriteLiteral(@"OU0KmxfBEFq6pwzbpCFfzkv3CEJLVzUlcBN3BUJLVzUhcKdOHoPQ0lkNubihyCK0dF7dobM+i9DSeVMzWoQW0nrV89AjtNDt0HkhDKFFaNPyJgWEFqFNzGwWoaX7ykvcgNBCR1k6QGghsVeGAKGlL6yVIrTgKTwILUBr3DMEVO0n739gEDCjBUBoAYQWAKEFEFoAoQXgRg7vonI/+P7/avvcf/mz8Udo6YG//s1uR79YOgAQWgChBUBoAerjVQkq56gDhBYSc9QBfWPpAEBoAYQWAKEFEFqAzvLyL3X4OGwvL/uDf3zy9wf/+fe/hot+4B++t/7sy1/+ylV/PDH01KIoCoNAkwzibrngdmj4aGRjhZYGGi8Y2qGhQ2jhdoYLRPbEsNHU0N6zfHC5PM8NQn0m5XaXGeozw0ZTeTGMpvpo9puvf/0b6++8887a/B++fv2f408//fR07n/tGTIaO3EzmzWjbbrf/u73h5fMbjf/9Mc/TIwObVg6cBwtQGJCCyC0AEILgNACCC2A0AIgtABCCyC0AAgtgNACILQAQgsgtAAILYDQAggtAEILILQAQmsIAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWQGgBEFoAoQUQWgCaEto8z5fagkFRFI/Ddhh+X1zcgv1wOwrb2h0+JoAZbQjoWth2wm9PQhx3wza8IuRb4WYc/u7JmzdvHrurAKG9XWQH5Qz26R1mznFGuxtiux8j7S4D2uZexZE9iuFccKliK36M8NsNdxtgRnt5aPcXjexcbNfDzHbX3QYI7RcjuxMjuYqPFT5OfAFt6K4DhPbzyMYXvx6t+GM+ddcBbbGyNdoyqKPw2wdhW5v7/6fLLhlcMqsdho+7Hm6P3YVAL0IbAxu23VUH9YbPGZcPhBbo/tJBeYzruMrIlh64+4DOz2jDrDK+qaCuowAcUwt0f0YblwsMIUCiGW05mx0YQoB0M9r1mr92");
+ WriteLiteral(@"L4QB3Q5tmNG+V+cXHt+48ObNm3jCmbFzIABdndHWLsR2ELZ4aNlJeR4EgO6ENgTuVYOCu+aFOaCLM9pGrZGW568F6NSM9iDMIqeGEOB6S71hIcR2O9wcGkb47G3hcXs3u/rQx5dhixOUSfj5MVER2luFdhJ2rhjbcQN28j3XDaOG/W4Ubn4ettsuXQ3n/m0M7UHYPhJdSwc3xXYv3DyscxkhfO7j8HU8cXdS4T63E492KScZi74+EGe98Vwh8aiZeKmmdSNrRntdbOOj8kF5Fq8HWbXvGHsRYx+20/C53aOkDuzs/B6r3sfjx42Xa4oTlydxfzbaHQvtqp5yl7Pbvbq+GUsHJAzsWhnYUeJPNSqD+zAuzRl5SwfQl8jG2ethBZGdiVE/DJ/3sdEXWuhDZOO66VFWz7k9dsPnH7sXhBa6HtnDrN5zH4/EVmihq5GNcR1nzTjB/Kg8jAyhhU4ZZ/WfCvStr8fhX0ILXZrNxtljE8+dse/eEVroypJBU88EN4hvlHAvtc89QwBviYdUNflE8o9CbJ9f94aG8sEiLjMMw/bV7PMlkPhvXpW38VwLrlIitFDLbPZRw7/MtfLBYOeSr3+U3Xzeha25vz/NnGvB0gFUbJS14zL2jy4EdmvB8y4Mss/PtTB2lRKhhSp82JKvc62Ma7yNx/nuZ8ufeyE+yBx5N5qlA7js6X4MzDBss5MZDeaiMy23uBYZ1yYPrlrbLD9Omw6fepSt/jjfsxcC44VXy3NNY0ZLn33wwfvDcjY3e8o8KoM7uPDUeFg+PY5/53V5OsLhJR9y2LIhGGbpljm8G82Mlj771re+mf3ql7/Ivve97z5d8EPMTkc4Cbfbcy8CPTC6X4htZmYrtPRvFpt98LOfrnJGGF8Eiud+fZ5Vew7lNsX2VTk+CC1d95vtX2c//vGPUnzoszXJrF3rs1V6GsbnwOFfy8nLpwdGgsb65JN/HH3nO98WwvrsWUJY3FljhZaG76SzF7qo132z2sVD66gDmryDbolsYzwy");
+ WriteLiteral(@"BJYO6F5k46FL8dCtNaPRCNPQifuGwYyWbmn6yV36ZuB8uIsTWpo6m/VUtXmGhmAxDu+ijpAOsvM3DsTDqgYX/vhldn5qP7PZ5nGfCC0tCWx8R9fIrKmV3jMEQkuzIxvjumtWZEYrtJAmso6Fpde8GEbqyO6IbGe49I3Q0sDIDrPzNVm64b+GQGhpHpHtlokhWIx3hpFyNntoJDoZ2ngI3p5zH9z6Z0FoSbZzeQGs++IVdLevu/Q5QkvanSuep2BgJDovRnYzNMQLZUJLDTtXYRR6Iy4hbJjZXh1aL4YBy4rPXHYNw9XMaDGjZVWcHNyMFkhsyxBcTmhJxcymf941BEJLtSaGoHecGFxoqdgLQ9A7jjoQWqqU53k8mH1qJHrllSEQWqq3bQh65cAQCC3Vz2on4ea5keiFiXeHXfOz4DhaUnPeg17YENor938zWiqZ2W6b2XbatshaOqAZsX0SbjYzh311STzK4GG4b/cMhaUDmvdUKh5v+WF2ftxl3Fz0r13i7DUevvfciWRut3QgtDTeb3/3+3gC8eGF/735pz/+YbLkD8AgcyrHOwVWWBcLravg0t+nc+cnQJkaCVKzRgsgtABCC4DQAggtgNACILQAQgsgtAAILYDQAiC0AEILILQACC2A0AIILQBCCyC0AEJrCACEFkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWQGgBEFoAoQUQWgCE");
+ WriteLiteral(@"FkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAEQWgChBRBaAIQWQGgBhBYAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFkBoARBaAKEFQGgBhBZAaAEQWgChBRBaAIQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAoQVAaAGEFgChBRBaAKEFQGgBhBZAaAEQWgChBUBoAYQWQGgBEFoAoQUQWgCEFkBoAYQWAKEFEFoAhBZAaAGEFgChBRBaAKEFQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQUQWgCEFkBoARBaAKEFEFoAhBZAaAGEFgChBRBaAIQWQGgBhBYAoQUQWgChBUBoAYQWQGgBEFoAoQVAaAGEFkBoARBaAKEFEFoAhBZAaAGE1hAACC2A0AIgtABCCyC0AAgtgNACCC0AQgsgtAAILYDQAggtAEILILQAQguA0AIILYDQAiC0AEILgNACCC2A0AIgtABCCyC0AAgtgNACMCcvisIoACT0fwEGAL+BBlr+j4JHAAAAAElFTkSuQmCC""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 31229, "\"", 31298, 1);
+#line 179 "WelcomePage.cshtml"
+WriteAttributeValue("", 31257, Resources.WelcomePageImageText_LightBulb, 31257, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 31299, "\"", 31348, 1);
+#line 179 "WelcomePage.cshtml"
+WriteAttributeValue("", 31307, Resources.WelcomePageImageText_LightBulb, 31307, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(@" width=""346"" height=""658"" /></div>
+ <div class=""bottom"">
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABLwAAADKCAYAAACv61n1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAKsZJREFUeNrs3VtsXPed2PEzc87ceBlxJFKKR6Iskk5sL1U1m6hoyVYdF4VhAoMt7D64LwEmfUqQPGwKC32owTgbgy/BGNuXBA7ysNA2C7RddJ3dxQBFXTQQ7I4LrNEkgp2E3shkZVqOJVO8Dzn3nj95KFPk3M7M/9zmfD/A0VDkmXPO/M9l5vzm9//9g4qPZfOpaQUAAAAAAAB9Jejz1/+vOAQAAAAAAAD6i28DXkZ2V0J/nOIwAAAAAAAA6B9+zvA6DHTRrREAAAAAAKCP+DngNX3sEQAAAAAAAH3AlwGvbD6V1B8Sxn9Ft8YEhwIAAAAAAEB/8GuG13Sb/wMAAAAAAMCjCHgduMyhAAAAAAAA0B98F/Ayui8mj/16Uv99jMMBAAAAAADA+/yY4TXV5Pd0awQAAAAAAOgDfgx4NQtsTXI4AAAAAAAAeB8Br/a/BwAAAAAAgIf4KuCVzadaBbVi+t+nOCQAAAAAAAC8zW8ZXtM9/h0AAAAAAAAu57eAV7sMLgJeAAAAAAAAHuebgFc2n0rqD4k2syX0+RIcFgAAAAAAAN6l+ei1Xu1wPpHl9TaHBgDguGuZXN3M/G/dSAdoNQB+vQZyPQQAOMlPXRonO5zvMocFAAAAAACAd/ki4GV0U0x2OPukPn+MQwMAAAAAAMCb/JLhNW3x/AAAAAAAAHAJvwS8pkzOP8mhAQAAAAAA4E19H/AyuieS4QUAAAAAAOATfsjw6iZbK5bNp6Y4PAAAAAAAALzHDwGvaZufBwAAAAAAAAcR8JL/PAAAAAAAADiorwNeRrfEWJdPT+");
+ WriteLiteral(@"jPT3CIAAAAAAAAeEu/Z3hNO/x8AAAAAAAA2IyAV2uXOUQAAAAAAAC8pW8DXkZ3xF67JE7qy4lxmAAAAAAAAHhHP2d4TbtsOQAAAAAAALBBPwe8ZHVHJOAFAAAAAADgIX0Z8DK6IU5KWtwkhwkAAAAAAIB39GuGl8ysrFg2nyLLCwAAAAAAwCP6NeAlOytrikMFAAAAAADAG8jwcmZ5AAAAAAAAsEjfBbyM7ocxyYtN6MtNcrgAAAAAAAC4Xz9meFnV/ZDi9QAAAAAAAB7QjwEvq7ofXuVwAQAAAAAAcL++CngZ3Q4TFi0+qS8/xiEDAAAAAADgbv2W4WV1t0OK1wMAAAAAALhcvwW8rO52SMALAAAAAADA5bR+eSFGd0OrR1KkcD0AwPOuZXJ1L2//WzfSAfYiAL9eB7kGAkBn+inDy47sq1g2nyLLCwAAAAAAwMUIeJk3xWEDAAAAAADgXgS83LseAAAAAAAAdKEvAl42dzNM6OtLcugAAAAAAAC4U79keNmddUXxegAAAAAAAJfql4CX3XW1rnLoAAAAAAAAuJPnA15G98KEzatN6uuNcfgAAAAAAAC4Tz9keE37bL0AAAAAAABogYCX99YLAAAAAACAFjwd8MrmU6Iro1MjJlK4HgAAAAAAwIW8nuHlZJZVLJtPkeUFAAAAAADgMl4PeE35fP0AAAAAAAA4xrMBL2OURKczrMjwAgAAAAAAcBkvZ3i5oYZWIptPJTmMAAAAAAAA3MPLAS+3ZFdRvB4AAAAAAMBFCHj17iqHEQAAAAAAgHt4MuBldCOMuWRzkkY9MQAAAAAAALiAVzO83JZVRfF6AAAAAAAAl/BqwMttdbMIeAEAAAAAALiE5wJe2XwqoT+4bWRECtcDAAAAAAC4hObBbXZjNlUsm09NX5+9+T6HVG/SuUw8l76xSUtAlmuZXP347966kQ7QMgCsvM6YZeV16fj2cQ0E4LfrIAB/8mKXxssu3a4pDicp/k06l5mjGQAAAAAAQLc8FfAyRkN0a/");
+ WriteLiteral(@"dB6njJ8aI+zdAMAAAAAACgW17L8HJzUCmRzaeSHFLdE90ZjX1MhhcAAAAAAOia12p4TXpg++5yWHXtMNA1ns5lpnPpG9REA/qcjJofVqCOCACug1wHAQDeRoaXXFc5pHoy2+RnAAAAAACAjnkm4JXNp0RR+JjLNzNp1BlDd+aa/AwAAAAAANAxL2V4TbOd/Ut0YdQf4kd+NWPU9AIAAAAAADCFgJd/t9NtXmzwO7o1AgAAAAAA0zwR8DJGP0x4pE0nOay60ii4RbdGAAAAAABgmlcyvLwURIpl8ymyvExI5zLjSuPMOAJeAAAAAADANK8EvLw2+uEUh5YpM01+HzdqewEAAAAAAHTM9QEvY9TDpMfalSCNOXNd/g0AAAAAAOAEL2R4eTF4lDDqjqEzrYrTE/ACAAAAAACmEPCyDsXrO5DOZURAK95q/xs1vgAAAAAAADrihYCXVwNHVzm8OjIjaR4AAAAAAIB9mps3zhjtMObRtk2K+mPXZ2/ucpi1NNfhPP+VpgIAuMm1TO4Z/SFDSwDw8XXwkv7wCi0BwI00l2+f10c7FAG7dznMGjO6KnbSXXGW1kKbD1t1WfO/dSMdoEUBtLmGPGPc4D3jxetgu3m5DgLo4DpyybgOfp3WAOBWbg94eX20QwJerXVakD6ezmVmc+kbeZoMAODgDd4zissCXQBg83VwRH/4jj79sT6N0CIA3My1AS9jlMOEx9uXwvWtmRmB8Tl9IuAFAHDiBu8ZhUAXAK6F31MIdAHwEDdneE33QfvGRB2y67M33+dQe1Q6lxEjM5opRj+nUB8AAGDvzd0zCoEuAFwLv25cCy/RGgC8hICX9UQdMgJeJ5mtyzUuan7l0jc+oukAeOgmoe70NlCPqav99oxCoAvoi+sh18Cer4V/phDoAuBRQTduVDafEl0Zk33SxtMcZg3N2fQcAADM3OD9XH8Q0zO0BgCuhQS7AHhX0KXbNdVHbZww6pHhUTM2PQcAADOeoQkAgGshAO9za8Cr37KiKF5/RD");
+ WriteLiteral(@"qXEft3vIunzhm1vwAAAAAAAJoi4GWPqxxqj+ila+IszQcAAAAAAFpxXcBLjGrYh+2c1F9XjMPtoTmHngsAAAAAAHzAjRle/VrkneL1yn53xniPbUEdLwAAAAAA0BIBL16X3XrN0Bo3aoABAAAAAAA05KqAlzGaYb92/aNw/QEZXRKp4wUAAAAAAJrSXLY9/VzcPSbqk12fvfm+z485GcGqF/XpJ5y+/eVaJlfvh+1460Y6wN4E4OVrYS/bwDUQAAC4hdu6NPZ7FtSUnw+2dC4jgl1xCYuaNmqBAQAAAAAAnOCagFc2n0roD8k+b2+/1556TuKyGK0RAAAAAAA05KYMLz8EgxJGnTK/mnXpsgAAAAAAQB9xU8DLL939fFm8Pp3LjCtyg5pkeAEAAAAAgIZcUbQ+m0+JkRn90t1PFOZ/24fHmuwAVTydy0zn0jfe5zSG38gsak2BaQBcB7kOAgDQj9yS4eWn2lZJI8DnNzMWLPNFTmEAAAAAAHCcWwJefuvm58fi9VZ0QaSOFwAAAAAAOEFzyXb4LQAkXu+7fnmx6VzGqnpb06I2WC594yNOZXdq1eWELiQAAAAAAKs4nuGVzadEsXq/dfHzW0ablQXmZziNAQAAAADAUW7o0ujH7n2xbD5l3etemB/Qp6dc9HqtDEo5Nlrj4vLElJi4jAAAAAAA4C5u6NI47dO2F4ESq0YY/Io+Pa5Pv3X6RYqRFPWHcQtXMeeCY/c2lxIA8B6ZI/0BANdBAHAXRzO8svlUQn9I+LTtrQz0fVU5CHq5geWF5S2sEdbJPvRrwBYAAAAAANdyukujn4MFiWw+lbRo2aI746iyMH/RBa/TjmCU7XW8FpcnDoO1CeNnAAAAAADgEk4HvC77vP3lF69fmBeZXQPG/xyt45XOZeKKPcEoJzK8ppv8DAAAAAAAHOZYwCubT4mRGSd93v5XLVjm00d+vubw67MrEDWezmXGbX5tl5v8DAAAAAAAHOZkhhdZMYqSNAJ/Mh2t3XVxf8RG58zauC");
+ WriteLiteral(@"7bsrwWlyeOB2snjd8BAAAAAAAXIODlPHntcFCza/TYb50sXj/Xp+ua5ngGAAAAAMC9nAx4TdL8+2QGShrV7PqqEy8qncuI1xW3cZUzRs0wp45djmcAAAAAAFzCkYBXNp8SwRC6gB2QGShpVLPLqQyvFx1Yp11dKMnwAgAAAADAxZzK8Jqi6R+KGQHA3hzU6rrY5G9OBL1mHVin5d0aF5cnxLHbKFgbM/4GAAAAAAAc5lTAi2yYR8kIlLQKatnardEYMdGJfTzn8LHLcQ0AAAAAgAtodq8wm08l9YcETf8IESj5mx6X0Sqo9ZTNr2fGoXaMi9phufSN9y3eV1buRwAucy2TG9EfvqNPf0xrAPDpdfB5/eFPaQkAgJc4keFFce+TEkYgsBetglqjxgiOdplzsC0tW/fi8oQI1LYK1iaMeQD0xw3eiD59T/9xSZ9e0acRWgWAz66Dl/Tp5/qPb+jTJVoEAOAlTgS8rtLsDXUfCDyo0TXQZi47uzXOOtiOVgbbpiXNA8D9N3nfUwh0AfD3dfBPjevgM7QGAMCLbA14ZfMpkf2SpNkb6iUQ+HQH89hSuD6dy4iAU9zBdpzWt8Gq9V+WNA8A9yPQBcDvvkMTAAC8zO4ML0axay6ZzadiXT63k2DWRWVhftSG1zHjgraUnuW1uDwh9k0nWXiTxrwAAAAAAMAhdge86O4lu30OanN1Gsiyo3j9nAvacc7hfcNxDgAAAACAgwh4uUs37WMmiGVpHa90LjOuP4y7oB2tqCE2adG8AAAAAABAMtsCXtl8imBXe9200TUT81pdx2vOJe0YT+cysw7uG451AAAAAAAcZGeGF0GADpgKDC7Mi5EZL5pawcGIjlaZcVFTPidrQYvLE6L2nJm6XDHjOQAAAAAAwAGajesiANAZEfB6v8N5uwleiW6N/1f2RhsjI865qB3FtrwicZ9085zbHM7+cy2Tq9MKALgGAgAAOMuWDK9sPpXUHxI0d0fMBAa7qcllVeH6WZe147hRU0yGaZueAw");
+ WriteLiteral(@"AAAAAAJLCrSyM3/51LGAHCTnQTvBo1RnaUbc6FbdnzNi0uT4hAbTfB2oTxXAAAAAAAYDMCXu7Uvr0OanENdLl8K0ZrnHFhO844fOxy3AMAAAAA4ADLA17ZfEpkuSRpalM6CZQ83cPypRauT+cyYnvHXdiOMrLOLjv0XAAAAAAA0CU7MrzIcjEvaQQKW+klaHVRWZgflbi9c25tyHQu0/W2LS5PiJEZJ3tY/aSxDAAAAAAAYCM7Al6Mzii73Q5qcPUasJJZvH7Oxe3Yy7bJCNYS8AUAAAAAwGaWBryy+VSMG/6utWo3GcEqKXW80rlM3OX7uJc6XpMS1j/JoQwAAAAAgL2szvDiZr97rYJI1yQsX1YdrzmXt+O4UWNM9j6wcxkAAAAAAMAEqwNe3Oz3IJtPnWy/hXkxMuNFKSs4GOmxV7MeaErT27i4PCG6lMqovxUzlgUAAAAAAGxCwMvdGrWfzBEWZXRrnPNAO845fOxyHgAAAAAAYCPLAl7ZfCqpyMmQ8bNGmUFflbj8nmqBpXMZkTkV90A7zhi1xswg4AUAAAAAgEdZmeF1lebtWcIIHB4lc3TFUWPEx24956G27DjLa3F5IiHaXuZ+NJYJAAAAAABsYGXAi6wW2e14UHNrQPLye8kYm/VQO846fOxyPgAAAAAAYBPNioVm8ynZGTJ+JgIlbxo/P23B8kUQ7Q2zT0rnMuOKt4I4Zup4XbZg/WKZb3M4w4uuZXJ1WgEA10EAAOAlVmV4kc0iT9IIIApfsWD5F5WF+dEunjfnsXaMp3OZtsfl4vKEqDs3acH6J41lAwAAAAAAi2kWLfcyTSvVlLIwf08RNbesIeqCmc0+mvFgO76oT6+0mcfKYK1Y9rtuaIhsPnVOORgUQQT3zhnTUTv6dNt4vHV99uZtTkMAAACg6efrIeOz9RV9Ovz5uFuHn7P1z9e3aDXAWpoFJ7pVGTJ+JgIlpy1cvqjjZTbgNefBduykjpeVx65YtmMBL/3cFAGuZ5WDYOW5Dp4yc+S54o");
+ WriteLiteral(@"05r0/v6G/OeU5JAAAA+J3xJfKs8Rm7k/uIK0eeKx7eOfIZe5sWBeSyIsOL7ozWtOkTFi7fVFfJdC4z59V2FLXHcukbHzl0/DpybuhvpuKN9WtH32C7MGi8kT9rBL9E3bef8cYMAAAAvzE+X7+g9N7rZcaYdvRl8vkakMyKGl5kd0lWKGvaYKhsbbDkYARIMxdmr2q67YvLEyIDyso6WzFjHXa9EU/p0w/0H8V0ReKiRfBLBNBu6Mt/gTMUAAAAfiAyuo58vpZ5T3T08/XXjO6RAHpkRcCLDC/Jfr16ZjSmVYYtXs1XTcw75+HmnHP42LXl/BBvlPrDDxW5ga5Gb8zf0Nf1I6O7JAAAANCXjC96f2TD5+v9z/F8vgZ6F5R8ERA384xEJ9nSenw0rFYHLF7NU53MZIx0OO7h5mxVx8vzAS/xbZAIQBlvlHYRWZ0/0Nf7LGcrAAAA+onx+VoMfPUN5SAgZQdRG+yHfL4GeiM7w4sotAXuFwZG1EA9FFGrUQtXM6oszF/sYL5ZjzdnvFENssXliYT+kLBh/QljXVa8GYvz74biTLdi8eb/Em/KAAAA6BdG10LZ3RfNEJ+vX2JPAN2RHfCiO6Nk790fHS3XgvuDC7ikW+NcHzTrjMPHrvR1GcEu8WY86HDbEvQCAACA5x0Jdjldo/pZgl5Ad4ISLwhJxZ4MGV9Z2RoaOfw5qlWsLl7YsnB9OpeJK94uWH+oUdDuso3rl7quI2/Ggy5pX4JeAAAA8CwXBbsOiaDXN9kzgDkyM7wYndECH20Njx7+HArWolqwFrJwdReVhfnRFn+f65NmHU/nMg/rkC0uT8RsPn4njXXK4qZg16GXKLQJAAAAj3rJhfe3z/OlMmCOzIDXVZpTrpWtoaFCWXukbldUqzhZvH62j5r3aPDOia64UtZpfNPj1mDzd7VQmRMZAAAAnmGMxujWXi3f1LfvHHsJ6IyUgJd+0olslSTNKdcHDxInsq0cruM110fN63TAa1LCeSeGRH7exW187vGnf8");
+ WriteLiteral(@"mJDAAAAE8wgklfc/Em7g8UxZ4COiMrw4ti9Ra4szl8IuAVVatWB7wa1vFK5zJiH8f7qHlnjJpkghMZUjLOGdf34x+7sKzEz9znZAYAAIAXiM/Xgy7fxivZfGqWXQW0R8DLpVZ3o9GNYqRhkfrBUNnaoNfCfKOg11wfNvPs4vKEOHZjDqw7pq+76xpXRv99T9TNu/DF9zmhAQAA4GpG7wmvDND1DfYY0J6sgBcF6yVb2jg10uxvDnVr7MeAl3hNThZW7yVQ/IJXGjl++r4yGF/npAYAAICbveChbT1HAXugvZ4DXvqJ5lSGTF9bWo83HS0xrFZtLVxvjGjYj1l8Mw6/rukuzzkRpPNUkPkLl/6ekxoAAACuZNTumvHYZhPwAtqQkeFFd0YL3N0eahrwUgP1UEStRi1c/aiyMH/xyP9n+rGNR0LF8fVyxMnAUWJxeSLRxfOe91pbnz73MSc1AAAA3MqLNbGuMGIj0JomYRlTNKNc790fHW03j+jWWKyqexZuhujWeMf4uR+7Mypj4V3ls1JsfCRUXHNwM0TA+G2zb25ubtfdihb8/c5gZH0vEnqw/vk4B4FzK8rug1ElWo5ykgMAAMB5C/OiZvLk//74d/+6UgueOfz1xfhWIapWao8N7RRd/gpEoO4NdiTQWE8Br2w+ldQfEjSjXLfXT7UNeEW1ypBSjFg5/N1Xjlw8+3IUkEsDm8pmJSy6a95ycDMuKyYCXkZ3Rtd9k7O2Fw393SdfGPl4eyi2uhuNPLzAFD8PbpWHPlVK2pai1lRlsDSgPP3tP//F6PaZf/vWjfQvOesBAABgi4V58Vn6WeMeZzIYqAff+Tj5SBKH/rn29MN7hlOb21Mj6zt/eO7epgtfzRWFgBfQVK8ZXnRntMC9wsBIu3lCwVpUC9ZClVqwbNFmXNTfDEbTX77zJf3neL+1sd5+okujUqypiVJNDYeD1ZJDmzK5uDwRe/LS0m6n87upHT/ZHoz8rzsXx/THtnX8gmp1/7EarCqb0S0xffnB0N");
+ WriteLiteral(@"ovLrz0oz9Zee1b3+PMB+Bn1zK5P9Mf/h0tAQAWOQh0fU05VvtKvy+ItHra8kZ8SExvrZwf+4dn76+nxldWXfSqrrBjgRb3oD0+n4CXZCtbQ0OFstZRn6+oVrGjeP1z/djO56PbD3++VxoYd3hzzJxHrsnu+h9Ll8b+4tdPX+wk2CWoWuXE70pqWdmJFF5J/IfsL/ZCe1wAAPjZ1/Xp5zQDAFhgYV4Eum4oDQq9twt4PfzcWlWDIvPr9V9eubS0ccotg7YNZvOpIXYw0FjXGV76iSW6MiZpQrk+eJAY7XReUcdruxTesHBzRB2vvqzflYzuPPx5rRy5cCG6ddvBzRFZW+92OK/j3+KIGl3/+TdPXTjadbFTgUBdqdcDjf705d+dXVIurCWVkcIpLgQAWrqWydX79KV9mb0LwMfXQPkOanR9t9Vn6EBAUc0sUr//Cv23xS9e+GcXPr7/T5KfrLvgVYp7iVvsbOCkXro0UqzeAnc2hzsOeEXV6rClB0ew9k/1h/F+bOez4cLDnwtVzemsKc9kSooujH+5+KUL4huubp4vujVWK80vOyuJu/uPBL0AAADQk4Ng1w+UNiVBwmq1q2ytt1fOj90vDET+6Inbn9LYgDv10qWR7oySre5GoxvFiKmU1MFQ2bKgVzRUHVvfifRdO4vujKKG16FaPRD+rBRzMugVW1yecH0AWWR2/fXvph7rNtjVKRH0Wh/Y4IIAAACA7nQY7OrV4oNE/OZHF87Q4IA7dXXjms2nRBScgJdkSxunRsw+R3RrtGp7QlotsV4I9107j0VO1odfLUW9VMfLEX/1wReTIoXbjnWJoBc1vQAAANCllxSbBnsSdb1+s3qaOlqAC3WbqTFJ08m3tB4fNfucsFq1pHB9MFDXwmrt7NZuqO/a+Wj9rkM71RDdGlsQ31x1WpxeljtnVvZHdAQAAAA6tjAvCtPP2LnKN5cfPyd6Q9D4gLt0e1KS3WWBu9tDpgNeaqAeiqjVqOxtiYSqp8VjsaIqhZLWN208Ei");
+ WriteLiteral(@"oqg2r5xO+LNTWxUw05+c1MYnF5IuHGNlvbi4Z+dW9sRMaymhSsb0iM4Lg6tMaFAQAAAJ056Mr4TTNPqdYC5V5XK0p+/M/lx8fYAYC7EPByiffuj452+1wrujVGtNrDC3Y/1fEaC+82/dv9YuyCw5vXyXlle1HMt1fOn5ZVt6tWNTUIjrI6+IAsLwAAAHTqeX0aNPOEaj1YkbFiUc9LfFHswGveYbcDjZm+ic3mU0n9IUbTyXV7/VTXAa+oVpGemRRSaw+zjdZ3+qeO16WBzaZ/26qGne7WeLmDeWwNeInU7KWNuJTjq14zHzMTwS4K2AMAAKBDz5r+vFnvPcPr0N998oURu1/w9dmbt9ntQGPd9FW7SrPJd68w0PXFMRSsRbVgLVSpBaVcrMNqbTgYqD8MaooujaWKqoQ1b2faiJEZRZfGZrYrIacL108uLk/Enry0tNtinlt2btBvV08POZXddWhtYEM5s32aiwTQpWuZXJ1WAMA1EH1vYX5W/9f0F9jlarAoaxM+3IiL7LL7Nr7qD9nxQHPd3MhSsF6yla2hoUJZ66kOV1SrSCteH9aqJ2pJ9UPx+vPR7bbzfFIcdPtojba+qd1eHxmUtaxqtbtjSIzWWFbLXCgAAADQypVunlSuBYv1eqAmYwPEiOafbA9aVg+mVg8Ei1U1djjd2RxeVRbmr7DrgcZMZXhl8ykRCEnSbHJ98CAx2usyRB0v/QIrpe9XNFQ9sY/XCmHlzPCep9u50eiMx22Ww+cei+x85OBmioDyu83+eH325rZ+Hn6o2BR4/mw3Ku0Nu1rpfvCD3dCeEqqGuFgAAACg1eforhSrwe2oVo3L2Ij/txmPPTa0Iy1rTAS59irqUKGijVRqwUc+m7+5/Pi/0B/+kbIwL/77jj69obz86i0OBeCA2TtQitVb4M7mcM8Br6halVK4Phioa2qwfmJZ/VC4/my40HaerUpYZHi96+BmdnKOvalP37BjY8S3VDKWI+p3ddulUdgLF5X43jAXCwvQ1QMA10");
+ WriteLiteral(@"Gug0Cf6DrTaa+q7sgKeG0WI9K+pd2raEMbpdC5ej1womdWqarWfrN6+mgXlpn9aWFeBLx+rLz8KrW94HtmuzRO0WRyre5GoxvFiJSi4IOhcs8RgVioerbZ37wc9BLdGUUNr3bK9eDgejmScHBTY4vLE+3Oszft2BCZ6djlUk89dpVqgJEaAQAAYI29irYtq3j9/d2YlM/Q68XIufVi+LFGwS5haSPerF6LCPz9UFmYf5Y9C7/rOOCVzadEEXMyvCRb2jglbSQP0a2x12WEtFrTYM96wbujNY5Fdjt/cylHnB6tseV5Jro1KjYEvfaqWk/F6gP1wH4Gqf4mrVRKvR07oo4XAAAAYNln34q25ZZtEcGuvYraMuPs7ZXzD9os5iWCXvA7Mze0BLsssLQeH5W1rLBaHZCwjKYZXl4uXN9J/a5DG5WI2wvXCz91b2sHgsFK6IxailwU/6uUovtBLwAAAMCtdsramqzi9b3YKIbH2gW7Fh8kNtf2op1kpL1EUXv4mZmAF6MzWuDu9pC0gJcaqIciarXrvmORUDURCDSv61asqEqhpHmujUdCRWXQxCh/hap2rlRTnUxnSywuT7TsVnl99uan+sPPrNyIqFox/YYfrGqntGLkYrCqnhL/F4GucrH3rO7B0iAXCwAAAFimVg/UdsraqpPbIEZe3K1oLXsAidpdHWR3HfWKsjA/xB6GH5Hh5aD37o+Oyl5mL90ao1rz7K5DXqzjNRbeNf2ce6UBr2R5fWrVBpgZXSZQC8a0UuRisKKdOXpdKe4Okt0FAAAAT9guh9YrtWBPIyxeGN7a7fa5W6XQWLt5fnVvbL3D7K5D4pvj59m78KOOAl7ZfEoU0Y7RXHLdXj8lPeAV1SpdR+9Daq1tsfb1He/V8bo0sGn6OZuVkNN1vK62m8Go5fWalRsxFC61fDMVdbrUcjipT48pRs2uQ6JuV7UspxtstBThggEAAIBWbslYyEYx/GkvXRsjarWr0ZZEdlelFmz5ofeT7cHdmx9d6CYL7Q");
+ WriteLiteral(@"UOD/hRpxleZHdZ4F5hYET2MkPBWlQL1kxHGfTnxNRgvW12mOjSWKqonmljMTKj6NJo1lYlfMHhTU8uLk+0DTJfn715MOywRc4P7TT5hioQVMuhMVGnK1ALnuhGW6uqSmlvQNp2DJYGuGAAAACglQ9lLKRcCxa3yqH73T7/8fhmVxleuxWtZd0u0ZXxrz744t1uP04rC/OzHCLwGwJeDlnZGhoqlLWoFcuOahXT0YGI1j6765CXitefj2539bxaPRBeL0cSDm9+R+fd9dmbbygWjdrYKCU7WNESok5XoKY2DJCKLox7O8PSujJGy1FFralcNAAAANDKLVkL0u/TNrdL5oNeoneEmbIgR1VqgabZXSLY9ZeLX1rZrWi9FNWnJjd8p23AK5tPiZv+BE0l1wcPEqNWLbubOl7hUHWs03nXCt7p1mhmdMbjPivFvFDHa9/12Zuia6P0oNdTZx5sh9Xq/htroKYO7tfpqmqJZtcO2cEuIVE4xQUDAAAArb38al6RWN9W1PParWjrZp7z9JkHW92ur1l3xsNg1yfbg8UeXxKjNcJ3OsnwIrvLAnc2hy0LeEXVqvmAl1o72+m8XipcfzZc6Pq5m5Ww0wEvU9/CGEEvqSM3xrRK7crptb2DOl2hc8frdB0lujHubsf3H2URmV0jBLwAAADQGalfAG8Uw/c3S+GOg2hXxj7blLl+icEuwJc6CXhdpZnkWt2NRjeKEUuHhh0MlTsOesXClbNml++FoJfozihqeHWrWFMTO9WQk0P4xhaXJ0wFnK/P3nxdOShkvyNrI6aC6mV9b7fsfiuK0+9ndtWCUhvgzM5pujMCAACgUz+T+TlYEN0b1/YiK+0K2V8Z+2w9Ed0ry1qvKFD/k1/9gyWCXUD3Wt6dZvMpUTQ7STPJtbRxasTqdZjp1hhWa6a7rK57oFvjWGS352WslaNOj9Y4ZfYJ12dvim+2vqVIqmMQDtaUyyONs7P3uzAWhvYnmd0Y99dbDSlnN0e5YAAAAKAzL79qySjmxa");
+ WriteLiteral(@"q6e383uqQ/bje+n6rWro2vrMpYl8jqenvl/P2/+PXTvdbsAnyvXToG3RktsLQet/wuXr/odly4PqzVxswu3wuF63up33VorRxxerTGrs7B67M3P9Wnf6//+H1FQi2DJ+Pbytno518uieBWuRhTdrdO7Wd3WeHi6gUuFgAAADDnoJaX9Nq2tXqgtrYX+URke5VrwUe+Wf+jJz68K0qB9LJ8NVAvLz5IbP6n95++83/uPrZuQct8yMEBvyHg5YC720OWB7z0C2YoolbbjgIZVmvDwUA9Znb5xYqqFEqaa9t4JFRUBtXeM4oLVc3pDK/E4vJE11mW12dv5vUpoxx80/VOLxty7ewD5ZRWfRjoKu1FpWd1HbqwltwfnREAAADowo8ViwI8IttrdTe6IgJfIuPrXz5+59OJUxu9dC0R39L/7M/f/4P/8re/m/p0bS9atqhNbnFYwG/aRSwYulSy9+6P2tZHS3Rr1C/Ce63mCWvVrkfgFHW8BsIVV7bzWHhXynJq9UD4k+Lg+GORnY8cfDniPLzbywKMbo5vZvMpEcATI7TMGsttG9DbfDCmbK6eVdY+TSqntoeVB6N3lHpoz7IXK4JdFKoHAABA10TXxoV50dvhB1bd04rAlz795A/P3RN1w2aMz9hXOvl8rRwEn0RATnw5fRCIWvjn4vP5P7aoRXYUAl7woabpGfqNscjuytBEcv313089tbwR/4Id6yrXgnu/3xlcajXPmcHiVa2LGl6CCHb9wYU1V7bzs2N39rO8ZDgd2vvtk0Nr7zr4cu4+eWnpP1q1cP1c/1GTDwI/1d+Af3otk6sf/WU1WFWWRu8oe5KDXqI4/WMb5wh2AQAA+NBbN9Lyuw4szIsBqF5SDgJSsr2mvPzqm00+X4tA25UGf/q+6IHRYntvKJ0FzMx6U9/W1zjK4DetujRO0Tzy3SsMjNi1rlCwFtWCtaYFloKButZtsEsQXRpLFfeNoCdGZpQV7BK2KuFxh19ScnF5Imbh8rfNzCwCU0/cm1DObr");
+ WriteLiteral(@"Uu/VbV98PRqR6oN513sDigTHx2kWAXAAAA5BGZXi+/+ifKQRfHVgV+h45NrUboEplZ324W7Orxc/ePLWiFHYuWC7heq4AX9bskW9kaGiqUNVsLE0W1StPi9ZFQ9XSvy3dj8frz0W2pyyvXg4M71dCQwy/LdeejGEHxyd8/oSSaBKlEBtjRqaKe7P4qRmIUXRgnPnucml0AAACwxsuvvqEcjGLeLEg1eWxqlBQgBoISWV3f0qfbFm2nyP56R/JSXzdGrwR8p2ENr2w+lWxykqMHHzxIjNq9TlHHa7sU3mj0t0gXozMet1YIK2eG91zVzjJGZzzufjF2YXCg/FsHX5YIeL3rtmM6VA0p59eSytnNMWUztqWsDWy07eooglwio2t4d1iJ7w1zYQAAAID1Xn71IGC1MC+ynZ5VPq+71Yq4sTgIQh0Eo+wguh7Kqj32ZpeZaEBf0FrcXEOyO5vDtge8omq1aUQhrFbP9rp8Ubjebc6GC9KXuVUNi770Tga8XD2AhAh8ndk+vT/tfzKIFJT3zv/mkXlGdk7tZ3OJeQEAAABHHGQ7vWFMom6WCHr992NziSDRa0aQzP7tk1Nw/2f6sl5nh8PPmnVpJOAl2epuNLpRjDjSLW4wVD4R9AqrteFAoO0onR1xU9BLdGcUNbxk266Exks1NezgS4stLk945rwUGVwnjrlqmGAXAAAA3OXlVxuNXvipI8Guz7dpe7/rpKL8tMFfxT3JuSPTyW1XlO8T7AIaBLyy+ZToypikaeRa2jg14tS6RbfG47+LhnrP7jq0Xgi7pp3HIruWLXu1HD3n8MtjIAkAAADAL15+VQS8MorI1vq86H6zgJcopi+6a37bxu6XgKtp3FTbY2k9PurUusNq9US6TVirjslavpsK11tRv+vQWjky/lhk5yMHX57I8PobziYAAADAJw4yzV7fnxbmxb26qD/23SNzfF+fbjuakQa4VLDJTTUku7s95FjASw3UQxG1+nAIvGCgrqnBurRq4cWKqhRKmuNtPB");
+ WriteLiteral(@"IqKoNq2bLlFyohpzO8EovLE2RfAgAAAH50MDrkrWO/yxPsAhoj4GWD9+6Pjjq9DUe7NcYkdmc85IY6XmPhXUuXX64HB9fLEadHL53kjAIAAAAAoLVHAl7ZfIpglwVur59yPOAV1SoPC+aHQ/K6Mx5a33G+jtelgU3L1/FZKTbu8Mu8yhkFAAAAAEBrxzO8CHhZ4F5hYMTpbQgFa1EtWAsZP5+WvXzRpbFUUZ18fftdGq22WQk7HfBKLi5PxDirAAAAAABo7njAi4L1kq1sDQ0VylrUDdsS1SoDkVA1EQgolhTccrJ4/fnoti3rKdbURKmmOp3ORmAaAAAAAIAWHga8svmUKIadoEnk+uBBYtQt2yLqeEU1+fW7Dq0VnIsDWTk643H3SgNOZ3kR8AIAAAAAoIWjGV7UBrLAnc1h1wS8omp1OKzVxqxavihcX60FHHltZ8MF29a16fxojRSuBwAAAACghf8vwABfcA5F9k0oGQAAAABJRU5ErkJggg==""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 46196, "\"", 46262, 1);
+#line 182 "WelcomePage.cshtml"
+WriteAttributeValue("", 46223, Resources.WelcomePageImageText_Skyline, 46223, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 46263, "\"", 46310, 1);
+#line 182 "WelcomePage.cshtml"
+WriteAttributeValue("", 46271, Resources.WelcomePageImageText_Skyline, 46271, 39, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" width=\"1212\" height=\"202\" /></div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"content\">\r\n <div class=\"bodyHeadline\">");
+#line 187 "WelcomePage.cshtml"
+ Write(Resources.WelcomeHeader);
+
+#line default
+#line hidden
+ WriteLiteral("</div>\r\n <div class=\"bodyContent\">");
+#line 188 "WelcomePage.cshtml"
+ Write(Resources.WelcomeStarted);
+
+#line default
+#line hidden
+ WriteLiteral("</div>\r\n <a class=\"bodyCTA longer\" href=\"http://go.microsoft.com/fwlink/?LinkID=398596&amp;clcid=0x409\">");
+#line 189 "WelcomePage.cshtml"
+ Write(Resources.WelcomeLearnMicrosoftAspNet);
+
+#line default
+#line hidden
+ WriteLiteral(@"<div>
+ <img src=""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAAdCAYAAAD7En+mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMjVCMDEwM0FBQkExMUUyQjdGNEEwODg0RjhFODY4OCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMjVCMDEwNEFBQkExMUUyQjdGNEEwODg0RjhFODY4OCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyNUIwM");
+ WriteLiteral(@"TAxQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMyNUIwMTAyQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+I1MZRAAAA4FJREFUeNq8md1x2kAQx3UaCoAKAg0YkgYQHTgV2KoAeMqjncfkBbsCSAd0AK5AOA1Y6YAOyJ7mf5rV6T4l8M3c2ALd/fZ/u/exh0gCy+VyyeiPrHPtqzPVd6p7IcTJ0rbxTO8FMe++/vIy/xY/TiF9CY+4If1ZUV1SHQb0V1L9Q/WFxJy7CCVxnZkk+hwtlIyTsCcGk53sMZJ8FGcY8Ux7NydB+xihJLI3k8Tug4TCi1uq92zEfpJxu4DwfoShY3y0o3a5Tyi82GKS0buA8G4xqV3uFAqRB4xYAoHPSURBH08IvwosR9omFCIbTDI0iok+GkxdrC5UjuqjHnpdCry7xaOcs2uLkQ2mLfQCBTeY1Ne6JRRzcoPH731EWsQuqM+jYU7WzD4iLWIX1GfFTLVwU+HaG4gQ3WExSRhcDzcVrldhYl63mAJCnwEtybiJx0tjNvETtCk9c/YDq2OuFjUSWjPJuInHSy0mtSk9c7ZmSvEpvntQIxswaGeE2wG1IDEzh1fl+694XLKvejFJzMzh1RZTwMhCdkZGjSJWVr5SnjEHT44o+MDjaPrt91gxyahRxMraYtpORoiCmpmyvesYMfcqCNvEKyNsnkVoq3ezLkx4qcW0eRahXTOl0C94eI9caGxiM0uTEzvVdGI6xHqZAzbJT4aQO8ADoUWJzQ0nqX/sfyuTjO7EpHa54SRVM1Ntwl+rbALf+zTmwDDKJtf7SqYZvwhs92nMATrOTFDbsY2F9gwrITdywVM0Vqbae0YmP7ZZVlMj05KiTXnoqjieRx7vFHAYIDJh28KxK5OJH");
+ WriteLiteral(@"AaIbDCFvsc5DO0sku3VMkqEvse5EuauIvF+gSgRqbbHrW7gSX4i2hn2uNUNPNliCi3LkA0nIV6NDPFCz2BYllExQ7waGeIFz2BSlmWUGLHtFUXy/o48TcOed3Umu62omI00DUVl5PdIwK+1t81UUm34vmYiAb8ZUzgS5eq+p4cnN7g5cCbyWqJsvO+J8GSDyXNc0+XYlr18RA5ZRt7/btjSnqsFwXE51mK68k3L/W+DqR8HRcAViFq5Xm1pGBP4wAyu751Crjs1z9ZM1wU1BLaYptsK4VktN9pRq0R9Y5/NMZL8slmC1ioSIu51ezNtkSACQ3HJckjXAX1v8nzsTxLwVBTT99OEiFxkMsNIVpu/J6yjhBpEG5mhv7vI8l+AAQB7WiwH/DuungAAAABJRU5ErkJggg==""");
+ BeginWriteAttribute("alt", "\r\n alt=\"", 49142, "\"", 49206, 1);
+#line 191 "WelcomePage.cshtml"
+WriteAttributeValue("", 49165, Resources.WelcomePageImageText_LearnMore, 49165, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ BeginWriteAttribute("title", " title=\"", 49207, "\"", 49256, 1);
+#line 191 "WelcomePage.cshtml"
+WriteAttributeValue("", 49215, Resources.WelcomePageImageText_LearnMore, 49215, 41, false);
+
+#line default
+#line hidden
+ EndWriteAttribute();
+ WriteLiteral(" width=\"58\" height=\"29\" /></div>\r\n </a>\r\n </div>\r\n\r\n</body>\r\n</html>\r\n");
+ }
+ #pragma warning restore 1998
+ }
+}
+#pragma warning restore 1591
diff --git a/src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.cshtml b/src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.cshtml
new file mode 100644
index 0000000000..3e15978d72
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/WelcomePage/Views/WelcomePage.cshtml
@@ -0,0 +1,196 @@
+@using System
+@using Microsoft.AspNetCore.Diagnostics
+@{
+ Response.ContentType = "text/html; charset=utf-8";
+}
+<!DOCTYPE html>
+<html lang="@System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName">
+<head>
+ <meta charset="utf-8" />
+ <title>@Resources.WelcomeTitle</title>
+ <style type="text/css">
+ @@font-face {
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ }
+
+ body {
+ background-color: #00abec;
+ color: #fff;
+ font-family: 'SegoeLight', helvetica, sans-serif;
+ font-size: 18px;
+ margin: 0;
+ padding: 0;
+ }
+
+ .content {
+ position: absolute;
+ left: 50px;
+ top: 38px;
+ width: 440px;
+ }
+
+ .content .azureLogo {
+ margin: 0 0 65px 0;
+ }
+
+ .content .bodyHeadline {
+ margin: 35px 0 0;
+ font-size: 40px;
+ line-height: 43px;
+ }
+
+ .content .bodyContent {
+ margin: 10px 0 30px 0;
+ line-height: 22px;
+ }
+
+ .content .bodyContent a {
+ color: #fff;
+ text-decoration: none;
+ }
+
+ .content .bodyContent a:hover {
+ opacity: .7;
+ }
+
+ .content .bodyCTA {
+ color: #fff;
+ display: block;
+ line-height: 30px;
+ height: 29px;
+ width: 230px;
+ cursor: pointer;
+ text-decoration: none;
+ position: relative;
+ }
+
+ .content .bodyCTA.longer {
+ margin-top: 10px;
+ width: 440px;
+ }
+
+ .content .bodyCTA div {
+ position: absolute;
+ overflow: hidden;
+ width: 29px;
+ height: 29px;
+ float: right;
+ top: 0;
+ right: 0;
+ }
+
+ .content .bodyCTA div img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ border: 0;
+ }
+
+ .content .bodyCTA:hover div img {
+ left: -29px;
+ }
+
+ .content .bodyCTA:hover {
+ opacity: .7;
+ }
+
+ .wrapper {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ min-width: 1200px;
+ }
+
+ .innerwrapper {
+ width: 384px;
+ height: 100%;
+ margin-right: auto;
+ margin-left: auto;
+ }
+
+ .browser {
+ position: absolute;
+ display: block;
+ top: 400px;
+ width: 384px;
+ height: 305px;
+ cursor: default;
+ z-index: 10;
+ }
+
+ .browser div {
+ width: 384px;
+ height: 305px;
+ position: absolute;
+ top: 40px;
+ left: 100px;
+ font-size: 200px;
+ text-align: left;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ }
+
+ .bulb {
+ position: fixed;
+ margin-left: 20px;
+ top: 0;
+ }
+
+ .light {
+ position: fixed;
+ margin-left: 53px;
+ top: 0;
+ opacity: 1;
+ }
+
+ .bottom {
+ position: fixed;
+ bottom: 0;
+ margin-right: auto;
+ margin-left: -303px;
+ z-index: -1;
+ height: 202px;
+ }
+ </style>
+ <script>
+ </script>
+</head>
+<body>
+ <div class="wrapper">
+ <div class="innerwrapper">
+ <div class="light first">
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARIAAAESCAYAAAAxN1ojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACKhJREFUeNrs3TFPG0kYgOE1kou4IIVTUITiXJAiFFxBk/8vGopQQBEKKFyci7jAhRsX3E4YB3I5Asva3p2Z55EspJNOOq/3Xn8zu7arCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADI1MAhILi/v9+v/wwb/murwWCwcPQQknJCMa7/jOLj3X/+bsKqfiye/F3Gx6KOzcorICSkGY0P9WM/hmK/4/+kZYxLeHwXFyGh3+EYx0cK1lGZh4ewCAm7D0fYyziIjxCOYQZPaxajMqujsvQqCwnbj8dB5k83TCtTURESNheQdTgOCz0EsxiUqbNBSGg+fUzqx8dqc1dVUreKU8qtKUVI+HNAQjSOCp4+mkwpN3VQ5g6FkPAYkHGMh4A0E0LyTVCEpPSAhPs7jqt0LtkKCkJiCVNEUC7dti8kuQdkvYl65GhsVdiUvXKTm5DkGJFwCfdz5SrMroSIXNcxuXEohCSXZcxJZR+kK2GZ89VyR0hSjsh6GTN0NDp3EycUyx0hMYVgOhGSMiJyECNiCumvMJl8cxiEpI8BCeEIm6ku6aYhXCo+t9QRkr4tZU6r7r9AiGZCRM4sdYSkDxEZx4hYyqTrwqeLhaTLiIRlzIkjkYVpHZMLh0FIdh2REBD7IXmZxenEvomQbD0gNlXzFvZLzsRESLYdkS+VTdUSYnLuC5SERERoyxUdIRERxERIRAQxERIRQUzKsucQiAgvCufC3/GcQEga+SwiPBHOhS9iIiRNphE3m/FcTE4dBiF5TUT8LAR/Mo5vNAjJsxEJH8BzkvCSw/iGg5D8FpGRsZUGTuIbD0LyMyLDylcB0NypzVchecoVGt5iaIoVkvU0Er5j1XqXtwqbr5+EpOyIrL/tHdo4Kn2/pPSJxLe9s7FzqeT9kmJDEn+8yq47m7L+YXghKWxJ44e82bRJqUucUicSSxoscYSk1TRyYEnDlpc4EyHJOyLrL26GbTqKy2chyXUNG98xYBfLZyHJcBqxwcoujUvaeC1pIhERdu1YSPKaRsLnaNwGz67tl/J1A6VMJMfOaUzCQtJmGgnrVJd76cqohKmkhInEkgZTiZC0mkZGQoKpREi8E2AyFpJOp5GhaYQeyfq+kpwnkolzF1OJkLT10XlL30KS6yeDswxJ/ISvz9RgKhGSVg6cr/TUX0KSxjRik5U+G8WPbAiJaQQsb4QEnKNC8sKyRkiwvBESpcfyRkiEBIo7V3MLia8LIKXlTTb3OmUTkvg5Br9Vg6lESFr54LzEBC0kXhSEREi8KNDQMJfLwFmEpNQfbsaSXEi8GBCYSLwY0Np7IREScO5mFBJfYkSyctjj2/MigDdCE4lpBOewkHgRIP19khxC8s55SOKS/4yYiQS6Z7PVRAKYSKAHUv/MzZ6XEHoh6X2SpEOS4++DgJCoOBS5RLe0ASEREkBIACEBEBJASAAhAYQEQEgAIQGEBHi0EhIHH9q6E5KODAaDhfMPhASwtPECgOk6j5BY3oCQmEgoXvJvhiYS8GYoJLWl85DEzYVESMBEYmkDnbtL/QkkH5LBYLAyleDNUEi8EJRsGd8MhURIoOxzN5eQfHc+kqh5Dk/CRALdusvhSWQRkrjGFBNSPHdNJJY3YFmTW0jmzksSMxMSIQFTdG4hifskYkIqljl9w19u35A2c35ighYSIaEUWZ2rWYWkHhXDZ25cBqbvVvW5KiQ9N3WeYhoREi8SQiIkvVjeuHpDXy1zW9bkOpFY3mAaEZKNTCUhJL5dnj66FRJTCbQxj0tvIVF+eLObXJ9YtiGJ5XcFh77IcpO1hIkk63cAknOd85PLOiTxS2NcCqZrq3gBQEgS9s15TMey36/LPiSmErqeRkpYYu8V8mKaSuhsGsnhd2uExFRCd8KVmiLexPYKelFNJezadSlPtJiQxKnE3a7syiL3KzWlTiTrdwifwWEXLkt6skWFJN7t6tZ5tm2ayw9fCcnzMQl7Jb6OkW0JE+9VaU96r9AX+9L5zpZclXC5V0iqnxuvPofDps1L2mA1kTwIG69L5z4bXNJclPrkiw1JHD/Pnf9syEWuX1okJC/HZFEVdNMQWzPL+btGhOR1MQlXcdw+z1stS17SCMmvzis3qtHcj+VxiVdphOT/p5JwIpw5EjR0FZfHxROSx5gsjKg0MC31Uq+QvByTcGI4OXhJuF/Em46Q/DEm4QSx+cpzwuTqtgEheZXzyudx+J3NVSFpNJWsN1/FhKcROSv5pjMheXtMvlYuC/MYEW8sQvKmmCziZCImZRMRIRETWrkQkVf8f+IQvM79/f1+/edL/Rg6GsUsZ85L+6YzIdldTE7rx8jRyD4iljNCstWYDONksu9oiAhC0jYmYTIZOxpZWcTljEu8QrLToJzUfw4diSzMKzebCUmHMZnUfz47Ekmb+uyMkPQhJuO41HFFJy0/fjrCp3iFpE8xsQmblmVcythUFZJeBuVT/efIkei18P2qF/ZDhCSFpU7YiHW/iaWMkNB6qRMmk4mj0QvzqvCfjBAS0wmmECERkx/TyaSyd7Jr06rQ3+IVkryDMorTiTtitytcibn0gTshKWG5c1y5VLxpYf/j2jJGSEoLymFc7tg/aScsXW7jryYiJILiaDQPSP24sQ8iJPwalPCwh/KKJUz18OPdAiIkPBOUcQyKTxb/ah6nj5lDISS8PijDGJO/Cl72hOkjhOPWzWRCQvuo7MeoHBQQlVWMx8z0ISSIingICYlEZRSDMo6PlL4TZR4f//g4v5DQv2nlQ/Vws9v7qj83vYX9jUUMx527ToWE9OIyjkugUfV4aXlbl5gXcZkyj3/vwj9zmVZIyH+CWS+H3jdYGq0j8XAimTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHL0rwADANq3ok68n5URAAAAAElFTkSuQmCC"
+ alt="@Resources.WelcomePageImageText_LightBulb" title="@Resources.WelcomePageImageText_LightBulb" width="274" height="274" /></div>
+ <div class="browser" style="opacity: 1; visible: true;">
+ <img src="data:image/png;base64,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"
+ alt="@Resources.WelcomePageImageText_Browser" title="@Resources.WelcomePageImageText_Browser" width="384" height="305" /><div>:-)</div>
+ </div>
+ <div class="light second">
+ <img src="data:image/png;base64,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"
+ alt="@Resources.WelcomePageImageText_LightBulb" title="@Resources.WelcomePageImageText_LightBulb" width="274" height="274" /></div>
+ <div class="bulb">
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVoAAAKSCAYAAACTAhMyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2OEMyQkI4M0Q4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2OEMyQkI4NEQ4NzIxMUUyQTdDN0Y3QzMzMkU0QTgyQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjY4QzJCQjgxRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjY4QzJCQjgyRDg3MjExRTJBN0M3RjdDMzMyRTRBODJCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+zKbSDwAAExlJREFUeNrs3b1u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"
+ alt="@Resources.WelcomePageImageText_LightBulb" title="@Resources.WelcomePageImageText_LightBulb" width="346" height="658" /></div>
+ <div class="bottom">
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABLwAAADKCAYAAACv61n1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAKsZJREFUeNrs3VtsXPed2PEzc87ceBlxJFKKR6Iskk5sL1U1m6hoyVYdF4VhAoMt7D64LwEmfUqQPGwKC32owTgbgy/BGNuXBA7ysNA2C7RddJ3dxQBFXTQQ7I4LrNEkgp2E3shkZVqOJVO8Dzn3nj95KFPk3M7M/9zmfD/A0VDkmXPO/M9l5vzm9//9g4qPZfOpaQUAAAAAAAB9Jejz1/+vOAQAAAAAAAD6i28DXkZ2V0J/nOIwAAAAAAAA6B9+zvA6DHTRrREAAAAAAKCP+DngNX3sEQAAAAAAAH3AlwGvbD6V1B8Sxn9Ft8YEhwIAAAAAAEB/8GuG13Sb/wMAAAAAAMCjCHgduMyhAAAAAAAA0B98F/Ayui8mj/16Uv99jMMBAAAAAADA+/yY4TXV5Pd0awQAAAAAAOgDfgx4NQtsTXI4AAAAAAAAeB8Br/a/BwAAAAAAgIf4KuCVzadaBbVi+t+nOCQAAAAAAAC8zW8ZXtM9/h0AAAAAAAAu57eAV7sMLgJeAAAAAAAAHuebgFc2n0rqD4k2syX0+RIcFgAAAAAAAN6l+ei1Xu1wPpHl9TaHBgDguGuZXN3M/G/dSAdoNQB+vQZyPQQAOMlPXRonO5zvMocFAAAAAACAd/ki4GV0U0x2OPukPn+MQwMAAAAAAMCb/JLhNW3x/AAAAAAAAHAJvwS8pkzOP8mhAQAAAAAA4E19H/AyuieS4QUAAAAAAOATfsjw6iZbK5bNp6Y4PAAAAAAAALzHDwGvaZufBwAAAAAAAAcR8JL/PAAAAAAAADiorwNeRrfEWJdPT+jPT3CIAAAAAAAAeEu/Z3hNO/x8AAAAAAAA2IyAV2uXOUQAAAAAAAC8pW8DXkZ3xF67JE7qy4lxmAAAAAAAAHhHP2d4TbtsOQAAAAAAALBBPwe8ZHVHJOAFAAAAAADgIX0Z8DK6IU5KWtwkhwkAAAAAAIB39GuGl8ysrFg2nyLLCwAAAAAAwCP6NeAlOytrikMFAAAAAADAG8jwcmZ5AAAAAAAAsEjfBbyM7ocxyYtN6MtNcrgAAAAAAAC4Xz9meFnV/ZDi9QAAAAAAAB7QjwEvq7ofXuVwAQAAAAAAcL++CngZ3Q4TFi0+qS8/xiEDAAAAAADgbv2W4WV1t0OK1wMAAAAAALhcvwW8rO52SMALAAAAAADA5bR+eSFGd0OrR1KkcD0AwPOuZXJ1L2//WzfSAfYiAL9eB7kGAkBn+inDy47sq1g2nyLLCwAAAAAAwMUIeJk3xWEDAAAAAADgXgS83LseAAAAAAAAdKEvAl42dzNM6OtLcugAAAAAAAC4U79keNmddUXxegAAAAAAAJfql4CX3XW1rnLoAAAAAAAAuJPnA15G98KEzatN6uuNcfgAAAAAAAC4Tz9keE37bL0AAAAAAABogYCX99YLAAAAAACAFjwd8MrmU6Iro1MjJlK4HgAAAAAAwIW8nuHlZJZVLJtPkeUFAAAAAADgMl4PeE35fP0AAAAAAAA4xrMBL2OURKczrMjwAgAAAAAAcBkvZ3i5oYZWIptPJTmMAAAAAAAA3MPLAS+3ZFdRvB4AAAAAAMBFCHj17iqHEQAAAAAAgHt4MuBldCOMuWRzkkY9MQAAAAAAALiAVzO83JZVRfF6AAAAAAAAl/BqwMttdbMIeAEAAAAAALiE5wJe2XwqoT+4bWRECtcDAAAAAAC4hObBbXZjNlUsm09NX5+9+T6HVG/SuUw8l76xSUtAlmuZXP347966kQ7QMgCsvM6YZeV16fj2cQ0E4LfrIAB/8mKXxssu3a4pDicp/k06l5mjGQAAAAAAQLc8FfAyRkN0a/dB6njJ8aI+zdAMAAAAAACgW17L8HJzUCmRzaeSHFLdE90ZjX1MhhcAAAAAAOia12p4TXpg++5yWHXtMNA1ns5lpnPpG9REA/qcjJofVqCOCACug1wHAQDeRoaXXFc5pHoy2+RnAAAAAACAjnkm4JXNp0RR+JjLNzNp1BlDd+aa/AwAAAAAANAxL2V4TbOd/Ut0YdQf4kd+NWPU9AIAAAAAADCFgJd/t9NtXmzwO7o1AgAAAAAA0zwR8DJGP0x4pE0nOay60ii4RbdGAAAAAABgmlcyvLwURIpl8ymyvExI5zLjSuPMOAJeAAAAAADANK8EvLw2+uEUh5YpM01+HzdqewEAAAAAAHTM9QEvY9TDpMfalSCNOXNd/g0AAAAAAOAEL2R4eTF4lDDqjqEzrYrTE/ACAAAAAACmEPCyDsXrO5DOZURAK95q/xs1vgAAAAAAADrihYCXVwNHVzm8OjIjaR4AAAAAAIB9mps3zhjtMObRtk2K+mPXZ2/ucpi1NNfhPP+VpgIAuMm1TO4Z/SFDSwDw8XXwkv7wCi0BwI00l2+f10c7FAG7dznMGjO6KnbSXXGW1kKbD1t1WfO/dSMdoEUBtLmGPGPc4D3jxetgu3m5DgLo4DpyybgOfp3WAOBWbg94eX20QwJerXVakD6ezmVmc+kbeZoMAODgDd4zissCXQBg83VwRH/4jj79sT6N0CIA3My1AS9jlMOEx9uXwvWtmRmB8Tl9IuAFAHDiBu8ZhUAXAK6F31MIdAHwEDdneE33QfvGRB2y67M33+dQe1Q6lxEjM5opRj+nUB8AAGDvzd0zCoEuAFwLv25cCy/RGgC8hICX9UQdMgJeJ5mtyzUuan7l0jc+oukAeOgmoe70NlCPqav99oxCoAvoi+sh18Cer4V/phDoAuBRQTduVDafEl0Zk33SxtMcZg3N2fQcAADM3OD9XH8Q0zO0BgCuhQS7AHhX0KXbNdVHbZww6pHhUTM2PQcAADOeoQkAgGshAO9za8Cr37KiKF5/RDqXEft3vIunzhm1vwAAAAAAAJoi4GWPqxxqj+ila+IszQcAAAAAAFpxXcBLjGrYh+2c1F9XjMPtoTmHngsAAAAAAHzAjRle/VrkneL1yn53xniPbUEdLwAAAAAA0BIBL16X3XrN0Bo3aoABAAAAAAA05KqAlzGaYb92/aNw/QEZXRKp4wUAAAAAAJrSXLY9/VzcPSbqk12fvfm+z485GcGqF/XpJ5y+/eVaJlfvh+1460Y6wN4E4OVrYS/bwDUQAAC4hdu6NPZ7FtSUnw+2dC4jgl1xCYuaNmqBAQAAAAAAnOCagFc2n0roD8k+b2+/1556TuKyGK0RAAAAAAA05KYMLz8EgxJGnTK/mnXpsgAAAAAAQB9xU8DLL939fFm8Pp3LjCtyg5pkeAEAAAAAgIZcUbQ+m0+JkRn90t1PFOZ/24fHmuwAVTydy0zn0jfe5zSG38gsak2BaQBcB7kOAgDQj9yS4eWn2lZJI8DnNzMWLPNFTmEAAAAAAHCcWwJefuvm58fi9VZ0QaSOFwAAAAAAOEFzyXb4LQAkXu+7fnmx6VzGqnpb06I2WC594yNOZXdq1eWELiQAAAAAAKs4nuGVzadEsXq/dfHzW0ablQXmZziNAQAAAADAUW7o0ujH7n2xbD5l3etemB/Qp6dc9HqtDEo5Nlrj4vLElJi4jAAAAAAA4C5u6NI47dO2F4ESq0YY/Io+Pa5Pv3X6RYqRFPWHcQtXMeeCY/c2lxIA8B6ZI/0BANdBAHAXRzO8svlUQn9I+LTtrQz0fVU5CHq5geWF5S2sEdbJPvRrwBYAAAAAANdyukujn4MFiWw+lbRo2aI746iyMH/RBa/TjmCU7XW8FpcnDoO1CeNnAAAAAADgEk4HvC77vP3lF69fmBeZXQPG/xyt45XOZeKKPcEoJzK8ppv8DAAAAAAAHOZYwCubT4mRGSd93v5XLVjm00d+vubw67MrEDWezmXGbX5tl5v8DAAAAAAAHOZkhhdZMYqSNAJ/Mh2t3XVxf8RG58zauC7bsrwWlyeOB2snjd8BAAAAAAAXIODlPHntcFCza/TYb50sXj/Xp+ua5ngGAAAAAMC9nAx4TdL8+2QGShrV7PqqEy8qncuI1xW3cZUzRs0wp45djmcAAAAAAFzCkYBXNp8SwRC6gB2QGShpVLPLqQyvFx1Yp11dKMnwAgAAAADAxZzK8Jqi6R+KGQHA3hzU6rrY5G9OBL1mHVin5d0aF5cnxLHbKFgbM/4GAAAAAAAc5lTAi2yYR8kIlLQKatnardEYMdGJfTzn8LHLcQ0AAAAAgAtodq8wm08l9YcETf8IESj5mx6X0Sqo9ZTNr2fGoXaMi9phufSN9y3eV1buRwAucy2TG9EfvqNPf0xrAPDpdfB5/eFPaQkAgJc4keFFce+TEkYgsBetglqjxgiOdplzsC0tW/fi8oQI1LYK1iaMeQD0xw3eiD59T/9xSZ9e0acRWgWAz66Dl/Tp5/qPb+jTJVoEAOAlTgS8rtLsDXUfCDyo0TXQZi47uzXOOtiOVgbbpiXNA8D9N3nfUwh0AfD3dfBPjevgM7QGAMCLbA14ZfMpkf2SpNkb6iUQ+HQH89hSuD6dy4iAU9zBdpzWt8Gq9V+WNA8A9yPQBcDvvkMTAAC8zO4ML0axay6ZzadiXT63k2DWRWVhftSG1zHjgraUnuW1uDwh9k0nWXiTxrwAAAAAAMAhdge86O4lu30OanN1Gsiyo3j9nAvacc7hfcNxDgAAAACAgwh4uUs37WMmiGVpHa90LjOuP4y7oB2tqCE2adG8AAAAAABAMtsCXtl8imBXe9200TUT81pdx2vOJe0YT+cysw7uG451AAAAAAAcZGeGF0GADpgKDC7Mi5EZL5pawcGIjlaZcVFTPidrQYvLE6L2nJm6XDHjOQAAAAAAwAGajesiANAZEfB6v8N5uwleiW6N/1f2RhsjI865qB3FtrwicZ9085zbHM7+cy2Tq9MKALgGAgAAOMuWDK9sPpXUHxI0d0fMBAa7qcllVeH6WZe147hRU0yGaZueAwAAAAAAJLCrSyM3/51LGAHCTnQTvBo1RnaUbc6FbdnzNi0uT4hAbTfB2oTxXAAAAAAAYDMCXu7Uvr0OanENdLl8K0ZrnHFhO844fOxy3AMAAAAA4ADLA17ZfEpkuSRpalM6CZQ83cPypRauT+cyYnvHXdiOMrLOLjv0XAAAAAAA0CU7MrzIcjEvaQQKW+klaHVRWZgflbi9c25tyHQu0/W2LS5PiJEZJ3tY/aSxDAAAAAAAYCM7Al6Mzii73Q5qcPUasJJZvH7Oxe3Yy7bJCNYS8AUAAAAAwGaWBryy+VSMG/6utWo3GcEqKXW80rlM3OX7uJc6XpMS1j/JoQwAAAAAgL2szvDiZr97rYJI1yQsX1YdrzmXt+O4UWNM9j6wcxkAAAAAAMAEqwNe3Oz3IJtPnWy/hXkxMuNFKSs4GOmxV7MeaErT27i4PCG6lMqovxUzlgUAAAAAAGxCwMvdGrWfzBEWZXRrnPNAO845fOxyHgAAAAAAYCPLAl7ZfCqpyMmQ8bNGmUFflbj8nmqBpXMZkTkV90A7zhi1xswg4AUAAAAAgEdZmeF1lebtWcIIHB4lc3TFUWPEx24956G27DjLa3F5IiHaXuZ+NJYJAAAAAABsYGXAi6wW2e14UHNrQPLye8kYm/VQO846fOxyPgAAAAAAYBPNioVm8ynZGTJ+JgIlbxo/P23B8kUQ7Q2zT0rnMuOKt4I4Zup4XbZg/WKZb3M4w4uuZXJ1WgEA10EAAOAlVmV4kc0iT9IIIApfsWD5F5WF+dEunjfnsXaMp3OZtsfl4vKEqDs3acH6J41lAwAAAAAAi2kWLfcyTSvVlLIwf08RNbesIeqCmc0+mvFgO76oT6+0mcfKYK1Y9rtuaIhsPnVOORgUQQT3zhnTUTv6dNt4vHV99uZtTkMAAACg6efrIeOz9RV9Ovz5uFuHn7P1z9e3aDXAWpoFJ7pVGTJ+JgIlpy1cvqjjZTbgNefBduykjpeVx65YtmMBL/3cFAGuZ5WDYOW5Dp4yc+S54o05r0/v6G/OeU5JAAAA+J3xJfKs8Rm7k/uIK0eeKx7eOfIZe5sWBeSyIsOL7ozWtOkTFi7fVFfJdC4z59V2FLXHcukbHzl0/DpybuhvpuKN9WtH32C7MGi8kT9rBL9E3bef8cYMAAAAvzE+X7+g9N7rZcaYdvRl8vkakMyKGl5kd0lWKGvaYKhsbbDkYARIMxdmr2q67YvLEyIDyso6WzFjHXa9EU/p0w/0H8V0ReKiRfBLBNBu6Mt/gTMUAAAAfiAyuo58vpZ5T3T08/XXjO6RAHpkRcCLDC/Jfr16ZjSmVYYtXs1XTcw75+HmnHP42LXl/BBvlPrDDxW5ga5Gb8zf0Nf1I6O7JAAAANCXjC96f2TD5+v9z/F8vgZ6F5R8ERA384xEJ9nSenw0rFYHLF7NU53MZIx0OO7h5mxVx8vzAS/xbZAIQBlvlHYRWZ0/0Nf7LGcrAAAA+onx+VoMfPUN5SAgZQdRG+yHfL4GeiM7w4sotAXuFwZG1EA9FFGrUQtXM6oszF/sYL5ZjzdnvFENssXliYT+kLBh/QljXVa8GYvz74biTLdi8eb/Em/KAAAA6BdG10LZ3RfNEJ+vX2JPAN2RHfCiO6Nk790fHS3XgvuDC7ikW+NcHzTrjMPHrvR1GcEu8WY86HDbEvQCAACA5x0Jdjldo/pZgl5Ad4ISLwhJxZ4MGV9Z2RoaOfw5qlWsLl7YsnB9OpeJK94uWH+oUdDuso3rl7quI2/Ggy5pX4JeAAAA8CwXBbsOiaDXN9kzgDkyM7wYndECH20Njx7+HArWolqwFrJwdReVhfnRFn+f65NmHU/nMg/rkC0uT8RsPn4njXXK4qZg16GXKLQJAAAAj3rJhfe3z/OlMmCOzIDXVZpTrpWtoaFCWXukbldUqzhZvH62j5r3aPDOia64UtZpfNPj1mDzd7VQmRMZAAAAnmGMxujWXi3f1LfvHHsJ6IyUgJd+0olslSTNKdcHDxInsq0cruM110fN63TAa1LCeSeGRH7exW187vGnf8mJDAAAAE8wgklfc/Em7g8UxZ4COiMrw4ti9Ra4szl8IuAVVatWB7wa1vFK5zJiH8f7qHlnjJpkghMZUjLOGdf34x+7sKzEz9znZAYAAIAXiM/Xgy7fxivZfGqWXQW0R8DLpVZ3o9GNYqRhkfrBUNnaoNfCfKOg11wfNvPs4vKEOHZjDqw7pq+76xpXRv99T9TNu/DF9zmhAQAA4GpG7wmvDND1DfYY0J6sgBcF6yVb2jg10uxvDnVr7MeAl3hNThZW7yVQ/IJXGjl++r4yGF/npAYAAICbveChbT1HAXugvZ4DXvqJ5lSGTF9bWo83HS0xrFZtLVxvjGjYj1l8Mw6/rukuzzkRpPNUkPkLl/6ekxoAAACuZNTumvHYZhPwAtqQkeFFd0YL3N0eahrwUgP1UEStRi1c/aiyMH/xyP9n+rGNR0LF8fVyxMnAUWJxeSLRxfOe91pbnz73MSc1AAAA3MqLNbGuMGIj0JomYRlTNKNc790fHW03j+jWWKyqexZuhujWeMf4uR+7Mypj4V3ls1JsfCRUXHNwM0TA+G2zb25ubtfdihb8/c5gZH0vEnqw/vk4B4FzK8rug1ElWo5ykgMAAMB5C/OiZvLk//74d/+6UgueOfz1xfhWIapWao8N7RRd/gpEoO4NdiTQWE8Br2w+ldQfEjSjXLfXT7UNeEW1ypBSjFg5/N1Xjlw8+3IUkEsDm8pmJSy6a95ycDMuKyYCXkZ3Rtd9k7O2Fw393SdfGPl4eyi2uhuNPLzAFD8PbpWHPlVK2pai1lRlsDSgPP3tP//F6PaZf/vWjfQvOesBAABgi4V58Vn6WeMeZzIYqAff+Tj5SBKH/rn29MN7hlOb21Mj6zt/eO7epgtfzRWFgBfQVK8ZXnRntMC9wsBIu3lCwVpUC9ZClVqwbNFmXNTfDEbTX77zJf3neL+1sd5+okujUqypiVJNDYeD1ZJDmzK5uDwRe/LS0m6n87upHT/ZHoz8rzsXx/THtnX8gmp1/7EarCqb0S0xffnB0NovLrz0oz9Zee1b3+PMB+Bn1zK5P9Mf/h0tAQAWOQh0fU05VvtKvy+ItHra8kZ8SExvrZwf+4dn76+nxldWXfSqrrBjgRb3oD0+n4CXZCtbQ0OFstZRn6+oVrGjeP1z/djO56PbD3++VxoYd3hzzJxHrsnu+h9Ll8b+4tdPX+wk2CWoWuXE70pqWdmJFF5J/IfsL/ZCe1wAAPjZ1/Xp5zQDAFhgYV4Eum4oDQq9twt4PfzcWlWDIvPr9V9eubS0ccotg7YNZvOpIXYw0FjXGV76iSW6MiZpQrk+eJAY7XReUcdruxTesHBzRB2vvqzflYzuPPx5rRy5cCG6ddvBzRFZW+92OK/j3+KIGl3/+TdPXTjadbFTgUBdqdcDjf705d+dXVIurCWVkcIpLgQAWrqWydX79KV9mb0LwMfXQPkOanR9t9Vn6EBAUc0sUr//Cv23xS9e+GcXPr7/T5KfrLvgVYp7iVvsbOCkXro0UqzeAnc2hzsOeEXV6rClB0ew9k/1h/F+bOez4cLDnwtVzemsKc9kSooujH+5+KUL4huubp4vujVWK80vOyuJu/uPBL0AAADQk4Ng1w+UNiVBwmq1q2ytt1fOj90vDET+6Inbn9LYgDv10qWR7oySre5GoxvFiKmU1MFQ2bKgVzRUHVvfifRdO4vujKKG16FaPRD+rBRzMugVW1yecH0AWWR2/fXvph7rNtjVKRH0Wh/Y4IIAAACA7nQY7OrV4oNE/OZHF87Q4IA7dXXjms2nRBScgJdkSxunRsw+R3RrtGp7QlotsV4I9107j0VO1odfLUW9VMfLEX/1wReTIoXbjnWJoBc1vQAAANCllxSbBnsSdb1+s3qaOlqAC3WbqTFJ08m3tB4fNfucsFq1pHB9MFDXwmrt7NZuqO/a+Wj9rkM71RDdGlsQ31x1WpxeljtnVvZHdAQAAAA6tjAvCtPP2LnKN5cfPyd6Q9D4gLt0e1KS3WWBu9tDpgNeaqAeiqjVqOxtiYSqp8VjsaIqhZLWN208Eioqg2r5xO+LNTWxUw05+c1MYnF5IuHGNlvbi4Z+dW9sRMaymhSsb0iM4Lg6tMaFAQAAAJ056Mr4TTNPqdYC5V5XK0p+/M/lx8fYAYC7EPByiffuj452+1wrujVGtNrDC3Y/1fEaC+82/dv9YuyCw5vXyXlle1HMt1fOn5ZVt6tWNTUIjrI6+IAsLwAAAHTqeX0aNPOEaj1YkbFiUc9LfFHswGveYbcDjZm+ic3mU0n9IUbTyXV7/VTXAa+oVpGemRRSaw+zjdZ3+qeO16WBzaZ/26qGne7WeLmDeWwNeInU7KWNuJTjq14zHzMTwS4K2AMAAKBDz5r+vFnvPcPr0N998oURu1/w9dmbt9ntQGPd9FW7SrPJd68w0PXFMRSsRbVgLVSpBaVcrMNqbTgYqD8MaooujaWKqoQ1b2faiJEZRZfGZrYrIacL108uLk/Enry0tNtinlt2btBvV08POZXddWhtYEM5s32aiwTQpWuZXJ1WAMA1EH1vYX5W/9f0F9jlarAoaxM+3IiL7LL7Nr7qD9nxQHPd3MhSsF6yla2hoUJZ66kOV1SrSCteH9aqJ2pJ9UPx+vPR7bbzfFIcdPtojba+qd1eHxmUtaxqtbtjSIzWWFbLXCgAAADQypVunlSuBYv1eqAmYwPEiOafbA9aVg+mVg8Ei1U1djjd2RxeVRbmr7DrgcZMZXhl8ykRCEnSbHJ98CAx2usyRB0v/QIrpe9XNFQ9sY/XCmHlzPCep9u50eiMx22Ww+cei+x85OBmioDyu83+eH325rZ+Hn6o2BR4/mw3Ku0Nu1rpfvCD3dCeEqqGuFgAAACg1eforhSrwe2oVo3L2Ij/txmPPTa0Iy1rTAS59irqUKGijVRqwUc+m7+5/Pi/0B/+kbIwL/77jj69obz86i0OBeCA2TtQitVb4M7mcM8Br6halVK4Phioa2qwfmJZ/VC4/my40HaerUpYZHi96+BmdnKOvalP37BjY8S3VDKWI+p3ddulUdgLF5X43jAXCwvQ1QMA10Gug0Cf6DrTaa+q7sgKeG0WI9K+pd2raEMbpdC5ej1womdWqarWfrN6+mgXlpn9aWFeBLx+rLz8KrW94HtmuzRO0WRyre5GoxvFiJSi4IOhcs8RgVioerbZ37wc9BLdGUUNr3bK9eDgejmScHBTY4vLE+3Oszft2BCZ6djlUk89dpVqgJEaAQAAYI29irYtq3j9/d2YlM/Q68XIufVi+LFGwS5haSPerF6LCPz9UFmYf5Y9C7/rOOCVzadEEXMyvCRb2jglbSQP0a2x12WEtFrTYM96wbujNY5Fdjt/cylHnB6tseV5Jro1KjYEvfaqWk/F6gP1wH4Gqf4mrVRKvR07oo4XAAAAYNln34q25ZZtEcGuvYraMuPs7ZXzD9os5iWCXvA7Mze0BLsssLQeH5W1rLBaHZCwjKYZXl4uXN9J/a5DG5WI2wvXCz91b2sHgsFK6IxailwU/6uUovtBLwAAAMCtdsramqzi9b3YKIbH2gW7Fh8kNtf2op1kpL1EUXv4mZmAF6MzWuDu9pC0gJcaqIciarXrvmORUDURCDSv61asqEqhpHmujUdCRWXQxCh/hap2rlRTnUxnSywuT7TsVnl99uan+sPPrNyIqFox/YYfrGqntGLkYrCqnhL/F4GucrH3rO7B0iAXCwAAAFimVg/UdsraqpPbIEZe3K1oLXsAidpdHWR3HfWKsjA/xB6GH5Hh5aD37o+Oyl5mL90ao1rz7K5DXqzjNRbeNf2ce6UBr2R5fWrVBpgZXSZQC8a0UuRisKKdOXpdKe4Okt0FAAAAT9guh9YrtWBPIyxeGN7a7fa5W6XQWLt5fnVvbL3D7K5D4pvj59m78KOOAl7ZfEoU0Y7RXHLdXj8lPeAV1SpdR+9Daq1tsfb1He/V8bo0sGn6OZuVkNN1vK62m8Go5fWalRsxFC61fDMVdbrUcjipT48pRs2uQ6JuV7UspxtstBThggEAAIBWbslYyEYx/GkvXRsjarWr0ZZEdlelFmz5ofeT7cHdmx9d6CYL7QUOD/hRpxleZHdZ4F5hYET2MkPBWlQL1kxHGfTnxNRgvW12mOjSWKqonmljMTKj6NJo1lYlfMHhTU8uLk+0DTJfn715MOywRc4P7TT5hioQVMuhMVGnK1ALnuhGW6uqSmlvQNp2DJYGuGAAAACglQ9lLKRcCxa3yqH73T7/8fhmVxleuxWtZd0u0ZXxrz744t1uP04rC/OzHCLwGwJeDlnZGhoqlLWoFcuOahXT0YGI1j6765CXitefj2539bxaPRBeL0cSDm9+R+fd9dmbbygWjdrYKCU7WNESok5XoKY2DJCKLox7O8PSujJGy1FFralcNAAAANDKLVkL0u/TNrdL5oNeoneEmbIgR1VqgabZXSLY9ZeLX1rZrWi9FNWnJjd8p23AK5tPiZv+BE0l1wcPEqNWLbubOl7hUHWs03nXCt7p1mhmdMbjPivFvFDHa9/12Zuia6P0oNdTZx5sh9Xq/htroKYO7tfpqmqJZtcO2cEuIVE4xQUDAAAArb38al6RWN9W1PParWjrZp7z9JkHW92ur1l3xsNg1yfbg8UeXxKjNcJ3OsnwIrvLAnc2hy0LeEXVqvmAl1o72+m8XipcfzZc6Pq5m5Ww0wEvU9/CGEEvqSM3xrRK7crptb2DOl2hc8frdB0lujHubsf3H2URmV0jBLwAAADQGalfAG8Uw/c3S+GOg2hXxj7blLl+icEuwJc6CXhdpZnkWt2NRjeKEUuHhh0MlTsOesXClbNml++FoJfozihqeHWrWFMTO9WQk0P4xhaXJ0wFnK/P3nxdOShkvyNrI6aC6mV9b7fsfiuK0+9ndtWCUhvgzM5pujMCAACgUz+T+TlYEN0b1/YiK+0K2V8Z+2w9Ed0ry1qvKFD/k1/9gyWCXUD3Wt6dZvMpUTQ7STPJtbRxasTqdZjp1hhWa6a7rK57oFvjWGS352WslaNOj9Y4ZfYJ12dvim+2vqVIqmMQDtaUyyONs7P3uzAWhvYnmd0Y99dbDSlnN0e5YAAAAKAzL79qySjmxaq6e383uqQ/bje+n6rWro2vrMpYl8jqenvl/P2/+PXTvdbsAnyvXToG3RktsLQet/wuXr/odly4PqzVxswu3wuF63up33VorRxxerTGrs7B67M3P9Wnf6//+H1FQi2DJ+Pbytno518uieBWuRhTdrdO7Wd3WeHi6gUuFgAAADDnoJaX9Nq2tXqgtrYX+URke5VrwUe+Wf+jJz68K0qB9LJ8NVAvLz5IbP6n95++83/uPrZuQct8yMEBvyHg5YC720OWB7z0C2YoolbbjgIZVmvDwUA9Znb5xYqqFEqaa9t4JFRUBtXeM4oLVc3pDK/E4vJE11mW12dv5vUpoxx80/VOLxty7ewD5ZRWfRjoKu1FpWd1HbqwltwfnREAAADowo8ViwI8IttrdTe6IgJfIuPrXz5+59OJUxu9dC0R39L/7M/f/4P/8re/m/p0bS9atqhNbnFYwG/aRSwYulSy9+6P2tZHS3Rr1C/Ce63mCWvVrkfgFHW8BsIVV7bzWHhXynJq9UD4k+Lg+GORnY8cfDniPLzbywKMbo5vZvMpEcATI7TMGsttG9DbfDCmbK6eVdY+TSqntoeVB6N3lHpoz7IXK4JdFKoHAABA10TXxoV50dvhB1bd04rAlz795A/P3RN1w2aMz9hXOvl8rRwEn0RATnw5fRCIWvjn4vP5P7aoRXYUAl7woabpGfqNscjuytBEcv313089tbwR/4Id6yrXgnu/3xlcajXPmcHiVa2LGl6CCHb9wYU1V7bzs2N39rO8ZDgd2vvtk0Nr7zr4cu4+eWnpP1q1cP1c/1GTDwI/1d+Af3otk6sf/WU1WFWWRu8oe5KDXqI4/WMb5wh2AQAA+NBbN9Lyuw4szIsBqF5SDgJSsr2mvPzqm00+X4tA25UGf/q+6IHRYntvKJ0FzMx6U9/W1zjK4DetujRO0Tzy3SsMjNi1rlCwFtWCtaYFloKButZtsEsQXRpLFfeNoCdGZpQV7BK2KuFxh19ScnF5Imbh8rfNzCwCU0/cm1DObrUu/VbV98PRqR6oN513sDigTHx2kWAXAAAA5BGZXi+/+ifKQRfHVgV+h45NrUboEplZ324W7Orxc/ePLWiFHYuWC7heq4AX9bskW9kaGiqUNVsLE0W1StPi9ZFQ9XSvy3dj8frz0W2pyyvXg4M71dCQwy/LdeejGEHxyd8/oSSaBKlEBtjRqaKe7P4qRmIUXRgnPnucml0AAACwxsuvvqEcjGLeLEg1eWxqlBQgBoISWV3f0qfbFm2nyP56R/JSXzdGrwR8p2ENr2w+lWxykqMHHzxIjNq9TlHHa7sU3mj0t0gXozMet1YIK2eG91zVzjJGZzzufjF2YXCg/FsHX5YIeL3rtmM6VA0p59eSytnNMWUztqWsDWy07eooglwio2t4d1iJ7w1zYQAAAID1Xn71IGC1MC+ynZ5VPq+71Yq4sTgIQh0Eo+wguh7Kqj32ZpeZaEBf0FrcXEOyO5vDtge8omq1aUQhrFbP9rp8Ubjebc6GC9KXuVUNi770Tga8XD2AhAh8ndk+vT/tfzKIFJT3zv/mkXlGdk7tZ3OJeQEAAABHHGQ7vWFMom6WCHr992NziSDRa0aQzP7tk1Nw/2f6sl5nh8PPmnVpJOAl2epuNLpRjDjSLW4wVD4R9AqrteFAoO0onR1xU9BLdGcUNbxk266Exks1NezgS4stLk945rwUGVwnjrlqmGAXAAAA3OXlVxuNXvipI8Guz7dpe7/rpKL8tMFfxT3JuSPTyW1XlO8T7AIaBLyy+ZToypikaeRa2jg14tS6RbfG47+LhnrP7jq0Xgi7pp3HIruWLXu1HD3n8MtjIAkAAADAL15+VQS8MorI1vq86H6zgJcopi+6a37bxu6XgKtp3FTbY2k9PurUusNq9US6TVirjslavpsK11tRv+vQWjky/lhk5yMHX57I8PobziYAAADAJw4yzV7fnxbmxb26qD/23SNzfF+fbjuakQa4VLDJTTUku7s95FjASw3UQxG1+nAIvGCgrqnBurRq4cWKqhRKmuNtPBIqKoNq2bLlFyohpzO8EovLE2RfAgAAAH50MDrkrWO/yxPsAhoj4GWD9+6Pjjq9DUe7NcYkdmc85IY6XmPhXUuXX64HB9fLEadHL53kjAIAAAAAoLVHAl7ZfIpglwVur59yPOAV1SoPC+aHQ/K6Mx5a33G+jtelgU3L1/FZKTbu8Mu8yhkFAAAAAEBrxzO8CHhZ4F5hYMTpbQgFa1EtWAsZP5+WvXzRpbFUUZ18fftdGq22WQk7HfBKLi5PxDirAAAAAABo7njAi4L1kq1sDQ0VylrUDdsS1SoDkVA1EQgolhTccrJ4/fnoti3rKdbURKmmOp3ORmAaAAAAAIAWHga8svmUKIadoEnk+uBBYtQt2yLqeEU1+fW7Dq0VnIsDWTk643H3SgNOZ3kR8AIAAAAAoIWjGV7UBrLAnc1h1wS8omp1OKzVxqxavihcX60FHHltZ8MF29a16fxojRSuBwAAAACghf8vwABfcA5F9k0oGQAAAABJRU5ErkJggg=="
+ alt="@Resources.WelcomePageImageText_Skyline" title="@Resources.WelcomePageImageText_Skyline" width="1212" height="202" /></div>
+ </div>
+ </div>
+
+ <div class="content">
+ <div class="bodyHeadline">@Resources.WelcomeHeader</div>
+ <div class="bodyContent">@Resources.WelcomeStarted</div>
+ <a class="bodyCTA longer" href="http://go.microsoft.com/fwlink/?LinkID=398596&amp;clcid=0x409">@Resources.WelcomeLearnMicrosoftAspNet<div>
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAAdCAYAAAD7En+mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMjVCMDEwM0FBQkExMUUyQjdGNEEwODg0RjhFODY4OCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMjVCMDEwNEFBQkExMUUyQjdGNEEwODg0RjhFODY4OCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyNUIwMTAxQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMyNUIwMTAyQUFCQTExRTJCN0Y0QTA4ODRGOEU4Njg4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+I1MZRAAAA4FJREFUeNq8md1x2kAQx3UaCoAKAg0YkgYQHTgV2KoAeMqjncfkBbsCSAd0AK5AOA1Y6YAOyJ7mf5rV6T4l8M3c2ALd/fZ/u/exh0gCy+VyyeiPrHPtqzPVd6p7IcTJ0rbxTO8FMe++/vIy/xY/TiF9CY+4If1ZUV1SHQb0V1L9Q/WFxJy7CCVxnZkk+hwtlIyTsCcGk53sMZJ8FGcY8Ux7NydB+xihJLI3k8Tug4TCi1uq92zEfpJxu4DwfoShY3y0o3a5Tyi82GKS0buA8G4xqV3uFAqRB4xYAoHPSURBH08IvwosR9omFCIbTDI0iok+GkxdrC5UjuqjHnpdCry7xaOcs2uLkQ2mLfQCBTeY1Ne6JRRzcoPH731EWsQuqM+jYU7WzD4iLWIX1GfFTLVwU+HaG4gQ3WExSRhcDzcVrldhYl63mAJCnwEtybiJx0tjNvETtCk9c/YDq2OuFjUSWjPJuInHSy0mtSk9c7ZmSvEpvntQIxswaGeE2wG1IDEzh1fl+694XLKvejFJzMzh1RZTwMhCdkZGjSJWVr5SnjEHT44o+MDjaPrt91gxyahRxMraYtpORoiCmpmyvesYMfcqCNvEKyNsnkVoq3ezLkx4qcW0eRahXTOl0C94eI9caGxiM0uTEzvVdGI6xHqZAzbJT4aQO8ADoUWJzQ0nqX/sfyuTjO7EpHa54SRVM1Ntwl+rbALf+zTmwDDKJtf7SqYZvwhs92nMATrOTFDbsY2F9gwrITdywVM0Vqbae0YmP7ZZVlMj05KiTXnoqjieRx7vFHAYIDJh28KxK5OJHAaIbDCFvsc5DO0sku3VMkqEvse5EuauIvF+gSgRqbbHrW7gSX4i2hn2uNUNPNliCi3LkA0nIV6NDPFCz2BYllExQ7waGeIFz2BSlmWUGLHtFUXy/o48TcOed3Umu62omI00DUVl5PdIwK+1t81UUm34vmYiAb8ZUzgS5eq+p4cnN7g5cCbyWqJsvO+J8GSDyXNc0+XYlr18RA5ZRt7/btjSnqsFwXE51mK68k3L/W+DqR8HRcAViFq5Xm1pGBP4wAyu751Crjs1z9ZM1wU1BLaYptsK4VktN9pRq0R9Y5/NMZL8slmC1ioSIu51ezNtkSACQ3HJckjXAX1v8nzsTxLwVBTT99OEiFxkMsNIVpu/J6yjhBpEG5mhv7vI8l+AAQB7WiwH/DuungAAAABJRU5ErkJggg=="
+ alt="@Resources.WelcomePageImageText_LearnMore" title="@Resources.WelcomePageImageText_LearnMore" width="58" height="29" /></div>
+ </a>
+ </div>
+
+</body>
+</html>
diff --git a/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageExtensions.cs b/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageExtensions.cs
new file mode 100644
index 0000000000..67b967c139
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageExtensions.cs
@@ -0,0 +1,90 @@
+// 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 Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// IApplicationBuilder extensions for the WelcomePageMiddleware.
+ /// </summary>
+ public static class WelcomePageExtensions
+ {
+ /// <summary>
+ /// Adds the WelcomePageMiddleware to the pipeline with the given options.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="options"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseWelcomePage(this IApplicationBuilder app, WelcomePageOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ return app.UseMiddleware<WelcomePageMiddleware>(Options.Create(options));
+ }
+
+ /// <summary>
+ /// Adds the WelcomePageMiddleware to the pipeline with the given path.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="path"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseWelcomePage(this IApplicationBuilder app, PathString path)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseWelcomePage(new WelcomePageOptions
+ {
+ Path = path
+ });
+ }
+
+ /// <summary>
+ /// Adds the WelcomePageMiddleware to the pipeline with the given path.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <param name="path"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseWelcomePage(this IApplicationBuilder app, string path)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseWelcomePage(new WelcomePageOptions
+ {
+ Path = new PathString(path)
+ });
+ }
+
+ /// <summary>
+ /// Adds the WelcomePageMiddleware to the pipeline.
+ /// </summary>
+ /// <param name="app"></param>
+ /// <returns></returns>
+ public static IApplicationBuilder UseWelcomePage(this IApplicationBuilder app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ return app.UseMiddleware<WelcomePageMiddleware>();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageMiddleware.cs b/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageMiddleware.cs
new file mode 100644
index 0000000000..ad201ddff1
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageMiddleware.cs
@@ -0,0 +1,60 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.RazorViews;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ /// <summary>
+ /// This middleware provides a default web page for new applications.
+ /// </summary>
+ public class WelcomePageMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly WelcomePageOptions _options;
+
+ /// <summary>
+ /// Creates a default web page for new applications.
+ /// </summary>
+ /// <param name="next"></param>
+ /// <param name="options"></param>
+ public WelcomePageMiddleware(RequestDelegate next, IOptions<WelcomePageOptions> options)
+ {
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ _next = next;
+ _options = options.Value;
+ }
+
+ /// <summary>
+ /// Process an individual request.
+ /// </summary>
+ /// <param name="context">The <see cref="HttpContext"/>.</param>
+ /// <returns></returns>
+ public Task Invoke(HttpContext context)
+ {
+ HttpRequest request = context.Request;
+ if (!_options.Path.HasValue || _options.Path == request.Path)
+ {
+ // Dynamically generated for LOC.
+ var welcomePage = new WelcomePage();
+ return welcomePage.ExecuteAsync(context);
+ }
+
+ return _next(context);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageOptions.cs b/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageOptions.cs
new file mode 100644
index 0000000000..a62cea951b
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/WelcomePage/WelcomePageOptions.cs
@@ -0,0 +1,19 @@
+// 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 Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// Options for the WelcomePageMiddleware.
+ /// </summary>
+ public class WelcomePageOptions
+ {
+ /// <summary>
+ /// Specifies which requests paths will be responded to. Exact matches only. Leave null to handle all requests.
+ /// </summary>
+ public PathString Path { get; set; }
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/_gruntfile.js b/src/Middleware/Diagnostics/src/_gruntfile.js
new file mode 100644
index 0000000000..75132d0cbc
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/_gruntfile.js
@@ -0,0 +1,32 @@
+
+module.exports = function (grunt) {
+
+ grunt.initConfig({
+ jshint: {
+ src: [
+ "**/*.js",
+ "!node_modules/**/*.js"
+ ],
+ options: {
+ // Options are documented at https://github.com/gruntjs/grunt-contrib-jshint#options
+ jshintrc: ".jshintrc"
+ }
+ },
+ csslint: {
+ src: [
+ "**/*.css",
+ "!node_modules/**/*.css"
+ ],
+ options: {
+ // Options are documented at https://github.com/gruntjs/grunt-contrib-csslint#options
+ csslintrc: ".csslintrc"
+ }
+ }
+ });
+
+ grunt.loadNpmTasks("grunt-contrib-jshint");
+ grunt.loadNpmTasks("grunt-contrib-csslint");
+
+
+ grunt.registerTask("default", ["jshint", "csslint"]);
+}; \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/_gruntfile.readme b/src/Middleware/Diagnostics/src/_gruntfile.readme
new file mode 100644
index 0000000000..ee51f091af
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/_gruntfile.readme
@@ -0,0 +1 @@
+If js or css files changes, rename _gruntfile.js to gruntfile.js, _package.json to package.json, and run build. \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/src/_package.json b/src/Middleware/Diagnostics/src/_package.json
new file mode 100644
index 0000000000..a89dd3241c
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/_package.json
@@ -0,0 +1,12 @@
+{
+ "name": "DiagnosticsPages",
+ "version": "0.0.0",
+ "description": "",
+ "private": true,
+ "devDependencies": {
+ "grunt": "~0.4.4",
+ "grunt-cli": "~0.1.13",
+ "grunt-contrib-csslint": "~0.2.0",
+ "grunt-contrib-jshint": "~0.10.0"
+ }
+}
diff --git a/src/Middleware/Diagnostics/src/baseline.netcore.json b/src/Middleware/Diagnostics/src/baseline.netcore.json
new file mode 100644
index 0000000000..eeea95de0b
--- /dev/null
+++ b/src/Middleware/Diagnostics/src/baseline.netcore.json
@@ -0,0 +1,1543 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.Diagnostics, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Prefix",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Value",
+ "Parameters": [],
+ "ReturnType": "System.Object",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Literal",
+ "Parameters": [],
+ "ReturnType": "System.Boolean",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "FromTuple",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Tuple<System.String, System.Object, System.Boolean>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Static": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "FromTuple",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Tuple<System.String, System.String, System.Boolean>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Static": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "op_Implicit",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Tuple<System.String, System.Object, System.Boolean>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue",
+ "Static": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "prefix",
+ "Type": "System.String"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ },
+ {
+ "Name": "literal",
+ "Type": "System.Boolean"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.BaseView",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Context",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.HttpContext",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Request",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.HttpRequest",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Response",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.HttpResponse",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Output",
+ "Parameters": [],
+ "ReturnType": "System.IO.StreamWriter",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_HtmlEncoder",
+ "Parameters": [],
+ "ReturnType": "System.Text.Encodings.Web.HtmlEncoder",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_HtmlEncoder",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Text.Encodings.Web.HtmlEncoder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_UrlEncoder",
+ "Parameters": [],
+ "ReturnType": "System.Text.Encodings.Web.UrlEncoder",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_UrlEncoder",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Text.Encodings.Web.UrlEncoder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_JavaScriptEncoder",
+ "Parameters": [],
+ "ReturnType": "System.Text.Encodings.Web.JavaScriptEncoder",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_JavaScriptEncoder",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Text.Encodings.Web.JavaScriptEncoder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "ExecuteAsync",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "ExecuteAsync",
+ "Parameters": [],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Virtual": true,
+ "Abstract": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteral",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteral",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteAttributeValue",
+ "Parameters": [
+ {
+ "Name": "thingy",
+ "Type": "System.String"
+ },
+ {
+ "Name": "startPostion",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ },
+ {
+ "Name": "endValue",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "dealyo",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "yesno",
+ "Type": "System.Boolean"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "BeginWriteAttribute",
+ "Parameters": [
+ {
+ "Name": "name",
+ "Type": "System.String"
+ },
+ {
+ "Name": "begining",
+ "Type": "System.String"
+ },
+ {
+ "Name": "startPosition",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "ending",
+ "Type": "System.String"
+ },
+ {
+ "Name": "endPosition",
+ "Type": "System.Int32"
+ },
+ {
+ "Name": "thingy",
+ "Type": "System.Int32"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "EndWriteAttribute",
+ "Parameters": [],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteAttributeTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "name",
+ "Type": "System.String"
+ },
+ {
+ "Name": "leader",
+ "Type": "System.String"
+ },
+ {
+ "Name": "trailer",
+ "Type": "System.String"
+ },
+ {
+ "Name": "values",
+ "Type": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue[]",
+ "IsParams": true
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Write",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Write",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Write",
+ "Parameters": [
+ {
+ "Name": "result",
+ "Type": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.HelperResult"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteralTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.Object"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteLiteralTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ },
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "HtmlEncodeAndReplaceLineBreaks",
+ "Parameters": [
+ {
+ "Name": "input",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.String",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Protected",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.HelperResult",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_WriteAction",
+ "Parameters": [],
+ "ReturnType": "System.Action<System.IO.TextWriter>",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "WriteTo",
+ "Parameters": [
+ {
+ "Name": "writer",
+ "Type": "System.IO.TextWriter"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "action",
+ "Type": "System.Action<System.IO.TextWriter>"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Invoke",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.DeveloperExceptionPageOptions>"
+ },
+ {
+ "Name": "loggerFactory",
+ "Type": "Microsoft.Extensions.Logging.ILoggerFactory"
+ },
+ {
+ "Name": "hostingEnvironment",
+ "Type": "Microsoft.AspNetCore.Hosting.IHostingEnvironment"
+ },
+ {
+ "Name": "diagnosticSource",
+ "Type": "System.Diagnostics.DiagnosticSource"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerFeature",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Diagnostics.IExceptionHandlerPathFeature"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Error",
+ "Parameters": [],
+ "ReturnType": "System.Exception",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_Error",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Exception"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Path",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IExceptionHandlerPathFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_Path",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Invoke",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ },
+ {
+ "Name": "loggerFactory",
+ "Type": "Microsoft.Extensions.Logging.ILoggerFactory"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.ExceptionHandlerOptions>"
+ },
+ {
+ "Name": "diagnosticSource",
+ "Type": "System.Diagnostics.DiagnosticSource"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.StatusCodeContext",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_HttpContext",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.HttpContext",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Options",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Builder.StatusCodePagesOptions",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Next",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.RequestDelegate",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.AspNetCore.Builder.StatusCodePagesOptions"
+ },
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.StatusCodePagesFeature",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Diagnostics.IStatusCodePagesFeature"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Enabled",
+ "Parameters": [],
+ "ReturnType": "System.Boolean",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodePagesFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_Enabled",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Boolean"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodePagesFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Invoke",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.StatusCodePagesOptions>"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.StatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_OriginalPath",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_OriginalPath",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_OriginalPathBase",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_OriginalPathBase",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_OriginalQueryString",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_OriginalQueryString",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Diagnostics.IStatusCodeReExecuteFeature",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.WelcomePageMiddleware",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Invoke",
+ "Parameters": [
+ {
+ "Name": "context",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Builder.WelcomePageOptions>"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Diagnostics.Views.WelcomePage",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "BaseType": "Microsoft.AspNetCore.DiagnosticsViewPage.Views.BaseView",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "ExecuteAsync",
+ "Parameters": [],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Virtual": true,
+ "Override": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.DeveloperExceptionPageExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "UseDeveloperExceptionPage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseDeveloperExceptionPage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.AspNetCore.Builder.DeveloperExceptionPageOptions"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.DeveloperExceptionPageOptions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_SourceCodeLineCount",
+ "Parameters": [],
+ "ReturnType": "System.Int32",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_SourceCodeLineCount",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Int32"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_FileProvider",
+ "Parameters": [],
+ "ReturnType": "Microsoft.Extensions.FileProviders.IFileProvider",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_FileProvider",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "Microsoft.Extensions.FileProviders.IFileProvider"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.ExceptionHandlerExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "UseExceptionHandler",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseExceptionHandler",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "errorHandlingPath",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseExceptionHandler",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "configure",
+ "Type": "System.Action<Microsoft.AspNetCore.Builder.IApplicationBuilder>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseExceptionHandler",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.AspNetCore.Builder.ExceptionHandlerOptions"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.ExceptionHandlerOptions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_ExceptionHandlingPath",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.PathString",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_ExceptionHandlingPath",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "Microsoft.AspNetCore.Http.PathString"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_ExceptionHandler",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.RequestDelegate",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_ExceptionHandler",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.StatusCodePagesExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "UseStatusCodePages",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.AspNetCore.Builder.StatusCodePagesOptions"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseStatusCodePages",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseStatusCodePages",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "handler",
+ "Type": "System.Func<Microsoft.AspNetCore.Diagnostics.StatusCodeContext, System.Threading.Tasks.Task>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseStatusCodePages",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "contentType",
+ "Type": "System.String"
+ },
+ {
+ "Name": "bodyFormat",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseStatusCodePagesWithRedirects",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "locationFormat",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseStatusCodePages",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "configuration",
+ "Type": "System.Action<Microsoft.AspNetCore.Builder.IApplicationBuilder>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseStatusCodePagesWithReExecute",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "pathFormat",
+ "Type": "System.String"
+ },
+ {
+ "Name": "queryFormat",
+ "Type": "System.String",
+ "DefaultValue": "null"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.StatusCodePagesOptions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_HandleAsync",
+ "Parameters": [],
+ "ReturnType": "System.Func<Microsoft.AspNetCore.Diagnostics.StatusCodeContext, System.Threading.Tasks.Task>",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_HandleAsync",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Func<Microsoft.AspNetCore.Diagnostics.StatusCodeContext, System.Threading.Tasks.Task>"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.WelcomePageExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "UseWelcomePage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "options",
+ "Type": "Microsoft.AspNetCore.Builder.WelcomePageOptions"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseWelcomePage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "path",
+ "Type": "Microsoft.AspNetCore.Http.PathString"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseWelcomePage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ },
+ {
+ "Name": "path",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "UseWelcomePage",
+ "Parameters": [
+ {
+ "Name": "app",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.Builder.WelcomePageOptions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Path",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.PathString",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_Path",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "Microsoft.AspNetCore.Http.PathString"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.cs
new file mode 100644
index 0000000000..acb68868b1
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/DatabaseErrorPageSampleTest.cs
@@ -0,0 +1,38 @@
+// 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Testing.xunit;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
+{
+ public class DatabaseErrorPageSampleTest : IClassFixture<TestFixture<DatabaseErrorPageSample.Startup>>
+ {
+ public DatabaseErrorPageSampleTest(TestFixture<DatabaseErrorPageSample.Startup> fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux)]
+ [OSSkipCondition(OperatingSystems.MacOSX)]
+ public async Task DatabaseErrorPage_ShowsError()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/");
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ Assert.Contains("In Visual Studio, use the Package Manager Console to scaffold a new migration and apply it to the database:", body);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs
new file mode 100644
index 0000000000..2ff8511325
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs
@@ -0,0 +1,36 @@
+// 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
+{
+ public class DeveloperExceptionPageSampleTest : IClassFixture<TestFixture<DeveloperExceptionPageSample.Startup>>
+ {
+ public DeveloperExceptionPageSampleTest(TestFixture<DeveloperExceptionPageSample.Startup> fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+
+ [Fact]
+ public async Task DeveloperExceptionPage_ShowsError()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/");
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ Assert.Contains("Exception: Demonstration exception.", body);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/Diagnostics.FunctionalTests.csproj b/src/Middleware/Diagnostics/test/FunctionalTests/Diagnostics.FunctionalTests.csproj
new file mode 100644
index 0000000000..f581883ab8
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/Diagnostics.FunctionalTests.csproj
@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
+ <SignAssembly>false</SignAssembly>
+ <AssemblyName>Diagnostics.FunctionalTests</AssemblyName>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\testassets\DatabaseErrorPageSample\DatabaseErrorPageSample.csproj" />
+ <ProjectReference Include="..\testassets\DeveloperExceptionPageSample\DeveloperExceptionPageSample.csproj" />
+ <ProjectReference Include="..\testassets\ExceptionHandlerSample\ExceptionHandlerSample.csproj" />
+ <ProjectReference Include="..\testassets\StatusCodePagesSample\StatusCodePagesSample.csproj" />
+ <ProjectReference Include="..\testassets\WelcomePageSample\WelcomePageSample.csproj" />
+ <ProjectReference Include="..\..\..\MiddlewareAnalysis\samples\MiddlewareAnalysisSample\MiddlewareAnalysisSample.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ <Reference Include="Microsoft.AspNetCore.TestHost" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/ExceptionHandlerSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/ExceptionHandlerSampleTest.cs
new file mode 100644
index 0000000000..44057bd7ca
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/ExceptionHandlerSampleTest.cs
@@ -0,0 +1,35 @@
+// 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
+{
+ public class ExceptionHandlerSampleTest : IClassFixture<TestFixture<ExceptionHandlerSample.Startup>>
+ {
+ public ExceptionHandlerSampleTest(TestFixture<ExceptionHandlerSample.Startup> fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [Fact]
+ public async Task ExceptionHandlerPage_ShowsError()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/throw");
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ Assert.Contains("we encountered an un-expected issue with your application.", body);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/MiddlewareAnalysisSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/MiddlewareAnalysisSampleTest.cs
new file mode 100644
index 0000000000..4692b62b57
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/MiddlewareAnalysisSampleTest.cs
@@ -0,0 +1,34 @@
+// 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
+{
+ public class MiddlewareAnalysisSampleTest : IClassFixture<TestFixture<MiddlewareAnaysisSample.Startup>>
+ {
+ public MiddlewareAnalysisSampleTest(TestFixture<MiddlewareAnaysisSample.Startup> fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [Fact]
+ public async Task MiddlewareAnalysisPage_ShowsAnalysis()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/");
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/StatusCodeSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/StatusCodeSampleTest.cs
new file mode 100644
index 0000000000..a990af53bc
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/StatusCodeSampleTest.cs
@@ -0,0 +1,76 @@
+// 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.WebUtilities;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
+{
+ public class StatusCodeSampleTest : IClassFixture<TestFixture<StatusCodePagesSample.Startup>>
+ {
+ public StatusCodeSampleTest(TestFixture<StatusCodePagesSample.Startup> fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [Fact]
+ public async Task StatusCodePage_ShowsError()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/errors/417");
+
+ // Act
+ var response = await Client.SendAsync(request);
+
+ // Assert
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Contains("Status Code: 417", body);
+ }
+
+ [Fact]
+ public async Task StatusCodePageOptions_ExcludesSemicolon_WhenReasonPhrase_IsUnknown()
+ {
+ //Arrange
+ var httpStatusCode = 541;
+ var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost/?statuscode={httpStatusCode}");
+
+ //Act
+ var response = await Client.SendAsync(request);
+
+ var statusCode = response.StatusCode;
+
+ var responseBody = await response.Content.ReadAsStringAsync();
+
+ //Assert
+ Assert.Equal((HttpStatusCode)httpStatusCode, response.StatusCode);
+ Assert.DoesNotContain(";", responseBody);
+ }
+
+ [Fact]
+ public async Task StatusCodePageOptions_IncludesSemicolon__AndReasonPhrase_WhenReasonPhrase_IsKnown()
+ {
+ //Arrange
+ var httpStatusCode = 400;
+ var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost/?statuscode={httpStatusCode}");
+
+ //Act
+ var response = await Client.SendAsync(request);
+
+ var statusCode = response.StatusCode;
+ var statusCodeReasonPhrase = ReasonPhrases.GetReasonPhrase(httpStatusCode);
+
+ var responseBody = await response.Content.ReadAsStringAsync();
+
+ //Assert
+ Assert.Equal((HttpStatusCode)httpStatusCode, response.StatusCode);
+ Assert.Contains(";", responseBody);
+ Assert.Contains(statusCodeReasonPhrase, responseBody);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/TestFixture.cs b/src/Middleware/Diagnostics/test/FunctionalTests/TestFixture.cs
new file mode 100644
index 0000000000..ef4666ad31
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/TestFixture.cs
@@ -0,0 +1,41 @@
+// 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.Net.Http;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.AspNetCore.Testing;
+
+namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
+{
+ public class TestFixture<TStartup> : IDisposable
+ {
+ private readonly TestServer _server;
+
+ public TestFixture()
+ {
+ // RequestLocalizationOptions saves the current culture when constructed, potentially changing response
+ // localization i.e. RequestLocalizationMiddleware behavior. Ensure the saved culture
+ // (DefaultRequestCulture) is consistent regardless of system configuration or personal preferences.
+ using (new CultureReplacer())
+ {
+ var builder = new WebHostBuilder()
+ .UseStartup(typeof(TStartup));
+
+ _server = new TestServer(builder);
+ }
+
+ Client = _server.CreateClient();
+ Client.BaseAddress = new Uri("http://localhost");
+ }
+
+ public HttpClient Client { get; }
+
+ public void Dispose()
+ {
+ Client.Dispose();
+ _server.Dispose();
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/WelcomePageSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/WelcomePageSampleTest.cs
new file mode 100644
index 0000000000..e20fa6fdf4
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/FunctionalTests/WelcomePageSampleTest.cs
@@ -0,0 +1,36 @@
+// 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.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests
+{
+ public class WelcomePageSampleTest : IClassFixture<TestFixture<WelcomePageSample.Startup>>
+ {
+ public WelcomePageSampleTest(TestFixture<WelcomePageSample.Startup> fixture)
+ {
+ Client = fixture.Client;
+ }
+
+ public HttpClient Client { get; }
+
+ [Fact]
+ public async Task WelcomePage_ShowsWelcome()
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/");
+
+ var response = await Client.SendAsync(request);
+
+ var bytes = await response.Content.ReadAsByteArrayAsync();
+ Assert.True(bytes.Length > 1);
+ Assert.NotEqual(0xEF, bytes[0]); // No leading UTF-8 BOM
+
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Contains("Your ASP.NET Core application has been successfully started", body);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs b/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs
new file mode 100644
index 0000000000..2d0f6b42db
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/UnitTests/DeveloperExceptionPageMiddlewareTest.cs
@@ -0,0 +1,144 @@
+// 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.Linq;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class DeveloperExceptionPageMiddlewareTest
+ {
+ [Fact]
+ public async Task UnhandledErrorsWriteToDiagnosticWhenUsingExceptionPage()
+ {
+ // Arrange
+ DiagnosticListener diagnosticListener = null;
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ diagnosticListener = app.ApplicationServices.GetRequiredService<DiagnosticListener>();
+ app.UseDeveloperExceptionPage();
+ app.Run(context =>
+ {
+ throw new Exception("Test exception");
+ });
+ });
+ var server = new TestServer(builder);
+ var listener = new TestDiagnosticListener();
+ diagnosticListener.SubscribeWithAdapter(listener);
+
+ // Act
+ await server.CreateClient().GetAsync("/path");
+
+ // Assert
+ Assert.NotNull(listener.DiagnosticUnhandledException?.HttpContext);
+ Assert.NotNull(listener.DiagnosticUnhandledException?.Exception);
+ Assert.Null(listener.DiagnosticHandledException?.HttpContext);
+ Assert.Null(listener.DiagnosticHandledException?.Exception);
+ }
+
+ public static TheoryData CompilationExceptionData
+ {
+ get
+ {
+ var variations = new TheoryData<List<CompilationFailure>>();
+ var failures = new List<CompilationFailure>();
+ var diagnosticMessages = new List<DiagnosticMessage>();
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(@"c:\sourcefilepath.cs", "source file content", "compiled content", diagnosticMessages)
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(null, "source file content", "compiled content", diagnosticMessages)
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(@"c:\sourcefilepath.cs", null, "compiled content", diagnosticMessages)
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(@"c:\sourcefilepath.cs", "source file content", null, diagnosticMessages)
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(null, null, null, diagnosticMessages)
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(@"c:\sourcefilepath.cs", "source file content", "compiled content", diagnosticMessages),
+ new CompilationFailure(@"c:\sourcefilepath.cs", null, "compiled content", diagnosticMessages)
+ });
+ variations.Add(null);
+ variations.Add(new List<CompilationFailure>()
+ {
+ null
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(@"c:\sourcefilepath.cs", "source file content", "compiled content", diagnosticMessages),
+ null
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(@"c:\sourcefilepath.cs", "source file content", "compiled content", null)
+ });
+ variations.Add(new List<CompilationFailure>()
+ {
+ new CompilationFailure(@"c:\sourcefilepath.cs", "source file content", "compiled content", new List<DiagnosticMessage>(){ null })
+ });
+ return variations;
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(CompilationExceptionData))]
+ public async Task NullInfoInCompilationException_ShouldNotThrowExceptionGeneratingExceptionPage(
+ List<CompilationFailure> failures)
+ {
+ // Arrange
+ DiagnosticListener diagnosticListener = null;
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ diagnosticListener = app.ApplicationServices.GetRequiredService<DiagnosticListener>();
+ app.UseDeveloperExceptionPage();
+ app.Run(context =>
+ {
+ throw new CustomCompilationException(failures);
+ });
+ });
+ var server = new TestServer(builder);
+ var listener = new TestDiagnosticListener();
+ diagnosticListener.SubscribeWithAdapter(listener);
+
+ // Act
+ await server.CreateClient().GetAsync("/path");
+
+ // Assert
+ Assert.NotNull(listener.DiagnosticUnhandledException?.HttpContext);
+ Assert.NotNull(listener.DiagnosticUnhandledException?.Exception);
+ Assert.Null(listener.DiagnosticHandledException?.HttpContext);
+ Assert.Null(listener.DiagnosticHandledException?.Exception);
+ }
+
+ public class CustomCompilationException : Exception, ICompilationException
+ {
+ public CustomCompilationException(IEnumerable<CompilationFailure> compilationFailures)
+ {
+ CompilationFailures = compilationFailures;
+ }
+
+ public IEnumerable<CompilationFailure> CompilationFailures { get; }
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/UnitTests/ExceptionDetailsProviderTest.cs b/src/Middleware/Diagnostics/test/UnitTests/ExceptionDetailsProviderTest.cs
new file mode 100644
index 0000000000..bda7bde74d
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/UnitTests/ExceptionDetailsProviderTest.cs
@@ -0,0 +1,392 @@
+// 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.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.AspNetCore.Testing;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Extensions.StackTrace.Sources;
+using Xunit;
+
+namespace Microsoft.Extensions.Internal
+{
+ public class ExceptionDetailsProviderTest
+ {
+ public static TheoryData RelativePathsData
+ {
+ get
+ {
+ var data = new TheoryData<string>
+ {
+ "TestFiles/SourceFile.txt"
+ };
+
+ if (!(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)))
+ {
+ data.Add(@"TestFiles\SourceFile.txt");
+ }
+
+ return data;
+ }
+ }
+
+ public static TheoryData<string> AbsolutePathsData
+ {
+ get
+ {
+ var rootPath = Directory.GetCurrentDirectory();
+
+ var data = new TheoryData<string>()
+ {
+ Path.Combine(rootPath, "TestFiles/SourceFile.txt")
+ };
+
+ if (!TestPlatformHelper.IsMono)
+ {
+ Path.Combine(rootPath, @"TestFiles\SourceFile.txt");
+ }
+
+ return data;
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(AbsolutePathsData))]
+ public void DisplaysSourceCodeLines_ForAbsolutePaths(string absoluteFilePath)
+ {
+ // Arrange
+ var rootPath = Directory.GetCurrentDirectory();
+ // PhysicalFileProvider handles only relative paths but we fall back to work with absolute paths too
+ using (var provider = new PhysicalFileProvider(rootPath))
+ {
+ // Act
+ var exceptionDetailProvider = new ExceptionDetailsProvider(provider, sourceCodeLineCount: 6);
+ var stackFrame = exceptionDetailProvider.GetStackFrameSourceCodeInfo(
+ "func1",
+ absoluteFilePath,
+ lineNumber: 10);
+
+ // Assert
+ // Lines 4-16 (inclusive) is the code block
+ Assert.Equal(4, stackFrame.PreContextLine);
+ Assert.Equal(GetCodeLines(4, 9), stackFrame.PreContextCode);
+ Assert.Equal(GetCodeLines(10, 10), stackFrame.ContextCode);
+ Assert.Equal(GetCodeLines(11, 16), stackFrame.PostContextCode);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(RelativePathsData))]
+ public void DisplaysSourceCodeLines_ForRelativePaths(string relativePath)
+ {
+ // Arrange
+ var rootPath = Directory.GetCurrentDirectory();
+ using (var provider = new PhysicalFileProvider(rootPath))
+ {
+ // Act
+ var exceptionDetailProvider = new ExceptionDetailsProvider(provider, sourceCodeLineCount: 6);
+ var stackFrame = exceptionDetailProvider.GetStackFrameSourceCodeInfo(
+ "func1",
+ relativePath,
+ lineNumber: 10);
+
+ // Assert
+ // Lines 4-16 (inclusive) is the code block
+ Assert.Equal(4, stackFrame.PreContextLine);
+ Assert.Equal(GetCodeLines(4, 9), stackFrame.PreContextCode);
+ Assert.Equal(GetCodeLines(10, 10), stackFrame.ContextCode);
+ Assert.Equal(GetCodeLines(11, 16), stackFrame.PostContextCode);
+ }
+ }
+
+ [Theory]
+ [InlineData("TestFiles/EmbeddedSourceFile.txt")]
+ //[InlineData(@"TestFiles\EmbeddedSourceFile.txt")]
+ public void DisplaysSourceCodeLines_ForRelativeEmbeddedPaths(string relativePath)
+ {
+ // Arrange
+ var provider = new EmbeddedFileProvider(
+ GetType().GetTypeInfo().Assembly,
+ baseNamespace: $"{typeof(ExceptionDetailsProviderTest).GetTypeInfo().Assembly.GetName().Name}.Resources");
+
+ // Act
+ var exceptionDetailProvider = new ExceptionDetailsProvider(provider, sourceCodeLineCount: 6);
+ var stackFrame = exceptionDetailProvider.GetStackFrameSourceCodeInfo(
+ "func1",
+ relativePath,
+ lineNumber: 10);
+
+ // Assert
+ // Lines 4-16 (inclusive) is the code block
+ Assert.Equal(4, stackFrame.PreContextLine);
+ Assert.Equal(GetCodeLines(4, 9), stackFrame.PreContextCode);
+ Assert.Equal(GetCodeLines(10, 10), stackFrame.ContextCode);
+ Assert.Equal(GetCodeLines(11, 16), stackFrame.PostContextCode);
+ }
+
+ public static TheoryData<ErrorData> DisplaysSourceCodeLines_PreAndPostErrorLineData
+ {
+ get
+ {
+ return new TheoryData<ErrorData>()
+ {
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 30),
+ ErrorStartLine = 10,
+ ErrorEndLine = 10,
+ ExpectedPreContextLine = 4,
+ ExpectedPreErrorCode = GetCodeLines(4, 9),
+ ExpectedErrorCode = GetCodeLines(10, 10),
+ ExpectedPostErrorCode = GetCodeLines(11, 16)
+ },
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 30),
+ ErrorStartLine = 10,
+ ErrorEndLine = 13, // multi-line error
+ ExpectedPreContextLine = 4,
+ ExpectedPreErrorCode = GetCodeLines(4, 9),
+ ExpectedErrorCode = GetCodeLines(10, 13),
+ ExpectedPostErrorCode = GetCodeLines(14, 19)
+ },
+
+ // PreErrorCode less than source code line count
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 10),
+ ErrorStartLine = 1,
+ ErrorEndLine = 1,
+ ExpectedPreContextLine = 1,
+ ExpectedPreErrorCode = Enumerable.Empty<string>(),
+ ExpectedErrorCode = GetCodeLines(1, 1),
+ ExpectedPostErrorCode = GetCodeLines(2, 7)
+ },
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 10),
+ ErrorStartLine = 3,
+ ErrorEndLine = 5,
+ ExpectedPreContextLine = 1,
+ ExpectedPreErrorCode = GetCodeLines(1, 2),
+ ExpectedErrorCode = GetCodeLines(3, 5),
+ ExpectedPostErrorCode = GetCodeLines(6, 10)
+ },
+
+ // PostErrorCode less than source code line count
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 10),
+ ErrorStartLine = 10,
+ ErrorEndLine = 10,
+ ExpectedPreContextLine = 4,
+ ExpectedPreErrorCode = GetCodeLines(4, 9),
+ ExpectedErrorCode = GetCodeLines(10, 10),
+ ExpectedPostErrorCode = Enumerable.Empty<string>()
+ },
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 10),
+ ErrorStartLine = 7,
+ ErrorEndLine = 10,
+ ExpectedPreContextLine = 1,
+ ExpectedPreErrorCode = GetCodeLines(1, 6),
+ ExpectedErrorCode = GetCodeLines(7, 10),
+ ExpectedPostErrorCode = Enumerable.Empty<string>()
+ },
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 10),
+ ErrorStartLine = 5,
+ ErrorEndLine = 8,
+ ExpectedPreContextLine = 1,
+ ExpectedPreErrorCode = GetCodeLines(1, 4),
+ ExpectedErrorCode = GetCodeLines(5, 8),
+ ExpectedPostErrorCode = GetCodeLines(9, 10)
+ },
+
+ // Pre and Post error code less than source code line count
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 4),
+ ErrorStartLine = 2,
+ ErrorEndLine = 3,
+ ExpectedPreContextLine = 1,
+ ExpectedPreErrorCode = GetCodeLines(1, 1),
+ ExpectedErrorCode = GetCodeLines(2, 3),
+ ExpectedPostErrorCode = GetCodeLines(4, 4)
+ },
+ new ErrorData()
+ {
+ AllLines = GetCodeLines(1, 4),
+ ErrorStartLine = 1,
+ ErrorEndLine = 4,
+ ExpectedPreContextLine = 1,
+ ExpectedPreErrorCode = Enumerable.Empty<string>(),
+ ExpectedErrorCode = GetCodeLines(1, 4),
+ ExpectedPostErrorCode = Enumerable.Empty<string>()
+ },
+
+ // change source code line count
+ new ErrorData()
+ {
+ SourceCodeLineCount = 1,
+ AllLines = GetCodeLines(1, 1),
+ ErrorStartLine = 1,
+ ErrorEndLine = 1,
+ ExpectedPreContextLine = 1,
+ ExpectedPreErrorCode = Enumerable.Empty<string>(),
+ ExpectedErrorCode = GetCodeLines(1, 1),
+ ExpectedPostErrorCode = Enumerable.Empty<string>()
+ },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(DisplaysSourceCodeLines_PreAndPostErrorLineData))]
+ public void DisplaysSourceCodeLines_PreAndPostErrorLine(ErrorData errorData)
+ {
+ // Arrange
+ var stackFrame = new StackFrameSourceCodeInfo();
+
+ // Act
+ var exceptionDetailProvider = new ExceptionDetailsProvider(
+ new PhysicalFileProvider(Directory.GetCurrentDirectory()),
+ sourceCodeLineCount: 6);
+
+ exceptionDetailProvider.ReadFrameContent(
+ stackFrame,
+ errorData.AllLines,
+ errorData.ErrorStartLine,
+ errorData.ErrorEndLine);
+
+ // Assert
+ Assert.Equal(errorData.ExpectedPreContextLine, stackFrame.PreContextLine);
+ Assert.Equal(errorData.ExpectedPreErrorCode, stackFrame.PreContextCode);
+ Assert.Equal(errorData.ExpectedErrorCode, stackFrame.ContextCode);
+ Assert.Equal(errorData.ExpectedPostErrorCode, stackFrame.PostContextCode);
+ }
+
+ private static IEnumerable<string> GetCodeLines(int fromLine, int toLine)
+ {
+ var start = fromLine;
+ var count = toLine - fromLine + 1;
+ return Enumerable.Range(start, count).Select(i => string.Format("Line{0}", i));
+ }
+
+ private class TestFileProvider : IFileProvider
+ {
+ private readonly IEnumerable<string> _sourceCodeLines;
+
+ public TestFileProvider(IEnumerable<string> sourceCodeLines)
+ {
+ _sourceCodeLines = sourceCodeLines;
+ }
+
+ public IDirectoryContents GetDirectoryContents(string subpath)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IFileInfo GetFileInfo(string subpath)
+ {
+ return new TestFileInfo(_sourceCodeLines);
+ }
+
+ public IChangeToken Watch(string filter)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ private class TestFileInfo : IFileInfo
+ {
+ private readonly MemoryStream _stream;
+
+ public TestFileInfo(IEnumerable<string> sourceCodeLines)
+ {
+ _stream = new MemoryStream();
+ using (var writer = new StreamWriter(_stream, Encoding.UTF8, 1024, leaveOpen: true))
+ {
+ foreach (var line in sourceCodeLines)
+ {
+ writer.WriteLine(line);
+ }
+ }
+ _stream.Seek(0, SeekOrigin.Begin);
+ }
+
+ public bool Exists
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public bool IsDirectory
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public DateTimeOffset LastModified
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public long Length
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public string Name
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public string PhysicalPath
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ public Stream CreateReadStream()
+ {
+ return _stream;
+ }
+ }
+
+ public class ErrorData
+ {
+ public int SourceCodeLineCount { get; set; } = 6;
+ public IEnumerable<string> AllLines { get; set; }
+ public int ErrorStartLine { get; set; }
+ public int ErrorEndLine { get; set; }
+ public int ExpectedPreContextLine { get; set; }
+ public IEnumerable<string> ExpectedPreErrorCode { get; set; }
+ public IEnumerable<string> ExpectedErrorCode { get; set; }
+ public IEnumerable<string> ExpectedPostErrorCode { get; set; }
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs
new file mode 100644
index 0000000000..0ee748add7
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs
@@ -0,0 +1,520 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class ExceptionHandlerTest
+ {
+ [Theory]
+ [InlineData(HttpStatusCode.NotFound)]
+ [InlineData(HttpStatusCode.BadRequest)]
+ [InlineData(HttpStatusCode.InternalServerError)]
+ public async Task OnlyHandles_UnhandledExceptions(HttpStatusCode expectedStatusCode)
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseExceptionHandler("/handle-errors");
+
+ app.Map("/handle-errors", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ await httpContext.Response.WriteAsync("Handled error in a custom way.");
+ });
+ });
+
+ app.Run((RequestDelegate)(async (context) =>
+ {
+ context.Response.StatusCode = (int)expectedStatusCode;
+ context.Response.ContentType = "text/plain; charset=utf-8";
+ await context.Response.WriteAsync("An error occurred while adding a product");
+ }));
+ });
+
+ using (var server = new TestServer(builder))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ Assert.Equal(expectedStatusCode, response.StatusCode);
+ Assert.Equal("An error occurred while adding a product", await response.Content.ReadAsStringAsync());
+ }
+ }
+
+ [Fact]
+ public async Task DoesNotHandle_UnhandledExceptions_WhenResponseAlreadyStarted()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(async (httpContext, next) =>
+ {
+ Exception exception = null;
+ try
+ {
+ await next();
+ }
+ catch (InvalidOperationException ex)
+ {
+ exception = ex;
+ }
+
+ Assert.NotNull(exception);
+ Assert.Equal("Something bad happened", exception.Message);
+ });
+
+ app.UseExceptionHandler("/handle-errors");
+
+ app.Map("/handle-errors", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ await httpContext.Response.WriteAsync("Handled error in a custom way.");
+ });
+ });
+
+ app.Run(async (httpContext) =>
+ {
+ await httpContext.Response.WriteAsync("Hello");
+ throw new InvalidOperationException("Something bad happened");
+ });
+ });
+
+ using (var server = new TestServer(builder))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ Assert.Equal("Hello", await response.Content.ReadAsStringAsync());
+ }
+ }
+
+ [Fact]
+ public async Task ClearsResponseBuffer_BeforeRequestIsReexecuted()
+ {
+ var expectedResponseBody = "New response body";
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ // add response buffering
+ app.Use(async (httpContext, next) =>
+ {
+ var response = httpContext.Response;
+ var originalResponseBody = response.Body;
+ var bufferingStream = new MemoryStream();
+ response.Body = bufferingStream;
+
+ try
+ {
+ await next();
+
+ response.Body = originalResponseBody;
+ bufferingStream.Seek(0, SeekOrigin.Begin);
+ await bufferingStream.CopyToAsync(response.Body);
+ }
+ finally
+ {
+ response.Body = originalResponseBody;
+ }
+ });
+
+ app.UseExceptionHandler("/handle-errors");
+
+ app.Map("/handle-errors", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ Assert.True(httpContext.Response.Body.CanSeek);
+ Assert.Equal(0, httpContext.Response.Body.Position);
+
+ await httpContext.Response.WriteAsync(expectedResponseBody);
+ });
+ });
+
+ app.Run(async (context) =>
+ {
+ // Write some content into the response before throwing exception
+ await context.Response.WriteAsync(new string('a', 100));
+
+ throw new InvalidOperationException("Invalid input provided.");
+ });
+ });
+
+ using (var server = new TestServer(builder))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ Assert.Equal(expectedResponseBody, await response.Content.ReadAsStringAsync());
+ IEnumerable<string> values;
+ Assert.True(response.Headers.TryGetValues("Cache-Control", out values));
+ Assert.Single(values);
+ Assert.Equal("no-cache", values.First());
+ Assert.True(response.Headers.TryGetValues("Pragma", out values));
+ Assert.Single(values);
+ Assert.Equal("no-cache", values.First());
+ Assert.True(response.Content.Headers.TryGetValues("Expires", out values));
+ Assert.Single(values);
+ Assert.Equal("-1", values.First());
+ Assert.False(response.Headers.TryGetValues("ETag", out values));
+ }
+ }
+
+ [Fact]
+ public async Task Redirect_StatusPage()
+ {
+ var expectedStatusCode = 432;
+ var destination = "/location";
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseStatusCodePagesWithRedirects("/errorPage?id={0}");
+
+ app.Map(destination, (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run((httpContext) =>
+ {
+ httpContext.Response.StatusCode = expectedStatusCode;
+ return Task.FromResult(1);
+ });
+ });
+
+ app.Map("/errorPage", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ await httpContext.Response.WriteAsync(httpContext.Request.QueryString.Value);
+ });
+ });
+
+ app.Run((context) =>
+ {
+
+ throw new InvalidOperationException($"Invalid input provided. {context.Request.Path}");
+ });
+ });
+ var expectedQueryString = $"?id={expectedStatusCode}";
+ var expectedUri = $"/errorPage{expectedQueryString}";
+ using (var server = new TestServer(builder))
+ {
+
+ var client = server.CreateClient();
+ var response = await client.GetAsync(destination);
+ Assert.Equal(HttpStatusCode.Found, response.StatusCode);
+ Assert.Equal(expectedUri, response.Headers.First(s => s.Key == "Location").Value.First());
+
+ response = await client.GetAsync(expectedUri);
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Equal(expectedQueryString, content);
+ Assert.Equal(expectedQueryString, response.RequestMessage.RequestUri.Query);
+ }
+ }
+
+ [Fact]
+ public async Task Reexecute_CanRetrieveInformationAboutOriginalRequest()
+ {
+ var expectedStatusCode = 432;
+ var destination = "/location";
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(async (context, next) =>
+ {
+ var beforeNext = context.Request.QueryString;
+ await next();
+ var afterNext = context.Request.QueryString;
+
+ Assert.Equal(beforeNext, afterNext);
+ });
+ app.UseStatusCodePagesWithReExecute(pathFormat: "/errorPage", queryFormat: "?id={0}");
+
+ app.Map(destination, (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run((httpContext) =>
+ {
+ httpContext.Response.StatusCode = expectedStatusCode;
+ return Task.FromResult(1);
+ });
+ });
+
+ app.Map("/errorPage", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ var statusCodeReExecuteFeature = httpContext.Features.Get<IStatusCodeReExecuteFeature>();
+ await httpContext.Response.WriteAsync(
+ httpContext.Request.QueryString.Value
+ + ", "
+ + statusCodeReExecuteFeature.OriginalPath
+ + ", "
+ + statusCodeReExecuteFeature.OriginalQueryString);
+ });
+ });
+
+ app.Run((context) =>
+ {
+ throw new InvalidOperationException("Invalid input provided.");
+ });
+ });
+
+ using (var server = new TestServer(builder))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(destination + "?name=James");
+ var content = await response.Content.ReadAsStringAsync();
+ Assert.Equal($"?id={expectedStatusCode}, /location, ?name=James", content);
+ }
+ }
+
+ [Fact]
+ public async Task ClearsCacheHeaders_SetByReexecutionPathHandlers()
+ {
+ var expiresTime = DateTime.UtcNow.AddDays(5).ToString("R");
+ var expectedResponseBody = "Handled error in a custom way.";
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseExceptionHandler("/handle-errors");
+
+ app.Map("/handle-errors", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ httpContext.Response.Headers.Add("Cache-Control", new[] { "max-age=600" });
+ httpContext.Response.Headers.Add("Pragma", new[] { "max-age=600" });
+ httpContext.Response.Headers.Add(
+ "Expires", new[] { expiresTime });
+ httpContext.Response.Headers.Add("ETag", new[] { "12345" });
+
+ await httpContext.Response.WriteAsync(expectedResponseBody);
+ });
+ });
+
+ app.Run((context) =>
+ {
+ throw new InvalidOperationException("Invalid input provided.");
+ });
+ });
+
+ using (var server = new TestServer(builder))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
+ Assert.Equal(expectedResponseBody, await response.Content.ReadAsStringAsync());
+ IEnumerable<string> values;
+ Assert.True(response.Headers.TryGetValues("Cache-Control", out values));
+ Assert.Single(values);
+ Assert.Equal("no-cache", values.First());
+ Assert.True(response.Headers.TryGetValues("Pragma", out values));
+ Assert.Single(values);
+ Assert.Equal("no-cache", values.First());
+ Assert.True(response.Content.Headers.TryGetValues("Expires", out values));
+ Assert.Single(values);
+ Assert.Equal("-1", values.First());
+ Assert.False(response.Headers.TryGetValues("ETag", out values));
+ }
+ }
+
+ [Fact]
+ public async Task DoesNotModifyCacheHeaders_WhenNoExceptionIsThrown()
+ {
+ var expiresTime = DateTime.UtcNow.AddDays(10).ToString("R");
+ var expectedResponseBody = "Hello world!";
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseExceptionHandler("/handle-errors");
+
+ app.Map("/handle-errors", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ await httpContext.Response.WriteAsync("Handled error in a custom way.");
+ });
+ });
+
+ app.Run(async (httpContext) =>
+ {
+ httpContext.Response.Headers.Add("Cache-Control", new[] { "max-age=3600" });
+ httpContext.Response.Headers.Add("Pragma", new[] { "max-age=3600" });
+ httpContext.Response.Headers.Add("Expires", new[] { expiresTime });
+ httpContext.Response.Headers.Add("ETag", new[] { "abcdef" });
+
+ await httpContext.Response.WriteAsync(expectedResponseBody);
+ });
+ });
+
+ using (var server = new TestServer(builder))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ Assert.Equal(expectedResponseBody, await response.Content.ReadAsStringAsync());
+ IEnumerable<string> values;
+ Assert.True(response.Headers.TryGetValues("Cache-Control", out values));
+ Assert.Single(values);
+ Assert.Equal("max-age=3600", values.First());
+ Assert.True(response.Headers.TryGetValues("Pragma", out values));
+ Assert.Single(values);
+ Assert.Equal("max-age=3600", values.First());
+ Assert.True(response.Content.Headers.TryGetValues("Expires", out values));
+ Assert.Single(values);
+ Assert.Equal(expiresTime, values.First());
+ Assert.True(response.Headers.TryGetValues("ETag", out values));
+ Assert.Single(values);
+ Assert.Equal("abcdef", values.First());
+ }
+ }
+
+ [Fact]
+ public async Task DoesNotClearCacheHeaders_WhenResponseHasAlreadyStarted()
+ {
+ var expiresTime = DateTime.UtcNow.AddDays(10).ToString("R");
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(async (httpContext, next) =>
+ {
+ Exception exception = null;
+ try
+ {
+ await next();
+ }
+ catch (InvalidOperationException ex)
+ {
+ exception = ex;
+ }
+
+ Assert.NotNull(exception);
+ Assert.Equal("Something bad happened", exception.Message);
+ });
+
+ app.UseExceptionHandler("/handle-errors");
+
+ app.Map("/handle-errors", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ await httpContext.Response.WriteAsync("Handled error in a custom way.");
+ });
+ });
+
+ app.Run(async (httpContext) =>
+ {
+ httpContext.Response.Headers.Add("Cache-Control", new[] { "max-age=3600" });
+ httpContext.Response.Headers.Add("Pragma", new[] { "max-age=3600" });
+ httpContext.Response.Headers.Add("Expires", new[] { expiresTime });
+ httpContext.Response.Headers.Add("ETag", new[] { "abcdef" });
+
+ await httpContext.Response.WriteAsync("Hello");
+
+ throw new InvalidOperationException("Something bad happened");
+ });
+ });
+
+ using (var server = new TestServer(builder))
+ {
+ var client = server.CreateClient();
+ var response = await client.GetAsync(string.Empty);
+ response.EnsureSuccessStatusCode();
+ Assert.Equal("Hello", await response.Content.ReadAsStringAsync());
+ IEnumerable<string> values;
+ Assert.True(response.Headers.TryGetValues("Cache-Control", out values));
+ Assert.Single(values);
+ Assert.Equal("max-age=3600", values.First());
+ Assert.True(response.Headers.TryGetValues("Pragma", out values));
+ Assert.Single(values);
+ Assert.Equal("max-age=3600", values.First());
+ Assert.True(response.Content.Headers.TryGetValues("Expires", out values));
+ Assert.Single(values);
+ Assert.Equal(expiresTime, values.First());
+ Assert.True(response.Headers.TryGetValues("ETag", out values));
+ Assert.Single(values);
+ Assert.Equal("abcdef", values.First());
+ }
+ }
+
+ [Fact]
+ public async Task HandledErrorsWriteToDiagnosticWhenUsingExceptionHandler()
+ {
+ // Arrange
+ DiagnosticListener diagnosticListener = null;
+
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ diagnosticListener = app.ApplicationServices.GetRequiredService<DiagnosticListener>();
+
+ app.UseExceptionHandler("/handle-errors");
+ app.Map("/handle-errors", (innerAppBuilder) =>
+ {
+ innerAppBuilder.Run(async (httpContext) =>
+ {
+ await httpContext.Response.WriteAsync("Handled error in a custom way.");
+ });
+ });
+ app.Run(context =>
+ {
+ throw new Exception("Test exception");
+ });
+ });
+ var server = new TestServer(builder);
+
+ var listener = new TestDiagnosticListener();
+ diagnosticListener.SubscribeWithAdapter(listener);
+
+ // Act
+ await server.CreateClient().GetAsync(string.Empty);
+
+ // This ensures that all diagnostics are completely written to the diagnostic listener
+ Thread.Sleep(1000);
+
+ // Assert
+ Assert.NotNull(listener.EndRequest?.HttpContext);
+ Assert.Null(listener.HostingUnhandledException?.HttpContext);
+ Assert.Null(listener.HostingUnhandledException?.Exception);
+ Assert.Null(listener.DiagnosticUnhandledException?.HttpContext);
+ Assert.Null(listener.DiagnosticUnhandledException?.Exception);
+ Assert.NotNull(listener.DiagnosticHandledException?.HttpContext);
+ Assert.NotNull(listener.DiagnosticHandledException?.Exception);
+ }
+
+ [Fact]
+ public void UsingExceptionHandler_ThrowsAnException_WhenExceptionHandlingPathNotSet()
+ {
+ // Arrange
+ DiagnosticListener diagnosticListener = null;
+
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ diagnosticListener = app.ApplicationServices.GetRequiredService<DiagnosticListener>();
+ app.UseExceptionHandler();
+ });
+
+ // Act
+ var exception = Assert.Throws<InvalidOperationException>(() => new TestServer(builder));
+
+ // Assert
+ Assert.Equal($"An error occurred when configuring the exception handler middleware. " +
+ $"Either the 'ExceptionHandlingPath' or the 'ExceptionHandler' option must be set in 'UseExceptionHandler()'.",
+ exception.Message);
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj b/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj
new file mode 100644
index 0000000000..5c37cdf8c4
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/UnitTests/Microsoft.AspNetCore.Diagnostics.Tests.csproj
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <EmbeddedResource Include="Resources\**" Exclude="$(DefaultItemExcludes)" />
+ <Content Include="TestFiles\**\*" CopyToOutputDirectory="PreserveNewest" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\testassets\ClassLibraryWithPortablePdbs\ClassLibraryWithPortablePdbs.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+ <Reference Include="Microsoft.AspNetCore.TestHost" />
+ <Reference Include="Microsoft.Extensions.DependencyInjection" />
+ <Reference Include="Microsoft.Extensions.DiagnosticAdapter" />
+ <Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/UnitTests/Resources/TestFiles/EmbeddedSourceFile.txt b/src/Middleware/Diagnostics/test/UnitTests/Resources/TestFiles/EmbeddedSourceFile.txt
new file mode 100644
index 0000000000..93c63d5245
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/UnitTests/Resources/TestFiles/EmbeddedSourceFile.txt
@@ -0,0 +1,30 @@
+Line1
+Line2
+Line3
+Line4
+Line5
+Line6
+Line7
+Line8
+Line9
+Line10
+Line11
+Line12
+Line13
+Line14
+Line15
+Line16
+Line17
+Line18
+Line19
+Line20
+Line21
+Line22
+Line23
+Line24
+Line25
+Line26
+Line27
+Line28
+Line29
+Line30 \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/UnitTests/TestDiagnosticListener.cs b/src/Middleware/Diagnostics/test/UnitTests/TestDiagnosticListener.cs
new file mode 100644
index 0000000000..1d8e036ef9
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/UnitTests/TestDiagnosticListener.cs
@@ -0,0 +1,83 @@
+// 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 Microsoft.Extensions.DiagnosticAdapter;
+
+namespace Microsoft.AspNetCore.Diagnostics
+{
+ public class TestDiagnosticListener
+ {
+ public class OnRequestEventData
+ {
+ public IProxyHttpContext HttpContext { get; set; }
+ }
+
+ public class OnExceptionEventData
+ {
+ public IProxyHttpContext HttpContext { get; set; }
+ public IProxyException Exception { get; set; }
+ }
+
+ public OnRequestEventData BeginRequest { get; set; }
+ public OnRequestEventData EndRequest { get; set; }
+ public OnExceptionEventData HostingUnhandledException { get; set; }
+ public OnExceptionEventData DiagnosticUnhandledException { get; set; }
+ public OnExceptionEventData DiagnosticHandledException { get; set; }
+
+ [DiagnosticName("Microsoft.AspNetCore.Hosting.BeginRequest")]
+ public virtual void OnBeginRequest(IProxyHttpContext httpContext)
+ {
+ BeginRequest = new OnRequestEventData()
+ {
+ HttpContext = httpContext
+ };
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.Hosting.EndRequest")]
+ public virtual void OnEndRequest(IProxyHttpContext httpContext)
+ {
+ EndRequest = new OnRequestEventData()
+ {
+ HttpContext = httpContext
+ };
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.Hosting.UnhandledException")]
+ public virtual void OnHostingUnhandledException(IProxyHttpContext httpContext, IProxyException exception)
+ {
+ HostingUnhandledException = new OnExceptionEventData()
+ {
+ HttpContext = httpContext,
+ Exception = exception
+ };
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.Diagnostics.UnhandledException")]
+ public virtual void OnDiagnosticUnhandledException(IProxyHttpContext httpContext, IProxyException exception)
+ {
+ DiagnosticUnhandledException = new OnExceptionEventData()
+ {
+ HttpContext = httpContext,
+ Exception = exception
+ };
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.Diagnostics.HandledException")]
+ public virtual void OnDiagnosticHandledException(IProxyHttpContext httpContext, IProxyException exception)
+ {
+ DiagnosticHandledException = new OnExceptionEventData()
+ {
+ HttpContext = httpContext,
+ Exception = exception
+ };
+ }
+
+ public interface IProxyHttpContext
+ {
+ }
+
+ public interface IProxyException
+ {
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/UnitTests/TestFiles/SourceFile.txt b/src/Middleware/Diagnostics/test/UnitTests/TestFiles/SourceFile.txt
new file mode 100644
index 0000000000..93c63d5245
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/UnitTests/TestFiles/SourceFile.txt
@@ -0,0 +1,30 @@
+Line1
+Line2
+Line3
+Line4
+Line5
+Line6
+Line7
+Line8
+Line9
+Line10
+Line11
+Line12
+Line13
+Line14
+Line15
+Line16
+Line17
+Line18
+Line19
+Line20
+Line21
+Line22
+Line23
+Line24
+Line25
+Line26
+Line27
+Line28
+Line29
+Line30 \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ClassLibraryWithPortablePdbs.csproj b/src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ClassLibraryWithPortablePdbs.csproj
new file mode 100644
index 0000000000..af5457b63c
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ClassLibraryWithPortablePdbs.csproj
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <DebugType>portable</DebugType>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ExceptionType.cs b/src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ExceptionType.cs
new file mode 100644
index 0000000000..8ca0f563af
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/ClassLibraryWithPortablePdbs/ExceptionType.cs
@@ -0,0 +1,20 @@
+// 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;
+
+namespace ClassLibraryWithPortablePdbs
+{
+ public class ExceptionType
+ {
+ public static void StaticMethodThatThrows()
+ {
+ throw new Exception();
+ }
+
+ public void MethodThatThrows()
+ {
+ throw new Exception();
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/DatabaseErrorPageSample.csproj b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/DatabaseErrorPageSample.csproj
new file mode 100644
index 0000000000..cdd38cc36e
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/DatabaseErrorPageSample.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+ <Reference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ <Reference Include="Microsoft.EntityFrameworkCore.SqlServer" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Startup.cs b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Startup.cs
new file mode 100644
index 0000000000..2247b52ba1
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Startup.cs
@@ -0,0 +1,57 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DatabaseErrorPageSample
+{
+ public class Startup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddDbContext<MyContext>(
+ options => options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=DatabaseErrorPageSample;Trusted_Connection=True;"));
+ }
+
+ public void Configure(IApplicationBuilder app)
+ {
+ app.UseDeveloperExceptionPage();
+ app.UseDatabaseErrorPage();
+ app.Run(context =>
+ {
+ context.RequestServices.GetService<MyContext>().Blog.FirstOrDefault();
+ return Task.FromResult(0);
+ });
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .UseKestrel()
+ .UseIISIntegration()
+ .UseStartup<Startup>()
+ .Build();
+
+ host.Run();
+ }
+ }
+
+ public class MyContext : DbContext
+ {
+ public MyContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ public DbSet<Blog> Blog { get; set; }
+ }
+
+ public class Blog
+ {
+ public int BlogId { get; set; }
+ public string Url { get; set; }
+ }
+}
+
diff --git a/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/web.config b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/web.config
new file mode 100644
index 0000000000..f7ac679334
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/web.config
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<configuration>
+ <system.webServer>
+ <handlers>
+ <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
+ </handlers>
+ <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
+ </system.webServer>
+</configuration> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/DeveloperExceptionPageSample.csproj b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/DeveloperExceptionPageSample.csproj
new file mode 100644
index 0000000000..2c4812c068
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/DeveloperExceptionPageSample.csproj
@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Startup.cs b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Startup.cs
new file mode 100644
index 0000000000..4da64e1fbe
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Startup.cs
@@ -0,0 +1,33 @@
+using System;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+
+namespace DeveloperExceptionPageSample
+{
+ public class Startup
+ {
+ public void Configure(IApplicationBuilder app)
+ {
+ app.UseDeveloperExceptionPage();
+ app.Run(context =>
+ {
+ throw new Exception(string.Concat(
+ "Demonstration exception. The list:", "\r\n",
+ "New Line 1", "\n",
+ "New Line 2", Environment.NewLine,
+ "New Line 3"));
+ });
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .UseKestrel()
+ .UseIISIntegration()
+ .UseStartup<Startup>()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/web.config b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/web.config
new file mode 100644
index 0000000000..f7ac679334
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/web.config
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<configuration>
+ <system.webServer>
+ <handlers>
+ <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
+ </handlers>
+ <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
+ </system.webServer>
+</configuration> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/ExceptionHandlerSample.csproj b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/ExceptionHandlerSample.csproj
new file mode 100644
index 0000000000..56d768b5f9
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/ExceptionHandlerSample.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+
+ <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ <Reference Include="Microsoft.AspNetCore.StaticFiles" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Startup.cs b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Startup.cs
new file mode 100644
index 0000000000..cf2e57429b
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Startup.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Text.Encodings.Web;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+
+namespace ExceptionHandlerSample
+{
+ public class Startup
+ {
+ public void Configure(IApplicationBuilder app)
+ {
+ // Configure the error handler to show an error page.
+ app.UseExceptionHandler(errorApp =>
+ {
+ // Normally you'd use MVC or similar to render a nice page.
+ errorApp.Run(async context =>
+ {
+ context.Response.StatusCode = 500;
+ context.Response.ContentType = "text/html";
+ await context.Response.WriteAsync("<html><body>\r\n");
+ await context.Response.WriteAsync("We're sorry, we encountered an un-expected issue with your application.<br>\r\n");
+
+ var error = context.Features.Get<IExceptionHandlerFeature>();
+ if (error != null)
+ {
+ // This error would not normally be exposed to the client
+ await context.Response.WriteAsync("<br>Error: " + HtmlEncoder.Default.Encode(error.Error.Message) + "<br>\r\n");
+ }
+ await context.Response.WriteAsync("<br><a href=\"/\">Home</a><br>\r\n");
+ await context.Response.WriteAsync("</body></html>\r\n");
+ await context.Response.WriteAsync(new string(' ', 512)); // Padding for IE
+ });
+ });
+
+ // We could also configure it to re-execute the request on the normal pipeline with a different path.
+ // app.UseExceptionHandler("/error.html");
+
+ // The broken section of our application.
+ app.Map("/throw", throwApp =>
+ {
+ throwApp.Run(context => { throw new Exception("Application Exception"); });
+ });
+
+ app.UseStaticFiles();
+
+ // The home page.
+ app.Run(async context =>
+ {
+ context.Response.ContentType = "text/html";
+ await context.Response.WriteAsync("<html><body>Welcome to the sample<br><br>\r\n");
+ await context.Response.WriteAsync("Click here to throw an exception: <a href=\"/throw\">throw</a>\r\n");
+ await context.Response.WriteAsync("</body></html>\r\n");
+ });
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .UseKestrel()
+ .UseIISIntegration()
+ .UseStartup<Startup>()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
+
diff --git a/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/web.config b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/web.config
new file mode 100644
index 0000000000..f7ac679334
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/web.config
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<configuration>
+ <system.webServer>
+ <handlers>
+ <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
+ </handlers>
+ <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
+ </system.webServer>
+</configuration> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/wwwroot/error.html b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/wwwroot/error.html
new file mode 100644
index 0000000000..b9989e017f
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/wwwroot/error.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta charset="utf-8" />
+ <title></title>
+</head>
+<body>
+ You've reached the static error page.<br /><br />
+ <a href="/">Home</a><br />
+</body>
+</html> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Startup.cs b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Startup.cs
new file mode 100644
index 0000000000..594cc19340
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Startup.cs
@@ -0,0 +1,116 @@
+// 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.Linq;
+using System.Net;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+
+namespace StatusCodePagesSample
+{
+ public class Startup
+ {
+ public void Configure(IApplicationBuilder app)
+ {
+ app.UseDeveloperExceptionPage();
+ app.UseStatusCodePages(); // There is a default response but any of the following can be used to change the behavior.
+
+ // app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
+ // app.UseStatusCodePages("text/plain", "Response, status code: {0}");
+ // app.UseStatusCodePagesWithRedirects("~/errors/{0}"); // PathBase relative
+ // app.UseStatusCodePagesWithRedirects("/base/errors/{0}"); // Absolute
+ // app.UseStatusCodePages(builder => builder.UseWelcomePage());
+ // app.UseStatusCodePagesWithReExecute("/errors/{0}");
+
+ // "/[?statuscode=400]"
+ app.Use(async (context, next) =>
+ {
+ // Check for ?statuscode=400
+ var requestedStatusCode = context.Request.Query["statuscode"];
+ if (!string.IsNullOrEmpty(requestedStatusCode))
+ {
+ context.Response.StatusCode = int.Parse(requestedStatusCode);
+
+ // To turn off the StatusCode feature - For example the below code turns off the StatusCode middleware
+ // if the query contains a disableStatusCodePages=true parameter.
+ var disableStatusCodePages = context.Request.Query["disableStatusCodePages"];
+ if (disableStatusCodePages == "true")
+ {
+ var statusCodePagesFeature = context.Features.Get<IStatusCodePagesFeature>();
+ if (statusCodePagesFeature != null)
+ {
+ statusCodePagesFeature.Enabled = false;
+ }
+ }
+
+ await Task.FromResult(0);
+ }
+ else
+ {
+ await next();
+ }
+ });
+
+ // "/errors/400"
+ app.Map("/errors", error =>
+ {
+ error.Run(async context =>
+ {
+ var builder = new StringBuilder();
+ builder.AppendLine("<html><body>");
+ builder.AppendLine("An error occurred, Status Code: " + HtmlEncoder.Default.Encode(context.Request.Path.ToString().Substring(1)) + "<br>");
+ var referrer = context.Request.Headers["referer"];
+ if (!string.IsNullOrEmpty(referrer))
+ {
+ builder.AppendLine("<a href=\"" + HtmlEncoder.Default.Encode(referrer) + "\">Retry " + WebUtility.HtmlEncode(referrer) + "</a><br>");
+ }
+ builder.AppendLine("</body></html>");
+ context.Response.ContentType = "text/html";
+ await context.Response.WriteAsync(builder.ToString());
+ });
+ });
+
+ app.Run(async context =>
+ {
+ // Generates the HTML with all status codes.
+ var builder = new StringBuilder();
+ builder.AppendLine("<html><body>");
+ builder.AppendLine("<a href=\"" +
+ HtmlEncoder.Default.Encode(context.Request.PathBase.ToString()) + "/missingpage/\">" +
+ HtmlEncoder.Default.Encode(context.Request.PathBase.ToString()) + "/missingpage/</a><br>");
+
+ var space = string.Concat(Enumerable.Repeat("&nbsp;", 12));
+ builder.AppendFormat("<br><b>{0}{1}{2}</b><br>", "Status Code", space, "Status Code Pages");
+ for (int statusCode = 400; statusCode < 600; statusCode++)
+ {
+ builder.AppendFormat("{0}{1}{2}{3}<br>",
+ statusCode,
+ space + space,
+ string.Format("<a href=\"?statuscode={0}\">[Enabled]</a>{1}", statusCode, space),
+ string.Format("<a href=\"?statuscode={0}&disableStatusCodePages=true\">[Disabled]</a>{1}", statusCode, space));
+ }
+
+ builder.AppendLine("</body></html>");
+ context.Response.ContentType = "text/html";
+ await context.Response.WriteAsync(builder.ToString());
+ });
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .UseKestrel()
+ .UseIISIntegration()
+ .UseStartup<Startup>()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/StatusCodePagesSample.csproj b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/StatusCodePagesSample.csproj
new file mode 100644
index 0000000000..84841ec052
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/StatusCodePagesSample.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+ <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/web.config b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/web.config
new file mode 100644
index 0000000000..f7ac679334
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/web.config
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<configuration>
+ <system.webServer>
+ <handlers>
+ <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
+ </handlers>
+ <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
+ </system.webServer>
+</configuration> \ No newline at end of file
diff --git a/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Startup.cs b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Startup.cs
new file mode 100644
index 0000000000..866b10d788
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Startup.cs
@@ -0,0 +1,25 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+
+namespace WelcomePageSample
+{
+ public class Startup
+ {
+ public void Configure(IApplicationBuilder app)
+ {
+ app.UseWelcomePage();
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .UseKestrel()
+ .UseIISIntegration()
+ .UseStartup<Startup>()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
+
diff --git a/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/WelcomePageSample.csproj b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/WelcomePageSample.csproj
new file mode 100644
index 0000000000..84841ec052
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/WelcomePageSample.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+ <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/web.config b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/web.config
new file mode 100644
index 0000000000..f7ac679334
--- /dev/null
+++ b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/web.config
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<configuration>
+ <system.webServer>
+ <handlers>
+ <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
+ </handlers>
+ <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
+ </system.webServer>
+</configuration> \ No newline at end of file
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheck.cs b/src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheck.cs
new file mode 100644
index 0000000000..7fa998f296
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheck.cs
@@ -0,0 +1,56 @@
+// 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.Extensions.Diagnostics.HealthChecks
+{
+ internal sealed class DbContextHealthCheck<TContext> : IHealthCheck where TContext : DbContext
+ {
+ private static readonly Func<TContext, CancellationToken, Task<bool>> DefaultTestQuery = (dbContext, cancellationToken) =>
+ {
+ return dbContext.Database.CanConnectAsync(cancellationToken);
+ };
+
+ private readonly TContext _dbContext;
+ private readonly IOptionsMonitor<DbContextHealthCheckOptions<TContext>> _options;
+
+ public DbContextHealthCheck(TContext dbContext, IOptionsMonitor<DbContextHealthCheckOptions<TContext>> options)
+ {
+ if (dbContext == null)
+ {
+ throw new ArgumentNullException(nameof(dbContext));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ _dbContext = dbContext;
+ _options = options;
+ }
+
+ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var options = _options.Get(context.Registration.Name);
+ var testQuery = options.CustomTestQuery ?? DefaultTestQuery;
+
+ if (await testQuery(_dbContext, cancellationToken))
+ {
+ return HealthCheckResult.Healthy();
+ }
+
+ return HealthCheckResult.Unhealthy();
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheckOptions.cs b/src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheckOptions.cs
new file mode 100644
index 0000000000..7fb330c376
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/src/DbContextHealthCheckOptions.cs
@@ -0,0 +1,15 @@
+// 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+
+namespace Microsoft.Extensions.Diagnostics.HealthChecks
+{
+ internal sealed class DbContextHealthCheckOptions<TContext> where TContext : DbContext
+ {
+ public Func<TContext, CancellationToken, Task<bool>> CustomTestQuery { get; set; }
+ }
+}
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/src/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensions.cs b/src/Middleware/HealthChecks.EntityFrameworkCore/src/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensions.cs
new file mode 100644
index 0000000000..bf299bc6b0
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/src/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensions.cs
@@ -0,0 +1,79 @@
+// 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class EntityFrameworkCoreHealthChecksBuilderExtensions
+ {
+ /// <summary>
+ /// Adds a health check for the specified <see cref="DbContext"/> type.
+ /// </summary>
+ /// <typeparam name="TContext">The <see cref="DbContext"/> type.</typeparam>
+ /// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
+ /// <param name="name">
+ /// The health check name. Optional. If <c>null</c> the type name of <typeparamref name="TContext"/> will be used for the name.
+ /// </param>
+ /// <param name="failureStatus">
+ /// The <see cref="HealthStatus"/> that should be reported when the health check fails. Optional. If <c>null</c> then
+ /// the default status of <see cref="HealthStatus.Unhealthy"/> will be reported.
+ /// </param>
+ /// <param name="tags">A list of tags that can be used to filter sets of health checks. Optional.</param>
+ /// <param name="customTestQuery">
+ /// A custom test query that will be executed when the health check executes to test the health of the database
+ /// connection and configurations.
+ /// </param>
+ /// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
+ /// <remarks>
+ /// <para>
+ /// The health check implementation added by this method will use the dependency injection container
+ /// to create an instance of <typeparamref name="TContext"/>.
+ /// </para>
+ /// <para>
+ /// By default the health check implementation will use the <see cref="DatabaseFacade.CanConnectAsync(CancellationToken)"/> method
+ /// to test connectivity to the database. This method requires that the database provider has correctly implemented the
+ /// <see cref="IDatabaseCreatorWithCanConnect" /> interface. If the database provide has not implemented this interface
+ /// then the health check will report a failure.
+ /// </para>
+ /// <para>
+ /// Providing a <paramref name="customTestQuery" /> will replace the use of <see cref="DatabaseFacade.CanConnectAsync(CancellationToken)"/>
+ /// to test database connectivity. An implementation of a test query should handle exceptions that can arise due to connectivity failure,
+ /// and should return a pass/fail result. The test query should be be designed to complete in a short and predicatable amount of time.
+ /// </para>
+ /// </remarks>
+ public static IHealthChecksBuilder AddDbContextCheck<TContext>(
+ this IHealthChecksBuilder builder,
+ string name = null,
+ HealthStatus? failureStatus = default,
+ IEnumerable<string> tags = default,
+ Func<TContext, CancellationToken, Task<bool>> customTestQuery = default)
+ where TContext : DbContext
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ if (name == null)
+ {
+ name = typeof(TContext).Name;
+ }
+
+ if (customTestQuery != null)
+ {
+ builder.Services.Configure<DbContextHealthCheckOptions<TContext>>(name, options => options.CustomTestQuery = customTestQuery);
+ }
+
+ return builder.AddCheck<DbContextHealthCheck<TContext>>(name, failureStatus, tags);
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj b/src/Middleware/HealthChecks.EntityFrameworkCore/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj
new file mode 100644
index 0000000000..6b59175d82
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/src/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>
+ Components for performing health checks using EntityFrameworkCore.
+ </Description>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <NoWarn>$(NoWarn);CS1591</NoWarn>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>diagnostics;healthchecks;entityframeworkcore</PackageTags>
+ <BaseNamespace>Microsoft.Extensions.Diagnostics.HealthChecks</BaseNamespace>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.EntityFrameworkCore.Relational" />
+ <Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
+ <Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/src/Properties/AssemblyInfo.cs b/src/Middleware/HealthChecks.EntityFrameworkCore/src/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..1aa256d83a
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/src/Properties/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/test/DbContextHealthCheckTest.cs b/src/Middleware/HealthChecks.EntityFrameworkCore/test/DbContextHealthCheckTest.cs
new file mode 100644
index 0000000000..b564a62795
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/test/DbContextHealthCheckTest.cs
@@ -0,0 +1,119 @@
+// 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Xunit;
+
+namespace Microsoft.Extensions.Diagnostics.HealthChecks
+{
+ public class DbContextHealthCheckTest
+ {
+ // Just testing healthy here since it would be complicated to simulate a failure. All of that logic lives in EF anyway.
+ [Fact]
+ public async Task CheckAsync_DefaultTest_Healthy()
+ {
+ // Arrange
+ var services = CreateServices();
+ using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var registration = Assert.Single(services.GetRequiredService<IOptions<HealthCheckServiceOptions>>().Value.Registrations);
+ var check = ActivatorUtilities.CreateInstance<DbContextHealthCheck<TestDbContext>>(scope.ServiceProvider);
+
+ // Act
+ var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, });
+
+ // Assert
+ Assert.Equal(HealthStatus.Healthy, result.Status);
+ }
+ }
+
+ [Fact]
+ public async Task CheckAsync_CustomTest_Healthy()
+ {
+ // Arrange
+ var services = CreateServices(async (c, ct) =>
+ {
+ return 0 < await c.Blogs.CountAsync();
+ });
+
+ using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var registration = Assert.Single(services.GetRequiredService<IOptions<HealthCheckServiceOptions>>().Value.Registrations);
+ var check = ActivatorUtilities.CreateInstance<DbContextHealthCheck<TestDbContext>>(scope.ServiceProvider);
+
+ // Add a blog so that the custom test passes
+ var context = scope.ServiceProvider.GetRequiredService<TestDbContext>();
+ context.Add(new Blog());
+ await context.SaveChangesAsync();
+
+ // Act
+ var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, });
+
+ // Assert
+ Assert.Equal(HealthStatus.Healthy, result.Status);
+ }
+ }
+
+ [Fact]
+ public async Task CheckAsync_CustomTest_Degraded()
+ {
+ // Arrange
+ var services = CreateServices(async (c, ct) =>
+ {
+ return 0 < await c.Blogs.CountAsync();
+ }, failureStatus: HealthStatus.Degraded);
+
+ using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var registration = Assert.Single(services.GetRequiredService<IOptions<HealthCheckServiceOptions>>().Value.Registrations);
+ var check = ActivatorUtilities.CreateInstance<DbContextHealthCheck<TestDbContext>>(scope.ServiceProvider);
+
+ // Act
+ var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, });
+
+ // Assert
+ Assert.Equal(HealthStatus.Unhealthy, result.Status);
+ }
+ }
+
+ [Fact]
+ public async Task CheckAsync_CustomTest_Unhealthy()
+ {
+ // Arrange
+ var services = CreateServices(async (c, ct) =>
+ {
+ return 0 < await c.Blogs.CountAsync();
+ }, failureStatus: HealthStatus.Unhealthy);
+
+ using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var registration = Assert.Single(services.GetRequiredService<IOptions<HealthCheckServiceOptions>>().Value.Registrations);
+ var check = ActivatorUtilities.CreateInstance<DbContextHealthCheck<TestDbContext>>(scope.ServiceProvider);
+
+ // Act
+ var result = await check.CheckHealthAsync(new HealthCheckContext() { Registration = registration, });
+
+ // Assert
+ Assert.Equal(HealthStatus.Unhealthy, result.Status);
+ }
+ }
+
+ private static IServiceProvider CreateServices(
+ Func<TestDbContext, CancellationToken, Task<bool>> testQuery = null,
+ HealthStatus failureStatus = HealthStatus.Unhealthy)
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddDbContext<TestDbContext>(o => o.UseInMemoryDatabase("Test"));
+
+ var builder = serviceCollection.AddHealthChecks();
+ builder.AddDbContextCheck<TestDbContext>("test", failureStatus, new[] { "tag1", "tag2", }, testQuery);
+ return serviceCollection.BuildServiceProvider();
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/test/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensionsTest.cs b/src/Middleware/HealthChecks.EntityFrameworkCore/test/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensionsTest.cs
new file mode 100644
index 0000000000..9efcb025e0
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/test/DependencyInjection/EntityFrameworkCoreHealthChecksBuilderExtensionsTest.cs
@@ -0,0 +1,47 @@
+// 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.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Options;
+using Xunit;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public class EntityFrameworkCoreHealthChecksBuilderExtensionsTest
+ {
+ [Fact]
+ public void AddDbContextCheck_RegistersDbContextHealthCheck()
+ {
+ // Arrange
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddDbContext<TestDbContext>(o => o.UseInMemoryDatabase("Test"));
+
+ var builder = serviceCollection.AddHealthChecks();
+
+ // Act
+ builder.AddDbContextCheck<TestDbContext>("test", HealthStatus.Degraded, new[] { "tag1", "tag2", }, (c, ct) => Task.FromResult(true));
+
+ // Assert
+ var services = serviceCollection.BuildServiceProvider();
+
+ var registrations = services.GetRequiredService<IOptions<HealthCheckServiceOptions>>().Value.Registrations;
+
+ var registration = Assert.Single(registrations);
+ Assert.Equal("test", registration.Name);
+ Assert.Equal(HealthStatus.Degraded, registration.FailureStatus);
+ Assert.Equal(new[] { "tag1", "tag2", }, registration.Tags.ToArray());
+
+ var options = services.GetRequiredService<IOptionsMonitor<DbContextHealthCheckOptions<TestDbContext>>>();
+ Assert.NotNull(options.Get("test").CustomTestQuery);
+
+ using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
+ {
+ var check = Assert.IsType<DbContextHealthCheck<TestDbContext>>(registration.Factory(scope.ServiceProvider));
+ }
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests.csproj b/src/Middleware/HealthChecks.EntityFrameworkCore/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests.csproj
new file mode 100644
index 0000000000..0523f91842
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/test/Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
+ <RootNamespace>Microsoft.AspNetCore.Diagnostics.HealthChecks</RootNamespace>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.EntityFrameworkCore.InMemory" />
+ <Reference Include="Microsoft.Extensions.DependencyInjection" />
+ <Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/HealthChecks.EntityFrameworkCore/test/TestDbContext.cs b/src/Middleware/HealthChecks.EntityFrameworkCore/test/TestDbContext.cs
new file mode 100644
index 0000000000..e38ed93674
--- /dev/null
+++ b/src/Middleware/HealthChecks.EntityFrameworkCore/test/TestDbContext.cs
@@ -0,0 +1,24 @@
+// 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 Microsoft.EntityFrameworkCore;
+
+namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
+{
+ public class TestDbContext : DbContext
+ {
+ public TestDbContext(DbContextOptions<TestDbContext> options)
+ : base(options)
+ {
+ }
+
+ public DbSet<Blog> Blogs { get; set; }
+ }
+
+ public class Blog
+ {
+ public int Id { get; set; }
+
+ public int Name { get; set; }
+ }
+}
diff --git a/src/Middleware/HealthChecks/src/Builder/HealthCheckApplicationBuilderExtensions.cs b/src/Middleware/HealthChecks/src/Builder/HealthCheckApplicationBuilderExtensions.cs
new file mode 100644
index 0000000000..6f54f51765
--- /dev/null
+++ b/src/Middleware/HealthChecks/src/Builder/HealthCheckApplicationBuilderExtensions.cs
@@ -0,0 +1,254 @@
+// 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 Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Builder
+{
+ /// <summary>
+ /// <see cref="IApplicationBuilder"/> extension methods for the <see cref="HealthCheckMiddleware"/>.
+ /// </summary>
+ public static class HealthCheckApplicationBuilderExtensions
+ {
+ /// <summary>
+ /// Adds a middleware that provides health check status.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <param name="path">The path on which to provide health check status.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// If <paramref name="path"/> is set to <c>null</c> or the empty string then the health check middleware
+ /// will ignore the URL path and process all requests. If <paramref name="path"/> is set to a non-empty
+ /// value, the health check middleware will process requests with a URL that matches the provided value
+ /// of <paramref name="path"/> case-insensitively, allowing for an extra trailing slash ('/') character.
+ /// </para>
+ /// <para>
+ /// The health check middleware will use default settings from <see cref="IOptions{HealthCheckOptions}"/>.
+ /// </para>
+ /// </remarks>
+ public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ UseHealthChecksCore(app, path, port: null, Array.Empty<object>());
+ return app;
+ }
+
+ /// <summary>
+ /// Adds a middleware that provides health check status.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <param name="path">The path on which to provide health check status.</param>
+ /// <param name="options">A <see cref="HealthCheckOptions"/> used to configure the middleware.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// If <paramref name="path"/> is set to <c>null</c> or the empty string then the health check middleware
+ /// will ignore the URL path and process all requests. If <paramref name="path"/> is set to a non-empty
+ /// value, the health check middleware will process requests with a URL that matches the provided value
+ /// of <paramref name="path"/> case-insensitively, allowing for an extra trailing slash ('/') character.
+ /// </para>
+ /// </remarks>
+ public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path, HealthCheckOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ UseHealthChecksCore(app, path, port: null, new[] { Options.Create(options), });
+ return app;
+ }
+
+ /// <summary>
+ /// Adds a middleware that provides health check status.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <param name="path">The path on which to provide health check status.</param>
+ /// <param name="port">The port to listen on. Must be a local port on which the server is listening.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// If <paramref name="path"/> is set to <c>null</c> or the empty string then the health check middleware
+ /// will ignore the URL path and process all requests on the specified port. If <paramref name="path"/> is
+ /// set to a non-empty value, the health check middleware will process requests with a URL that matches the
+ /// provided value of <paramref name="path"/> case-insensitively, allowing for an extra trailing slash ('/')
+ /// character.
+ /// </para>
+ /// <para>
+ /// The health check middleware will use default settings from <see cref="IOptions{HealthCheckOptions}"/>.
+ /// </para>
+ /// </remarks>
+ public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path, int port)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ UseHealthChecksCore(app, path, port, Array.Empty<object>());
+ return app;
+ }
+
+ /// <summary>
+ /// Adds a middleware that provides health check status.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <param name="path">The path on which to provide health check status.</param>
+ /// <param name="port">The port to listen on. Must be a local port on which the server is listening.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// If <paramref name="path"/> is set to <c>null</c> or the empty string then the health check middleware
+ /// will ignore the URL path and process all requests on the specified port. If <paramref name="path"/> is
+ /// set to a non-empty value, the health check middleware will process requests with a URL that matches the
+ /// provided value of <paramref name="path"/> case-insensitively, allowing for an extra trailing slash ('/')
+ /// character.
+ /// </para>
+ /// <para>
+ /// The health check middleware will use default settings from <see cref="IOptions{HealthCheckOptions}"/>.
+ /// </para>
+ /// </remarks>
+ public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path, string port)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ if (port == null)
+ {
+ throw new ArgumentNullException(nameof(port));
+ }
+
+ if (!int.TryParse(port, out var portAsInt))
+ {
+ throw new ArgumentException("The port must be a valid integer.", nameof(port));
+ }
+
+ UseHealthChecksCore(app, path, portAsInt, Array.Empty<object>());
+ return app;
+ }
+
+ /// <summary>
+ /// Adds a middleware that provides health check status.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <param name="path">The path on which to provide health check status.</param>
+ /// <param name="port">The port to listen on. Must be a local port on which the server is listening.</param>
+ /// <param name="options">A <see cref="HealthCheckOptions"/> used to configure the middleware.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// If <paramref name="path"/> is set to <c>null</c> or the empty string then the health check middleware
+ /// will ignore the URL path and process all requests on the specified port. If <paramref name="path"/> is
+ /// set to a non-empty value, the health check middleware will process requests with a URL that matches the
+ /// provided value of <paramref name="path"/> case-insensitively, allowing for an extra trailing slash ('/')
+ /// character.
+ /// </para>
+ /// </remarks>
+ public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path, int port, HealthCheckOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ UseHealthChecksCore(app, path, port, new[] { Options.Create(options), });
+ return app;
+ }
+
+ /// <summary>
+ /// Adds a middleware that provides health check status.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ /// <param name="path">The path on which to provide health check status.</param>
+ /// <param name="port">The port to listen on. Must be a local port on which the server is listening.</param>
+ /// <param name="options">A <see cref="HealthCheckOptions"/> used to configure the middleware.</param>
+ /// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
+ /// <remarks>
+ /// <para>
+ /// If <paramref name="path"/> is set to <c>null</c> or the empty string then the health check middleware
+ /// will ignore the URL path and process all requests on the specified port. If <paramref name="path"/> is
+ /// set to a non-empty value, the health check middleware will process requests with a URL that matches the
+ /// provided value of <paramref name="path"/> case-insensitively, allowing for an extra trailing slash ('/')
+ /// character.
+ /// </para>
+ /// </remarks>
+ public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path, string port, HealthCheckOptions options)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException(nameof(app));
+ }
+
+ if (path == null)
+ {
+ throw new ArgumentNullException(nameof(port));
+ }
+
+ if (!int.TryParse(port, out var portAsInt))
+ {
+ throw new ArgumentException("The port must be a valid integer.", nameof(port));
+ }
+
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+
+ UseHealthChecksCore(app, path, portAsInt, new[] { Options.Create(options), });
+ return app;
+ }
+
+ private static void UseHealthChecksCore(IApplicationBuilder app, PathString path, int? port, object[] args)
+ {
+ // NOTE: we explicitly don't use Map here because it's really common for multiple health
+ // check middleware to overlap in paths. Ex: `/health`, `/health/detailed` - this is order
+ // sensititive with Map, and it's really surprising to people.
+ //
+ // See:
+ // https://github.com/aspnet/Diagnostics/issues/511
+ // https://github.com/aspnet/Diagnostics/issues/512
+ // https://github.com/aspnet/Diagnostics/issues/514
+
+ Func<HttpContext, bool> predicate = c =>
+ {
+ return
+
+ // Process the port if we have one
+ (port == null || c.Connection.LocalPort == port) &&
+
+ // We allow you to listen on all URLs by providing the empty PathString.
+ (!path.HasValue ||
+
+ // If you do provide a PathString, want to handle all of the special cases that
+ // StartsWithSegments handles, but we also want it to have exact match semantics.
+ //
+ // Ex: /Foo/ == /Foo (true)
+ // Ex: /Foo/Bar == /Foo (false)
+ (c.Request.Path.StartsWithSegments(path, out var remaining) &&
+ string.IsNullOrEmpty(remaining)));
+ };
+
+ app.MapWhen(predicate, b => b.UseMiddleware<HealthCheckMiddleware>(args));
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/src/HealthCheckMiddleware.cs b/src/Middleware/HealthChecks/src/HealthCheckMiddleware.cs
new file mode 100644
index 0000000000..ba6b870bdc
--- /dev/null
+++ b/src/Middleware/HealthChecks/src/HealthCheckMiddleware.cs
@@ -0,0 +1,125 @@
+// 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.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Options;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
+{
+ public class HealthCheckMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private readonly HealthCheckOptions _healthCheckOptions;
+ private readonly HealthCheckService _healthCheckService;
+
+ public HealthCheckMiddleware(
+ RequestDelegate next,
+ IOptions<HealthCheckOptions> healthCheckOptions,
+ HealthCheckService healthCheckService)
+ {
+ if (next == null)
+ {
+ throw new ArgumentNullException(nameof(next));
+ }
+
+ if (healthCheckOptions == null)
+ {
+ throw new ArgumentNullException(nameof(healthCheckOptions));
+ }
+
+ if (healthCheckService == null)
+ {
+ throw new ArgumentNullException(nameof(healthCheckService));
+ }
+
+ _next = next;
+ _healthCheckOptions = healthCheckOptions.Value;
+ _healthCheckService = healthCheckService;
+ }
+
+ /// <summary>
+ /// Processes a request.
+ /// </summary>
+ /// <param name="httpContext"></param>
+ /// <returns></returns>
+ public async Task InvokeAsync(HttpContext httpContext)
+ {
+ if (httpContext == null)
+ {
+ throw new ArgumentNullException(nameof(httpContext));
+ }
+
+ // Get results
+ var result = await _healthCheckService.CheckHealthAsync(_healthCheckOptions.Predicate, httpContext.RequestAborted);
+
+ // Map status to response code - this is customizable via options.
+ if (!_healthCheckOptions.ResultStatusCodes.TryGetValue(result.Status, out var statusCode))
+ {
+ var message =
+ $"No status code mapping found for {nameof(HealthStatus)} value: {result.Status}." +
+ $"{nameof(HealthCheckOptions)}.{nameof(HealthCheckOptions.ResultStatusCodes)} must contain" +
+ $"an entry for {result.Status}.";
+
+ throw new InvalidOperationException(message);
+ }
+
+ httpContext.Response.StatusCode = statusCode;
+
+ if (!_healthCheckOptions.AllowCachingResponses)
+ {
+ // Similar to: https://github.com/aspnet/Security/blob/7b6c9cf0eeb149f2142dedd55a17430e7831ea99/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs#L377-L379
+ var headers = httpContext.Response.Headers;
+ headers[HeaderNames.CacheControl] = "no-store, no-cache";
+ headers[HeaderNames.Pragma] = "no-cache";
+ headers[HeaderNames.Expires] = "Thu, 01 Jan 1970 00:00:00 GMT";
+ }
+
+ if (_healthCheckOptions.ResponseWriter != null)
+ {
+ await _healthCheckOptions.ResponseWriter(httpContext, result);
+ }
+ }
+
+ private static IHealthCheck[] FilterHealthChecks(
+ IReadOnlyDictionary<string, IHealthCheck> checks,
+ ISet<string> names)
+ {
+ // If there are no filters then include all checks.
+ if (names.Count == 0)
+ {
+ return checks.Values.ToArray();
+ }
+
+ // Keep track of what we don't find so we can report errors.
+ var notFound = new HashSet<string>(names, StringComparer.OrdinalIgnoreCase);
+ var matches = new List<IHealthCheck>();
+
+ foreach (var kvp in checks)
+ {
+ if (!notFound.Remove(kvp.Key))
+ {
+ // This check was excluded
+ continue;
+ }
+
+ matches.Add(kvp.Value);
+ }
+
+ if (notFound.Count > 0)
+ {
+ var message =
+ $"The following health checks were not found: '{string.Join(", ", notFound)}'. " +
+ $"Registered health checks: '{string.Join(", ", checks.Keys)}'.";
+ throw new InvalidOperationException(message);
+ }
+
+ return matches.ToArray();
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/src/HealthCheckOptions.cs b/src/Middleware/HealthChecks/src/HealthCheckOptions.cs
new file mode 100644
index 0000000000..16b81c2b96
--- /dev/null
+++ b/src/Middleware/HealthChecks/src/HealthCheckOptions.cs
@@ -0,0 +1,63 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
+{
+ /// <summary>
+ /// Contains options for the <see cref="HealthCheckMiddleware"/>.
+ /// </summary>
+ public class HealthCheckOptions
+ {
+ /// <summary>
+ /// Gets or sets a predicate that is used to filter the set of health checks executed.
+ /// </summary>
+ /// <remarks>
+ /// If <see cref="Predicate"/> is <c>null</c>, the <see cref="HealthCheckMiddleware"/> will run all
+ /// registered health checks - this is the default behavior. To run a subset of health checks,
+ /// provide a function that filters the set of checks.
+ /// </remarks>
+ public Func<HealthCheckRegistration, bool> Predicate { get; set; }
+
+ /// <summary>
+ /// Gets a dictionary mapping the <see cref="HealthStatus"/> to an HTTP status code applied to the response.
+ /// This property can be used to configure the status codes returned for each status.
+ /// </summary>
+ public IDictionary<HealthStatus, int> ResultStatusCodes { get; } = new Dictionary<HealthStatus, int>()
+ {
+ { HealthStatus.Healthy, StatusCodes.Status200OK },
+ { HealthStatus.Degraded, StatusCodes.Status200OK },
+ { HealthStatus.Unhealthy, StatusCodes.Status503ServiceUnavailable },
+ };
+
+ /// <summary>
+ /// Gets or sets a delegate used to write the response.
+ /// </summary>
+ /// <remarks>
+ /// The default value is a delegate that will write a minimal <c>text/plain</c> response with the value
+ /// of <see cref="HealthReport.Status"/> as a string.
+ /// </remarks>
+ public Func<HttpContext, HealthReport, Task> ResponseWriter { get; set; } = HealthCheckResponseWriters.WriteMinimalPlaintext;
+
+ /// <summary>
+ /// Gets or sets a value that controls whether responses from the health check middleware can be cached.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The health check middleware does not perform caching of any kind. This setting configures whether
+ /// the middleware will apply headers to the HTTP response that instruct clients to avoid caching.
+ /// </para>
+ /// <para>
+ /// If the value is <c>false</c> the health check middleware will set or override the
+ /// <c>Cache-Control</c>, <c>Expires</c>, and <c>Pragma</c> headers to prevent response caching. If the value
+ /// is <c>true</c> the health check middleware will not modify the cache headers of the response.
+ /// </para>
+ /// </remarks>
+ public bool AllowCachingResponses { get; set; }
+ }
+}
diff --git a/src/Middleware/HealthChecks/src/HealthCheckResponseWriters.cs b/src/Middleware/HealthChecks/src/HealthCheckResponseWriters.cs
new file mode 100644
index 0000000000..50cb8c736e
--- /dev/null
+++ b/src/Middleware/HealthChecks/src/HealthCheckResponseWriters.cs
@@ -0,0 +1,18 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
+{
+ internal static class HealthCheckResponseWriters
+ {
+ public static Task WriteMinimalPlaintext(HttpContext httpContext, HealthReport result)
+ {
+ httpContext.Response.ContentType = "text/plain";
+ return httpContext.Response.WriteAsync(result.Status.ToString());
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj b/src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj
new file mode 100644
index 0000000000..6eecefa7dd
--- /dev/null
+++ b/src/Middleware/HealthChecks/src/Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj
@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>ASP.NET Core middleware for returning the results of Health Checks in the application</Description>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <NoWarn>$(NoWarn);CS1591</NoWarn>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>diagnostics;healthchecks</PackageTags>
+ <Description>ASP.NET Core middleware for returning the results of Health Checks in the application
+
+ </Description>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <NoWarn>$(NoWarn);CS1591</NoWarn>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>diagnostics;healthchecks</PackageTags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
+ <Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
+ <Reference Include="Microsoft.Extensions.Options" />
+ <Reference Include="Microsoft.Net.Http.Headers" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/HealthChecks/src/baseline.netcore.json b/src/Middleware/HealthChecks/src/baseline.netcore.json
new file mode 100644
index 0000000000..d089d2470d
--- /dev/null
+++ b/src/Middleware/HealthChecks/src/baseline.netcore.json
@@ -0,0 +1,5 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.Diagnostics.HealthChecks, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ ]
+} \ No newline at end of file
diff --git a/src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareSampleTest.cs b/src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareSampleTest.cs
new file mode 100644
index 0000000000..bffed9ed66
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareSampleTest.cs
@@ -0,0 +1,75 @@
+// 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.Net;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
+{
+ public class HealthCheckMiddlewareSampleTest
+ {
+ [Fact]
+ public async Task BasicStartup()
+ {
+ var builder = new WebHostBuilder()
+ .UseStartup<HealthChecksSample.BasicStartup>();
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CustomWriterStartup()
+ {
+ var builder = new WebHostBuilder()
+ .UseStartup<HealthChecksSample.CustomWriterStartup>();
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("application/json", response.Content.Headers.ContentType.ToString());
+
+ // Ignoring the body since it contains a bunch of statistics
+ }
+
+ [Fact]
+ public async Task LivenessProbeStartup_Liveness()
+ {
+ var builder = new WebHostBuilder()
+ .UseStartup<HealthChecksSample.LivenessProbeStartup>();
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health/live");
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task LivenessProbeStartup_Readiness()
+ {
+ var builder = new WebHostBuilder()
+ .UseStartup<HealthChecksSample.LivenessProbeStartup>();
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health/ready");
+ Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Unhealthy", await response.Content.ReadAsStringAsync());
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareTests.cs b/src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareTests.cs
new file mode 100644
index 0000000000..9bcefafbaa
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/UnitTests/HealthCheckMiddlewareTests.cs
@@ -0,0 +1,688 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Net.Http.Headers;
+using Newtonsoft.Json;
+using System.Net;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
+{
+ public class HealthCheckMiddlewareTests
+ {
+ [Fact] // Matches based on '.Map'
+ public async Task IgnoresRequestThatDoesNotMatchPath()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/frob");
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ [Fact] // Matches based on '.Map'
+ public async Task MatchIsCaseInsensitive()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/HEALTH");
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task ReturnsPlainTextStatus()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task StatusCodeIs200IfNoChecks()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task StatusCodeIs200IfAllChecksHealthy()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddCheck("Foo", () => HealthCheckResult.Healthy("A-ok!"))
+ .AddCheck("Bar", () => HealthCheckResult.Healthy("A-ok!"))
+ .AddCheck("Baz", () => HealthCheckResult.Healthy("A-ok!"));
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task StatusCodeIs200IfCheckIsDegraded()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddCheck("Foo", () => HealthCheckResult.Healthy("A-ok!"))
+ .AddCheck("Bar", () => HealthCheckResult.Degraded("Not so great."))
+ .AddCheck("Baz", () => HealthCheckResult.Healthy("A-ok!"));
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Degraded", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task StatusCodeIs503IfCheckIsUnhealthy()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
+ .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("Pretty bad.")))
+ .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Unhealthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task StatusCodeIs503IfCheckHasUnhandledException()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
+ .AddAsyncCheck("Bar", () => throw null)
+ .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Unhealthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanUseCustomWriter()
+ {
+ var expectedJson = JsonConvert.SerializeObject(new
+ {
+ status = "Unhealthy",
+ });
+
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ ResponseWriter = (c, r) =>
+ {
+ var json = JsonConvert.SerializeObject(new { status = r.Status.ToString(), });
+ c.Response.ContentType = "application/json";
+ return c.Response.WriteAsync(json);
+ },
+ });
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
+ .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("Pretty bad.")))
+ .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal("application/json", response.Content.Headers.ContentType.ToString());
+
+ var result = await response.Content.ReadAsStringAsync();
+ Assert.Equal(expectedJson, result);
+ }
+
+ [Fact]
+ public async Task NoResponseWriterReturnsEmptyBody()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ ResponseWriter = null,
+ });
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
+ .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("Pretty bad.")))
+ .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
+ Assert.Equal(string.Empty, await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanSetCustomStatusCodes()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ ResultStatusCodes =
+ {
+ [HealthStatus.Healthy] = 201,
+ }
+ });
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.Created, response.StatusCode);
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task SetsCacheHeaders()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ Assert.Equal("no-store, no-cache", response.Headers.CacheControl.ToString());
+ Assert.Equal("no-cache", response.Headers.Pragma.ToString());
+ Assert.Equal(new string[] { "Thu, 01 Jan 1970 00:00:00 GMT" }, response.Content.Headers.GetValues(HeaderNames.Expires));
+ }
+
+ [Fact]
+ public async Task CanSuppressCacheHeaders()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ AllowCachingResponses = true,
+ });
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ Assert.Null(response.Headers.CacheControl);
+ Assert.Empty(response.Headers.Pragma.ToString());
+ Assert.False(response.Content.Headers.Contains(HeaderNames.Expires));
+ }
+
+ [Fact]
+ public async Task CanFilterChecks()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ Predicate = (check) => check.Name == "Foo" || check.Name == "Baz",
+ });
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks()
+ .AddAsyncCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
+ // Will get filtered out
+ .AddAsyncCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("A-ok!")))
+ .AddAsyncCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
+ });
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanListenWithoutPath_AcceptsRequest()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks(default);
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanListenWithPath_AcceptsRequestWithExtraSlash()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health/");
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task CanListenWithPath_AcceptsRequestWithCaseInsensitiveMatch()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/HEALTH");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanListenWithPath_RejectsRequestWithExtraSegments()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health/detailed");
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ // See: https://github.com/aspnet/Diagnostics/issues/511
+ [Fact]
+ public async Task CanListenWithPath_MultipleMiddleware_LeastSpecificFirst()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ // Throws if used
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ ResponseWriter = (c, r) => throw null,
+ });
+
+ app.UseHealthChecks("/health/detailed");
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health/detailed");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ // See: https://github.com/aspnet/Diagnostics/issues/511
+ [Fact]
+ public async Task CanListenWithPath_MultipleMiddleware_MostSpecificFirst()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.UseHealthChecks("/health/detailed");
+
+ // Throws if used
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ ResponseWriter = (c, r) => throw null,
+ });
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health/detailed");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanListenOnPort_AcceptsRequest_OnSpecifiedPort()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(next => async (context) =>
+ {
+ // Need to fake setting the connection info. TestServer doesn't
+ // do that, because it doesn't have a connection.
+ context.Connection.LocalPort = context.Request.Host.Port.Value;
+ await next(context);
+ });
+
+ app.UseHealthChecks("/health", port: 5001);
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanListenOnPortWithoutPath_AcceptsRequest_OnSpecifiedPort()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(next => async (context) =>
+ {
+ // Need to fake setting the connection info. TestServer doesn't
+ // do that, because it doesn't have a connection.
+ context.Connection.LocalPort = context.Request.Host.Port.Value;
+ await next(context);
+ });
+
+ app.UseHealthChecks(default, port: 5001);
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanListenOnPort_RejectsRequest_OnOtherPort()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(next => async (context) =>
+ {
+ // Need to fake setting the connection info. TestServer doesn't
+ // do that, because it doesn't have a connection.
+ context.Connection.LocalPort = context.Request.Host.Port.Value;
+ await next(context);
+ });
+
+ app.UseHealthChecks("/health", port: 5001);
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5000/health");
+
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task CanListenOnPort_MultipleMiddleware()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(next => async (context) =>
+ {
+ // Need to fake setting the connection info. TestServer doesn't
+ // do that, because it doesn't have a connection.
+ context.Connection.LocalPort = context.Request.Host.Port.Value;
+ await next(context);
+ });
+
+ // Throws if used
+ app.UseHealthChecks("/health", port: 5001, new HealthCheckOptions()
+ {
+ ResponseWriter = (c, r) => throw null,
+ });
+
+ app.UseHealthChecks("/health/detailed", port: 5001);
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health/detailed");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+
+ [Fact]
+ public async Task CanListenOnPort_MultipleMiddleware_DifferentPorts()
+ {
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ app.Use(next => async (context) =>
+ {
+ // Need to fake setting the connection info. TestServer doesn't
+ // do that, because it doesn't have a connection.
+ context.Connection.LocalPort = context.Request.Host.Port.Value;
+ await next(context);
+ });
+
+ // Throws if used
+ app.UseHealthChecks("/health", port: 5002, new HealthCheckOptions()
+ {
+ ResponseWriter = (c, r) => throw null,
+ });
+
+ app.UseHealthChecks("/health", port: 5001);
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHealthChecks();
+ });
+
+ var server = new TestServer(builder);
+ var client = server.CreateClient();
+
+ var response = await client.GetAsync("http://localhost:5001/health");
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/UnitTests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj b/src/Middleware/HealthChecks/test/UnitTests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj
new file mode 100644
index 0000000000..ec431460cc
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/UnitTests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
+ <RootNamespace>Microsoft.AspNetCore.Diagnostics.HealthChecks</RootNamespace>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\testassets\HealthChecksSample\HealthChecksSample.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
+ <Reference Include="Microsoft.AspNetCore.TestHost" />
+ <Reference Include="Microsoft.Extensions.Logging.Testing" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/BasicStartup.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/BasicStartup.cs
new file mode 100644
index 0000000000..c89d78c8e5
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/BasicStartup.cs
@@ -0,0 +1,35 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace HealthChecksSample
+{
+ // Pass in `--scenario basic` at the command line to run this sample.
+ public class BasicStartup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Registers required services for health checks
+ services.AddHealthChecks();
+ }
+
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ // This will register the health checks middleware at the URL /health.
+ //
+ // By default health checks will return a 200 with 'Healthy'.
+ // - No health checks are registered by default, the app is healthy if it is reachable
+ // - The default response writer writes the HealthCheckStatus as text/plain content
+ //
+ // This is the simplest way to use health checks, it is suitable for systems
+ // that want to check for 'liveness' of an application.
+ app.UseHealthChecks("/health");
+
+ app.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Go to /health to see the health status");
+ });
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/CustomWriterStartup.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/CustomWriterStartup.cs
new file mode 100644
index 0000000000..6e37b1c6ff
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/CustomWriterStartup.cs
@@ -0,0 +1,58 @@
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace HealthChecksSample
+{
+ // Pass in `--scenario writer` at the command line to run this sample.
+ public class CustomWriterStartup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Registers required services for health checks
+ services.AddHealthChecks()
+
+ // Registers a custom health check implementation
+ .AddGCInfoCheck("GCInfo");
+ }
+
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ // This will register the health checks middleware at the URL /health
+ //
+ // This example overrides the HealthCheckResponseWriter to write the health
+ // check result in a totally custom way.
+ app.UseHealthChecks("/health", new HealthCheckOptions()
+ {
+ // This custom writer formats the detailed status as JSON.
+ ResponseWriter = WriteResponse,
+ });
+
+ app.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Go to /health to see the health status");
+ });
+ }
+
+ private static Task WriteResponse(HttpContext httpContext, HealthReport result)
+ {
+ httpContext.Response.ContentType = "application/json";
+
+ var json = new JObject(
+ new JProperty("status", result.Status.ToString()),
+ new JProperty("results", new JObject(result.Entries.Select(pair =>
+ new JProperty(pair.Key, new JObject(
+ new JProperty("status", pair.Value.Status.ToString()),
+ new JProperty("description", pair.Value.Description),
+ new JProperty("data", new JObject(pair.Value.Data.Select(p => new JProperty(p.Key, p.Value))))))))));
+ return httpContext.Response.WriteAsync(json.ToString(Formatting.Indented));
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DBHealthStartup.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DBHealthStartup.cs
new file mode 100644
index 0000000000..46639d26ea
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DBHealthStartup.cs
@@ -0,0 +1,46 @@
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace HealthChecksSample
+{
+ // Pass in `--scenario db` at the command line to run this sample.
+ public class DbHealthStartup
+ {
+ public DbHealthStartup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Registers required services for health checks
+ services.AddHealthChecks()
+ // Add a health check for a SQL database
+ .AddCheck("MyDatabase", new SqlConnectionHealthCheck(Configuration["ConnectionStrings:DefaultConnection"]));
+ }
+
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ // This will register the health checks middleware at the URL /health.
+ //
+ // By default health checks will return a 200 with 'Healthy' when the database is responsive
+ // - We've registered a SqlConnectionHealthCheck
+ // - The default response writer writes the HealthCheckStatus as text/plain content
+ //
+ // This is the simplest way to use health checks, it is suitable for systems
+ // that want to check for 'liveness' of an application with a database.
+ app.UseHealthChecks("/health");
+
+ app.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Go to /health to see the health status");
+ });
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbConnectionHealthCheck.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbConnectionHealthCheck.cs
new file mode 100644
index 0000000000..cb865f67c0
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbConnectionHealthCheck.cs
@@ -0,0 +1,61 @@
+// 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.Data.Common;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace HealthChecksSample
+{
+ public abstract class DbConnectionHealthCheck : IHealthCheck
+ {
+ protected DbConnectionHealthCheck(string connectionString)
+ : this(connectionString, testQuery: null)
+ {
+ }
+
+ protected DbConnectionHealthCheck(string connectionString, string testQuery)
+ {
+ ConnectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
+ TestQuery = testQuery;
+ }
+
+ protected string ConnectionString { get; }
+
+ // This sample supports specifying a query to run as a boolean test of whether the database
+ // is responding. It is important to choose a query that will return quickly or you risk
+ // overloading the database.
+ //
+ // In most cases this is not necessary, but if you find it necessary, choose a simple query such as 'SELECT 1'.
+ protected string TestQuery { get; }
+
+ protected abstract DbConnection CreateConnection(string connectionString);
+
+ public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ using (var connection = CreateConnection(ConnectionString))
+ {
+ try
+ {
+ await connection.OpenAsync(cancellationToken);
+
+ if (TestQuery != null)
+ {
+ var command = connection.CreateCommand();
+ command.CommandText = TestQuery;
+
+ await command.ExecuteNonQueryAsync(cancellationToken);
+ }
+ }
+ catch (DbException ex)
+ {
+ return new HealthCheckResult(status: context.Registration.FailureStatus, exception: ex);
+ }
+ }
+
+ return HealthCheckResult.Healthy();
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbContextHealthStartup.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbContextHealthStartup.cs
new file mode 100644
index 0000000000..5f04ea0ea0
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/DbContextHealthStartup.cs
@@ -0,0 +1,85 @@
+using System;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace HealthChecksSample
+{
+ // Pass in `--scenario dbcontext` at the command line to run this sample.
+ public class DbContextHealthStartup
+ {
+ public DbContextHealthStartup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Registers required services for health checks
+ services.AddHealthChecks()
+
+ // Registers a health check for the MyContext type. By default the name of the health check will be the
+ // name of the DbContext type. There are other options available through AddDbContextCheck to configure
+ // failure status, tags, and custom test query.
+ .AddDbContextCheck<MyContext>();
+
+ // Registers the MyContext type and configures the database provider.
+ //
+ // The health check added by AddDbContextCheck will create instances of MyContext from the service provider,
+ // and so will reuse the configuration provided here.
+ services.AddDbContext<MyContext>(options =>
+ {
+ options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);
+ });
+ }
+
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ // This will register the health checks middleware at the URL /health.
+ //
+ // Since this sample doesn't do anything to create the database by default, this will
+ // return unhealthy by default.
+ //
+ // You can to to /createdatabase and /deletedatabase to create and delete the database
+ // (respectively), and see how it immediately effects the health status.
+ //
+ app.UseHealthChecks("/health");
+
+ app.Map("/createdatabase", b => b.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Creating the database...\n");
+ await context.Response.Body.FlushAsync();
+
+ var myContext = context.RequestServices.GetRequiredService<MyContext>();
+ await myContext.Database.EnsureCreatedAsync();
+
+ await context.Response.WriteAsync("Done\n");
+ await context.Response.WriteAsync("Go to /health to see the health status\n");
+ }));
+
+ app.Map("/deletedatabase", b => b.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Deleting the database...\n");
+ await context.Response.Body.FlushAsync();
+
+ var myContext = context.RequestServices.GetRequiredService<MyContext>();
+ await myContext.Database.EnsureDeletedAsync();
+
+ await context.Response.WriteAsync("Done\n");
+ await context.Response.WriteAsync("Go to /health to see the health status\n");
+ }));
+
+ app.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Go to /health to see the health status\n");
+ await context.Response.WriteAsync("Go to /createdatabase to create the database\n");
+ await context.Response.WriteAsync("Go to /deletedatabase to delete the database\n");
+ });
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/GCInfoHealthCheck.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/GCInfoHealthCheck.cs
new file mode 100644
index 0000000000..91519af452
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/GCInfoHealthCheck.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Options;
+
+namespace HealthChecksSample
+{
+ // This is an example of a custom health check that implements IHealthCheck.
+ //
+ // This example also shows a technique for authoring a health check that needs to be registered
+ // with additional configuration data. This technique works via named options, and is useful
+ // for authoring health checks that can be disctributed as libraries.
+
+ public static class GCInfoHealthCheckBuilderExtensions
+ {
+ public static IHealthChecksBuilder AddGCInfoCheck(
+ this IHealthChecksBuilder builder,
+ string name,
+ HealthStatus? failureStatus = null,
+ IEnumerable<string> tags = null,
+ long? thresholdInBytes = null)
+ {
+ // Register a check of type GCInfo
+ builder.AddCheck<GCInfoHealthCheck>(name, failureStatus ?? HealthStatus.Degraded, tags);
+
+ // Configure named options to pass the threshold into the check.
+ if (thresholdInBytes.HasValue)
+ {
+ builder.Services.Configure<GCInfoOptions>(name, options =>
+ {
+ options.Threshold = thresholdInBytes.Value;
+ });
+ }
+
+ return builder;
+ }
+ }
+
+ public class GCInfoHealthCheck : IHealthCheck
+ {
+ private readonly IOptionsMonitor<GCInfoOptions> _options;
+
+ public GCInfoHealthCheck(IOptionsMonitor<GCInfoOptions> options)
+ {
+ _options = options;
+ }
+
+ public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var options = _options.Get(context.Registration.Name);
+
+ // This example will report degraded status if the application is using
+ // more than the configured amount of memory (1gb by default).
+ //
+ // Additionally we include some GC info in the reported diagnostics.
+ var allocated = GC.GetTotalMemory(forceFullCollection: false);
+ var data = new Dictionary<string, object>()
+ {
+ { "Allocated", allocated },
+ { "Gen0Collections", GC.CollectionCount(0) },
+ { "Gen1Collections", GC.CollectionCount(1) },
+ { "Gen2Collections", GC.CollectionCount(2) },
+ };
+
+ // Report failure if the allocated memory is >= the threshold.
+ //
+ // Using context.Registration.FailureStatus means that the application developer can configure
+ // how they want failures to appear.
+ var result = allocated >= options.Threshold ? context.Registration.FailureStatus : HealthStatus.Healthy;
+
+ return Task.FromResult(new HealthCheckResult(
+ result,
+ description: "reports degraded status if allocated bytes >= 1gb",
+ data: data));
+ }
+ }
+
+ public class GCInfoOptions
+ {
+ // The failure threshold (in bytes)
+ public long Threshold { get; set; } = 1024L * 1024L * 1024L;
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/HealthChecksSample.csproj b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/HealthChecksSample.csproj
new file mode 100644
index 0000000000..6a8939c3e5
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/HealthChecksSample.csproj
@@ -0,0 +1,22 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <!-- Used in our tests -->
+ <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">netcoreapp2.2;net461</TargetFrameworks>
+ <TargetFrameworks Condition="'$(TargetFrameworks)'==''">netcoreapp2.2</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ <Reference Include="Microsoft.AspNetCore.StaticFiles" />
+ <Reference Include="Microsoft.EntityFrameworkCore.SqlServer" />
+ <Reference Include="Microsoft.Extensions.Configuration.CommandLine" />
+ <Reference Include="Microsoft.Extensions.Configuration.Json" />
+ <Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" />
+ <Reference Include="Microsoft.Extensions.Logging.Console" />
+ <Reference Include="Newtonsoft.Json" />
+ <Reference Include="System.Data.SqlClient" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/LivenessProbeStartup.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/LivenessProbeStartup.cs
new file mode 100644
index 0000000000..82676221ce
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/LivenessProbeStartup.cs
@@ -0,0 +1,68 @@
+using System;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace HealthChecksSample
+{
+ // Pass in `--scenario liveness` at the command line to run this sample.
+ public class LivenessProbeStartup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Registers required services for health checks
+ services
+ .AddHealthChecks()
+ .AddCheck<SlowDependencyHealthCheck>("Slow", failureStatus: null, tags: new[] { "ready", });
+ }
+
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ // This will register the health checks middleware twice:
+ // - at /health/ready for 'readiness'
+ // - at /health/live for 'liveness'
+ //
+ // Using a separate liveness and readiness check is useful in an environment like Kubernetes
+ // when an application needs to do significant work before accepting requests. Using separate
+ // checks allows the orchestrator to distinguish whether the application is functioning but
+ // not yet ready or if the application has failed to start.
+ //
+ // For instance the liveness check will do a quick set of checks to determine if the process
+ // is functioning correctly.
+ //
+ // The readiness check might do a set of more expensive or time-consuming checks to determine
+ // if all other resources are responding.
+ //
+ // See https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/ for
+ // more details about readiness and liveness probes in Kubernetes.
+ //
+ // In this example, the liveness check will us an 'identity' check that always returns healthy.
+ //
+ // In this example, the readiness check will run all registered checks, include a check with a
+ // long initialization time (15 seconds).
+
+
+ // The readiness check uses all registered checks with the 'ready' tag.
+ app.UseHealthChecks("/health/ready", new HealthCheckOptions()
+ {
+ Predicate = (check) => check.Tags.Contains("ready"),
+ });
+
+ // The liveness filters out all checks and just returns success
+ app.UseHealthChecks("/health/live", new HealthCheckOptions()
+ {
+ // Exclude all checks, just return a 200.
+ Predicate = (check) => false,
+ });
+
+ app.Run(async (context) =>
+ {
+ await context.Response.WriteAsync("Go to /health/ready to see the readiness status");
+ await context.Response.WriteAsync(Environment.NewLine);
+ await context.Response.WriteAsync("Go to /health/live to see the liveness status");
+ });
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/ManagementPortStartup.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/ManagementPortStartup.cs
new file mode 100644
index 0000000000..79b38dc875
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/ManagementPortStartup.cs
@@ -0,0 +1,47 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace HealthChecksSample
+{
+ // Pass in `--scenario port` at the command line to run this sample.
+ public class ManagementPortStartup
+ {
+ public ManagementPortStartup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // Registers required services for health checks
+ services.AddHealthChecks();
+ }
+
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ // This will register the health checks middleware at the URL /health but only on the specified port.
+ //
+ // By default health checks will return a 200 with 'Healthy'.
+ // - No health checks are registered by default, the app is healthy if it is reachable
+ // - The default response writer writes the HealthCheckStatus as text/plain content
+ //
+ // Use UseHealthChecks with a port will only process health checks requests on connection
+ // to the specified port. This is typically used in a container environment where you can expose
+ // a port for monitoring services to have access to the service.
+ // - In this case the management is configured in the launchSettings.json and passed through
+ // an environment variable
+ // - Additionally, the server is also configured to listen to requests on the management port.
+ app.UseHealthChecks("/health", port: Configuration["ManagementPort"]);
+
+ app.Run(async (context) =>
+ {
+ await context.Response.WriteAsync($"Go to http://localhost:{Configuration["ManagementPort"]}/health to see the health status");
+ });
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/MyContext.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/MyContext.cs
new file mode 100644
index 0000000000..1cfbeaab25
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/MyContext.cs
@@ -0,0 +1,20 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace HealthChecksSample
+{
+ public class MyContext : DbContext
+ {
+ public MyContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ public DbSet<Blog> Blog { get; set; }
+ }
+
+ public class Blog
+ {
+ public int BlogId { get; set; }
+ public string Url { get; set; }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Program.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Program.cs
new file mode 100644
index 0000000000..56e6e6478f
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Program.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace HealthChecksSample
+{
+ public class Program
+ {
+ private static readonly Dictionary<string, Type> _scenarios;
+
+ static Program()
+ {
+ _scenarios = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
+ {
+ { "", typeof(BasicStartup) },
+ { "basic", typeof(BasicStartup) },
+ { "writer", typeof(CustomWriterStartup) },
+ { "liveness", typeof(LivenessProbeStartup) },
+ { "port", typeof(ManagementPortStartup) },
+ { "db", typeof(DbHealthStartup) },
+ { "dbcontext", typeof(DbContextHealthStartup) },
+ };
+ }
+
+ public static void Main(string[] args)
+ {
+ BuildWebHost(args).Run();
+ }
+
+ public static IWebHost BuildWebHost(string[] args)
+ {
+ var config = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json")
+ .AddEnvironmentVariables(prefix: "ASPNETCORE_")
+ .AddCommandLine(args)
+ .Build();
+
+ var scenario = config["scenario"] ?? string.Empty;
+ if (!_scenarios.TryGetValue(scenario, out var startupType))
+ {
+ startupType = typeof(BasicStartup);
+ }
+
+ return new WebHostBuilder()
+ .UseConfiguration(config)
+ .ConfigureLogging(builder =>
+ {
+ builder.SetMinimumLevel(LogLevel.Trace);
+ builder.AddConfiguration(config);
+ builder.AddConsole();
+ })
+ .UseKestrel()
+ .UseStartup(startupType)
+ .Build();
+ }
+
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Properties/launchSettings.json b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Properties/launchSettings.json
new file mode 100644
index 0000000000..6afb19b2b3
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/Properties/launchSettings.json
@@ -0,0 +1,15 @@
+{
+ "profiles": {
+ "HealthChecksSample": {
+ "commandName": "Project",
+ "commandLineArgs": "",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:5000/;http://localhost:5001/",
+ "ASPNETCORE_MANAGEMENTPORT": "5001"
+ },
+ "applicationUrl": "http://localhost:5000/"
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SlowDependencyHealthCheck.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SlowDependencyHealthCheck.cs
new file mode 100644
index 0000000000..e14aeb210c
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SlowDependencyHealthCheck.cs
@@ -0,0 +1,32 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace HealthChecksSample
+{
+ // Simulates a health check for an application dependency that takes a while to initialize.
+ // This is part of the readiness/liveness probe sample.
+ public class SlowDependencyHealthCheck : IHealthCheck
+ {
+ public static readonly string HealthCheckName = "slow_dependency";
+
+ private readonly Task _task;
+
+ public SlowDependencyHealthCheck()
+ {
+ _task = Task.Delay(15 * 1000);
+ }
+
+ public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ if (_task.IsCompleted)
+ {
+ return Task.FromResult(HealthCheckResult.Healthy("Dependency is ready"));
+ }
+
+ return Task.FromResult(new HealthCheckResult(
+ status: context.Registration.FailureStatus,
+ description: "Dependency is still initializing"));
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SqlConnectionHealthCheck.cs b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SqlConnectionHealthCheck.cs
new file mode 100644
index 0000000000..fcc135ad1e
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/SqlConnectionHealthCheck.cs
@@ -0,0 +1,25 @@
+using System.Data.Common;
+using System.Data.SqlClient;
+
+namespace HealthChecksSample
+{
+ public class SqlConnectionHealthCheck : DbConnectionHealthCheck
+ {
+ private static readonly string DefaultTestQuery = "Select 1";
+
+ public SqlConnectionHealthCheck(string connectionString)
+ : this(connectionString, testQuery: DefaultTestQuery)
+ {
+ }
+
+ public SqlConnectionHealthCheck(string connectionString, string testQuery)
+ : base(connectionString, testQuery ?? DefaultTestQuery)
+ {
+ }
+
+ protected override DbConnection CreateConnection(string connectionString)
+ {
+ return new SqlConnection(connectionString);
+ }
+ }
+}
diff --git a/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/appsettings.json b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/appsettings.json
new file mode 100644
index 0000000000..21b2dcdbfd
--- /dev/null
+++ b/src/Middleware/HealthChecks/test/testassets/HealthChecksSample/appsettings.json
@@ -0,0 +1,13 @@
+{
+ "ConnectionStrings": {
+ "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=HealthCheckSample;Trusted_Connection=True;MultipleActiveResultSets=true;ConnectRetryCount=0"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug"
+ },
+ "Console": {
+ "IncludeScopes": "true"
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/Middleware.sln b/src/Middleware/Middleware.sln
index 28a99b57c6..c909934ca9 100644
--- a/src/Middleware/Middleware.sln
+++ b/src/Middleware/Middleware.sln
@@ -19,6 +19,72 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSoc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets.Tests", "WebSockets\test\UnitTests\Microsoft.AspNetCore.WebSockets.Tests.csproj", "{93970702-1BDB-4A8C-B7F6-020294464BB6}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics", "Diagnostics", "{BDA541A2-1C14-4E71-85C1-D970828453C0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics", "Diagnostics\src\Microsoft.AspNetCore.Diagnostics.csproj", "{D84EFF9A-8CB9-411C-A7E3-43AF9043221E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2C971E2A-540D-4026-ABE9-519656E9485E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diagnostics.FunctionalTests", "Diagnostics\test\FunctionalTests\Diagnostics.FunctionalTests.csproj", "{F7730746-125D-40D8-AA3D-FA9736121D44}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{48F74384-B082-4210-9F00-BA62830621DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibraryWithPortablePdbs", "Diagnostics\test\testassets\ClassLibraryWithPortablePdbs\ClassLibraryWithPortablePdbs.csproj", "{D301185E-BEEA-4CC7-95C8-5C07F330F977}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DatabaseErrorPageSample", "Diagnostics\test\testassets\DatabaseErrorPageSample\DatabaseErrorPageSample.csproj", "{7C6C524E-6C7A-4579-B984-7217E9A0E2B8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeveloperExceptionPageSample", "Diagnostics\test\testassets\DeveloperExceptionPageSample\DeveloperExceptionPageSample.csproj", "{16FD80C3-D80D-4B46-92A8-6C43A8E54A46}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExceptionHandlerSample", "Diagnostics\test\testassets\ExceptionHandlerSample\ExceptionHandlerSample.csproj", "{112AE83E-595C-4343-8205-8CD0BE4F7562}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatusCodePagesSample", "Diagnostics\test\testassets\StatusCodePagesSample\StatusCodePagesSample.csproj", "{28EE1B22-E57E-4006-8017-F36348245F08}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WelcomePageSample", "Diagnostics\test\testassets\WelcomePageSample\WelcomePageSample.csproj", "{1709D0A6-D597-44F2-9657-873368066795}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics.Tests", "Diagnostics\test\UnitTests\Microsoft.AspNetCore.Diagnostics.Tests.csproj", "{70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics.Abstractions", "Diagnostics.Abstractions", "{3E557C8B-504F-40C1-9ED5-03E5F4A05FB4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics.Abstractions", "Diagnostics.Abstractions\src\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj", "{F8BB4CDB-766A-4223-8699-3157CE8FDBDC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Diagnostics.EntityFrameworkCore", "Diagnostics.EntityFrameworkCore", "{7FB67FE0-AC08-4C33-9904-8D33CE4D84F7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore", "Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj", "{DFF52006-A8FC-478E-A781-12255391C4A4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{26AE655E-C5B9-49AE-ACDB-92B429CC9582}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diagnostics.EFCore.FunctionalTests", "Diagnostics.EntityFrameworkCore\test\FunctionalTests\Diagnostics.EFCore.FunctionalTests.csproj", "{76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests", "Diagnostics.EntityFrameworkCore\test\UnitTests\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Tests.csproj", "{8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MiddlewareAnalysis", "MiddlewareAnalysis", "{91CFB5DE-CE68-4156-9153-6C597D9F524E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{8989BEC1-6D0A-4E11-A09C-B31FD6222748}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiddlewareAnalysisSample", "MiddlewareAnalysis\samples\MiddlewareAnalysisSample\MiddlewareAnalysisSample.csproj", "{18F82832-9164-434E-BAEF-6579B3CFCDF3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.MiddlewareAnalysis", "MiddlewareAnalysis\src\Microsoft.AspNetCore.MiddlewareAnalysis.csproj", "{7E00616F-9D5C-4318-99CB-8F6ECFA82515}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.MiddlewareAnalysis.Tests", "MiddlewareAnalysis\test\Microsoft.AspNetCore.MiddlewareAnalysis.Tests.csproj", "{009E3DE7-AFC7-4C66-852D-29BF73257176}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks", "HealthChecks", "{C5393E56-AA5F-46BB-A552-ECFE2C92C83F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics.HealthChecks", "HealthChecks\src\Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj", "{E4D35034-A705-4013-AC0F-40077B5F52D2}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{1859297F-585A-4B2E-A08C-35F9FDE6E38A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{2E8C6990-DD0F-4C1E-9DF4-D99D42A67582}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecksSample", "HealthChecks\test\testassets\HealthChecksSample\HealthChecksSample.csproj", "{12943699-52C8-498E-AA23-2C6BF22F2F74}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests", "HealthChecks\test\UnitTests\Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj", "{3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HealthChecks.EntityFrameworkCore", "HealthChecks.EntityFrameworkCore", "{B1E18419-B9E8-4FF1-8A48-012FC01A9E3E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore", "HealthChecks.EntityFrameworkCore\src\Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.csproj", "{DA051189-4133-4131-A8B4-0F63EE8552D0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests", "HealthChecks.EntityFrameworkCore\test\Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore.Tests.csproj", "{7553B6FF-9EA0-4BC2-A720-783685CB2F8D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -101,6 +167,258 @@ Global
{93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|x64.Build.0 = Release|Any CPU
{93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|x86.ActiveCfg = Release|Any CPU
{93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|x86.Build.0 = Release|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Debug|x64.Build.0 = Debug|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Debug|x86.Build.0 = Debug|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Release|x64.ActiveCfg = Release|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Release|x64.Build.0 = Release|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Release|x86.ActiveCfg = Release|Any CPU
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E}.Release|x86.Build.0 = Release|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Debug|x64.Build.0 = Debug|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Debug|x86.Build.0 = Debug|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Release|x64.ActiveCfg = Release|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Release|x64.Build.0 = Release|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Release|x86.ActiveCfg = Release|Any CPU
+ {F7730746-125D-40D8-AA3D-FA9736121D44}.Release|x86.Build.0 = Release|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Debug|x64.Build.0 = Debug|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Debug|x86.Build.0 = Debug|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Release|x64.ActiveCfg = Release|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Release|x64.Build.0 = Release|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Release|x86.ActiveCfg = Release|Any CPU
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977}.Release|x86.Build.0 = Release|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Debug|x64.Build.0 = Debug|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Debug|x86.Build.0 = Debug|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Release|x64.ActiveCfg = Release|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Release|x64.Build.0 = Release|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Release|x86.ActiveCfg = Release|Any CPU
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8}.Release|x86.Build.0 = Release|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Debug|x64.Build.0 = Debug|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Debug|x86.Build.0 = Debug|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Release|Any CPU.Build.0 = Release|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Release|x64.ActiveCfg = Release|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Release|x64.Build.0 = Release|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Release|x86.ActiveCfg = Release|Any CPU
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46}.Release|x86.Build.0 = Release|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Debug|x64.Build.0 = Debug|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Debug|x86.Build.0 = Debug|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Release|Any CPU.Build.0 = Release|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Release|x64.ActiveCfg = Release|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Release|x64.Build.0 = Release|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Release|x86.ActiveCfg = Release|Any CPU
+ {112AE83E-595C-4343-8205-8CD0BE4F7562}.Release|x86.Build.0 = Release|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Debug|x64.Build.0 = Debug|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Debug|x86.Build.0 = Debug|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Release|Any CPU.Build.0 = Release|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Release|x64.ActiveCfg = Release|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Release|x64.Build.0 = Release|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Release|x86.ActiveCfg = Release|Any CPU
+ {28EE1B22-E57E-4006-8017-F36348245F08}.Release|x86.Build.0 = Release|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Debug|x64.Build.0 = Debug|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Debug|x86.Build.0 = Debug|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Release|x64.ActiveCfg = Release|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Release|x64.Build.0 = Release|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Release|x86.ActiveCfg = Release|Any CPU
+ {1709D0A6-D597-44F2-9657-873368066795}.Release|x86.Build.0 = Release|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Debug|x64.Build.0 = Debug|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Debug|x86.Build.0 = Debug|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Release|Any CPU.Build.0 = Release|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Release|x64.ActiveCfg = Release|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Release|x64.Build.0 = Release|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Release|x86.ActiveCfg = Release|Any CPU
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43}.Release|x86.Build.0 = Release|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Debug|x64.Build.0 = Debug|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Debug|x86.Build.0 = Debug|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Release|x64.ActiveCfg = Release|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Release|x64.Build.0 = Release|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Release|x86.ActiveCfg = Release|Any CPU
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC}.Release|x86.Build.0 = Release|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Debug|x64.Build.0 = Debug|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Debug|x86.Build.0 = Debug|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Release|x64.ActiveCfg = Release|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Release|x64.Build.0 = Release|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Release|x86.ActiveCfg = Release|Any CPU
+ {DFF52006-A8FC-478E-A781-12255391C4A4}.Release|x86.Build.0 = Release|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Debug|x64.Build.0 = Debug|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Debug|x86.Build.0 = Debug|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Release|Any CPU.Build.0 = Release|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Release|x64.ActiveCfg = Release|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Release|x64.Build.0 = Release|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Release|x86.ActiveCfg = Release|Any CPU
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81}.Release|x86.Build.0 = Release|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Debug|x64.Build.0 = Debug|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Debug|x86.Build.0 = Debug|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Release|x64.ActiveCfg = Release|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Release|x64.Build.0 = Release|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Release|x86.ActiveCfg = Release|Any CPU
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4}.Release|x86.Build.0 = Release|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Debug|x64.Build.0 = Debug|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Debug|x86.Build.0 = Debug|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Release|x64.ActiveCfg = Release|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Release|x64.Build.0 = Release|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Release|x86.ActiveCfg = Release|Any CPU
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3}.Release|x86.Build.0 = Release|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Debug|x64.Build.0 = Debug|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Debug|x86.Build.0 = Debug|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Release|x64.ActiveCfg = Release|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Release|x64.Build.0 = Release|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Release|x86.ActiveCfg = Release|Any CPU
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515}.Release|x86.Build.0 = Release|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Debug|x64.Build.0 = Debug|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Debug|x86.Build.0 = Debug|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Release|Any CPU.Build.0 = Release|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Release|x64.ActiveCfg = Release|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Release|x64.Build.0 = Release|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Release|x86.ActiveCfg = Release|Any CPU
+ {009E3DE7-AFC7-4C66-852D-29BF73257176}.Release|x86.Build.0 = Release|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Debug|x64.Build.0 = Debug|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Debug|x86.Build.0 = Debug|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Release|x64.ActiveCfg = Release|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Release|x64.Build.0 = Release|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Release|x86.ActiveCfg = Release|Any CPU
+ {E4D35034-A705-4013-AC0F-40077B5F52D2}.Release|x86.Build.0 = Release|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Debug|x64.Build.0 = Debug|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Debug|x86.Build.0 = Debug|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Release|Any CPU.Build.0 = Release|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Release|x64.ActiveCfg = Release|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Release|x64.Build.0 = Release|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Release|x86.ActiveCfg = Release|Any CPU
+ {12943699-52C8-498E-AA23-2C6BF22F2F74}.Release|x86.Build.0 = Release|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Debug|x64.Build.0 = Debug|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Debug|x86.Build.0 = Debug|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Release|x64.ActiveCfg = Release|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Release|x64.Build.0 = Release|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Release|x86.ActiveCfg = Release|Any CPU
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F}.Release|x86.Build.0 = Release|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Debug|x64.Build.0 = Debug|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Debug|x86.Build.0 = Debug|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Release|x64.ActiveCfg = Release|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Release|x64.Build.0 = Release|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Release|x86.ActiveCfg = Release|Any CPU
+ {DA051189-4133-4131-A8B4-0F63EE8552D0}.Release|x86.Build.0 = Release|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Debug|x64.Build.0 = Debug|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Debug|x86.Build.0 = Debug|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Release|x64.ActiveCfg = Release|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Release|x64.Build.0 = Release|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Release|x86.ActiveCfg = Release|Any CPU
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -113,6 +431,33 @@ Global
{76B25812-AAFB-45BA-A71A-24F0C654ADFB} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432}
{88BDEE69-4DE3-40B5-A478-677EA355FB52} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432}
{93970702-1BDB-4A8C-B7F6-020294464BB6} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432}
+ {D84EFF9A-8CB9-411C-A7E3-43AF9043221E} = {BDA541A2-1C14-4E71-85C1-D970828453C0}
+ {2C971E2A-540D-4026-ABE9-519656E9485E} = {BDA541A2-1C14-4E71-85C1-D970828453C0}
+ {F7730746-125D-40D8-AA3D-FA9736121D44} = {2C971E2A-540D-4026-ABE9-519656E9485E}
+ {48F74384-B082-4210-9F00-BA62830621DF} = {2C971E2A-540D-4026-ABE9-519656E9485E}
+ {D301185E-BEEA-4CC7-95C8-5C07F330F977} = {48F74384-B082-4210-9F00-BA62830621DF}
+ {7C6C524E-6C7A-4579-B984-7217E9A0E2B8} = {48F74384-B082-4210-9F00-BA62830621DF}
+ {16FD80C3-D80D-4B46-92A8-6C43A8E54A46} = {48F74384-B082-4210-9F00-BA62830621DF}
+ {112AE83E-595C-4343-8205-8CD0BE4F7562} = {48F74384-B082-4210-9F00-BA62830621DF}
+ {28EE1B22-E57E-4006-8017-F36348245F08} = {48F74384-B082-4210-9F00-BA62830621DF}
+ {1709D0A6-D597-44F2-9657-873368066795} = {48F74384-B082-4210-9F00-BA62830621DF}
+ {70A402DC-5DEF-47B1-988F-9A0E7F2CAC43} = {2C971E2A-540D-4026-ABE9-519656E9485E}
+ {F8BB4CDB-766A-4223-8699-3157CE8FDBDC} = {3E557C8B-504F-40C1-9ED5-03E5F4A05FB4}
+ {DFF52006-A8FC-478E-A781-12255391C4A4} = {7FB67FE0-AC08-4C33-9904-8D33CE4D84F7}
+ {26AE655E-C5B9-49AE-ACDB-92B429CC9582} = {7FB67FE0-AC08-4C33-9904-8D33CE4D84F7}
+ {76DBAB75-DFAB-4676-BECA-B3E3F4A19E81} = {26AE655E-C5B9-49AE-ACDB-92B429CC9582}
+ {8D4E57C5-7A34-492C-AF22-BBAC6C06AEB4} = {26AE655E-C5B9-49AE-ACDB-92B429CC9582}
+ {8989BEC1-6D0A-4E11-A09C-B31FD6222748} = {91CFB5DE-CE68-4156-9153-6C597D9F524E}
+ {18F82832-9164-434E-BAEF-6579B3CFCDF3} = {8989BEC1-6D0A-4E11-A09C-B31FD6222748}
+ {7E00616F-9D5C-4318-99CB-8F6ECFA82515} = {91CFB5DE-CE68-4156-9153-6C597D9F524E}
+ {009E3DE7-AFC7-4C66-852D-29BF73257176} = {91CFB5DE-CE68-4156-9153-6C597D9F524E}
+ {E4D35034-A705-4013-AC0F-40077B5F52D2} = {C5393E56-AA5F-46BB-A552-ECFE2C92C83F}
+ {1859297F-585A-4B2E-A08C-35F9FDE6E38A} = {C5393E56-AA5F-46BB-A552-ECFE2C92C83F}
+ {2E8C6990-DD0F-4C1E-9DF4-D99D42A67582} = {1859297F-585A-4B2E-A08C-35F9FDE6E38A}
+ {12943699-52C8-498E-AA23-2C6BF22F2F74} = {2E8C6990-DD0F-4C1E-9DF4-D99D42A67582}
+ {3F1A1463-7951-48AC-85D6-B1CDBBBBE50F} = {1859297F-585A-4B2E-A08C-35F9FDE6E38A}
+ {DA051189-4133-4131-A8B4-0F63EE8552D0} = {B1E18419-B9E8-4FF1-8A48-012FC01A9E3E}
+ {7553B6FF-9EA0-4BC2-A720-783685CB2F8D} = {B1E18419-B9E8-4FF1-8A48-012FC01A9E3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {83786312-A93B-4BB4-AB06-7C6913A59AFA}
diff --git a/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.csproj b/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.csproj
new file mode 100644
index 0000000000..18875ae57d
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/MiddlewareAnalysisSample.csproj
@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFrameworks>net461;netcoreapp2.2</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+ <Reference Include="Microsoft.AspNetCore.MiddlewareAnalysis" />
+ <Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
+ <Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
+ <Reference Include="Microsoft.Extensions.DiagnosticAdapter" />
+ <Reference Include="Microsoft.Extensions.Logging.Console" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/Startup.cs b/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/Startup.cs
new file mode 100644
index 0000000000..8f8d950a47
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/Startup.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Diagnostics;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DiagnosticAdapter;
+using Microsoft.Extensions.Logging;
+
+namespace MiddlewareAnaysisSample
+{
+ public class Startup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddMiddlewareAnalysis();
+ }
+
+ public void Configure(IApplicationBuilder app, ILoggerFactory factory, DiagnosticListener diagnosticListener)
+ {
+ // Listen for middleware events and log them to the console.
+ var listener = new TestDiagnosticListener();
+ diagnosticListener.SubscribeWithAdapter(listener);
+
+ // Build our application pipeline
+
+ // Named via app.UseMiddleware<T>()
+ app.UseDeveloperExceptionPage();
+
+ // Anonymous method inline middleware
+ app.Use((context, next) =>
+ {
+ // No-op
+ return next();
+ });
+
+ app.Map("/map", subApp =>
+ {
+ subApp.Run(context =>
+ {
+ return context.Response.WriteAsync("Hello World");
+ });
+ });
+
+ // Low level anonymous method inline middleware, named Diagnostics.Middleware.Analysis.Startup+<>c by default
+ app.Use(next =>
+ {
+ return context =>
+ {
+ return next(context);
+ };
+ });
+
+ app.Map("/throw", throwApp =>
+ {
+ throwApp.Run(context => { throw new Exception("Application Exception"); });
+ });
+
+ // The home page.
+ app.Properties["analysis.NextMiddlewareName"] = "HomePage";
+ app.Use(async (context, next) =>
+ {
+ if (context.Request.Path == "/")
+ {
+ context.Response.ContentType = "text/html";
+ await context.Response.WriteAsync("<html><body>Welcome to the sample<br><br>\r\n");
+ await context.Response.WriteAsync("Click here to take a side branch: <a href=\"/map/foo\">Map</a><br>\r\n");
+ await context.Response.WriteAsync("Click here to throw an exception: <a href=\"/throw\">Throw</a><br>\r\n");
+ await context.Response.WriteAsync("Click here to for a 404: <a href=\"/404\">404</a><br>\r\n");
+ await context.Response.WriteAsync("</body></html>\r\n");
+ return;
+ }
+ else
+ {
+ await next();
+ }
+ });
+
+ // Note there's always a default 404 middleware at the end of the pipeline.
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .ConfigureLogging((_, factory) =>
+ {
+ factory.AddConsole();
+ factory.AddFilter("Console", level => level >= LogLevel.Debug);
+ })
+ .UseKestrel()
+ .UseIISIntegration()
+ .UseStartup<Startup>()
+ .Build();
+
+ host.Run();
+ }
+
+ public class TestDiagnosticListener
+ {
+ [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
+ public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
+ {
+ Console.WriteLine($"MiddlewareStarting: {name}; {httpContext.Request.Path}");
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException")]
+ public virtual void OnMiddlewareException(Exception exception, string name)
+ {
+ Console.WriteLine($"MiddlewareException: {name}; {exception.Message}");
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareFinished")]
+ public virtual void OnMiddlewareFinished(HttpContext httpContext, string name)
+ {
+ Console.WriteLine($"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}");
+ }
+ }
+ }
+}
+
diff --git a/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/web.config b/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/web.config
new file mode 100644
index 0000000000..f7ac679334
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/samples/MiddlewareAnalysisSample/web.config
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<configuration>
+ <system.webServer>
+ <handlers>
+ <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
+ </handlers>
+ <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
+ </system.webServer>
+</configuration> \ No newline at end of file
diff --git a/src/Middleware/MiddlewareAnalysis/src/AnalysisBuilder.cs b/src/Middleware/MiddlewareAnalysis/src/AnalysisBuilder.cs
new file mode 100644
index 0000000000..f26ca9926a
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/src/AnalysisBuilder.cs
@@ -0,0 +1,65 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+
+namespace Microsoft.AspNetCore.MiddlewareAnalysis
+{
+ public class AnalysisBuilder : IApplicationBuilder
+ {
+ private const string NextMiddlewareName = "analysis.NextMiddlewareName";
+
+ public AnalysisBuilder(IApplicationBuilder inner)
+ {
+ InnerBuilder = inner;
+ }
+
+ private IApplicationBuilder InnerBuilder { get; }
+
+ public IServiceProvider ApplicationServices
+ {
+ get { return InnerBuilder.ApplicationServices; }
+ set { InnerBuilder.ApplicationServices = value; }
+ }
+
+ public IDictionary<string, object> Properties
+ {
+ get { return InnerBuilder.Properties; }
+ }
+
+ public IFeatureCollection ServerFeatures
+ {
+ get { return InnerBuilder.ServerFeatures;}
+ }
+
+ public RequestDelegate Build()
+ {
+ // Add one maker at the end before the default 404 middleware (or any fancy Join middleware).
+ return InnerBuilder.UseMiddleware<AnalysisMiddleware>("EndOfPipeline")
+ .Build();
+ }
+
+ public IApplicationBuilder New()
+ {
+ return new AnalysisBuilder(InnerBuilder.New());
+ }
+
+ public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
+ {
+ string middlewareName = string.Empty; // UseMiddleware doesn't work with null params.
+ object middlewareNameObj;
+ if (Properties.TryGetValue(NextMiddlewareName, out middlewareNameObj))
+ {
+ middlewareName = middlewareNameObj?.ToString();
+ Properties.Remove(NextMiddlewareName);
+ }
+
+ return InnerBuilder.UseMiddleware<AnalysisMiddleware>(middlewareName)
+ .Use(middleware);
+ }
+ }
+}
diff --git a/src/Middleware/MiddlewareAnalysis/src/AnalysisMiddleware.cs b/src/Middleware/MiddlewareAnalysis/src/AnalysisMiddleware.cs
new file mode 100644
index 0000000000..081f64f0ce
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/src/AnalysisMiddleware.cs
@@ -0,0 +1,85 @@
+// 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.Diagnostics;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.MiddlewareAnalysis
+{
+ public class AnalysisMiddleware
+ {
+ private readonly Guid _instanceId = Guid.NewGuid();
+ private readonly RequestDelegate _next;
+ private readonly DiagnosticSource _diagnostics;
+ private readonly string _middlewareName;
+
+ public AnalysisMiddleware(RequestDelegate next, DiagnosticSource diagnosticSource, string middlewareName)
+ {
+ _next = next;
+ _diagnostics = diagnosticSource;
+ if (string.IsNullOrEmpty(middlewareName))
+ {
+ middlewareName = next.Target.GetType().FullName;
+ }
+ _middlewareName = middlewareName;
+ }
+
+ public async Task Invoke(HttpContext httpContext)
+ {
+ var startTimestamp = Stopwatch.GetTimestamp();
+ if (_diagnostics.IsEnabled("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting"))
+ {
+ _diagnostics.Write(
+ "Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting",
+ new
+ {
+ name = _middlewareName,
+ httpContext = httpContext,
+ instanceId = _instanceId,
+ timestamp = startTimestamp,
+ });
+ }
+
+ try
+ {
+ await _next(httpContext);
+
+ if (_diagnostics.IsEnabled("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareFinished"))
+ {
+ var currentTimestamp = Stopwatch.GetTimestamp();
+ _diagnostics.Write(
+ "Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareFinished",
+ new
+ {
+ name = _middlewareName,
+ httpContext = httpContext,
+ instanceId = _instanceId,
+ timestamp = currentTimestamp,
+ duration = currentTimestamp - startTimestamp,
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ if (_diagnostics.IsEnabled("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException"))
+ {
+ var currentTimestamp = Stopwatch.GetTimestamp();
+ _diagnostics.Write(
+ "Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException",
+ new
+ {
+ name = _middlewareName,
+ httpContext = httpContext,
+ instanceId = _instanceId,
+ timestamp = currentTimestamp,
+ duration = currentTimestamp - startTimestamp,
+ exception = ex,
+ });
+ }
+ throw;
+ }
+ }
+ }
+}
diff --git a/src/Middleware/MiddlewareAnalysis/src/AnalysisServiceCollectionExtensions.cs b/src/Middleware/MiddlewareAnalysis/src/AnalysisServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..c86020bc7d
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/src/AnalysisServiceCollectionExtensions.cs
@@ -0,0 +1,34 @@
+// 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 Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.MiddlewareAnalysis;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ /// <summary>
+ /// Extension methods for setting up diagnostic services in an <see cref="IServiceCollection" />.
+ /// </summary>
+ public static class AnalysisServiceCollectionExtensions
+ {
+ /// <summary>
+ /// Adds diagnostic services to the specified <see cref="IServiceCollection" />.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
+ /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
+ public static IServiceCollection AddMiddlewareAnalysis(this IServiceCollection services)
+ {
+ if (services == null)
+ {
+ throw new ArgumentNullException(nameof(services));
+ }
+
+ // Prevent registering the same implementation of IStartupFilter (AnalysisStartupFilter) multiple times.
+ // But allow multiple registrations of different implementation types.
+ services.TryAddEnumerable(ServiceDescriptor.Transient<IStartupFilter, AnalysisStartupFilter>());
+ return services;
+ }
+ }
+}
diff --git a/src/Middleware/MiddlewareAnalysis/src/AnalysisStartupFilter.cs b/src/Middleware/MiddlewareAnalysis/src/AnalysisStartupFilter.cs
new file mode 100644
index 0000000000..ffce35a667
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/src/AnalysisStartupFilter.cs
@@ -0,0 +1,27 @@
+// 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+
+namespace Microsoft.AspNetCore.MiddlewareAnalysis
+{
+ public class AnalysisStartupFilter : IStartupFilter
+ {
+ public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
+ {
+ return builder =>
+ {
+ var wrappedBuilder = new AnalysisBuilder(builder);
+ next(wrappedBuilder);
+
+ // The caller doesn't call build on our new builder, they call it on the original. Add this
+ // default middleware to the end. Compare with AnalysisBuilder.Build();
+
+ // Add one maker at the end before the default 404 middleware (or any fancy Join middleware).
+ builder.UseMiddleware<AnalysisMiddleware>("EndOfPipeline");
+ };
+ }
+ }
+}
diff --git a/src/Middleware/MiddlewareAnalysis/src/Microsoft.AspNetCore.MiddlewareAnalysis.csproj b/src/Middleware/MiddlewareAnalysis/src/Microsoft.AspNetCore.MiddlewareAnalysis.csproj
new file mode 100644
index 0000000000..5f6ec94f46
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/src/Microsoft.AspNetCore.MiddlewareAnalysis.csproj
@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>ASP.NET Core middleware for analyzing middleware in the request pipeline with System.Diagnostics.DiagnosticSource.</Description>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <NoWarn>$(NoWarn);CS1591</NoWarn>
+ <GenerateDocumentationFile>true</GenerateDocumentationFile>
+ <PackageTags>aspnetcore;diagnostics</PackageTags>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
+ <Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
+ <Reference Include="System.Diagnostics.DiagnosticSource" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/MiddlewareAnalysis/src/baseline.netcore.json b/src/Middleware/MiddlewareAnalysis/src/baseline.netcore.json
new file mode 100644
index 0000000000..fe220573ae
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/src/baseline.netcore.json
@@ -0,0 +1,218 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.MiddlewareAnalysis, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.Extensions.DependencyInjection.AnalysisServiceCollectionExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "AddMiddlewareAnalysis",
+ "Parameters": [
+ {
+ "Name": "services",
+ "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection"
+ }
+ ],
+ "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisBuilder",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_ApplicationServices",
+ "Parameters": [],
+ "ReturnType": "System.IServiceProvider",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_ApplicationServices",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.IServiceProvider"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_Properties",
+ "Parameters": [],
+ "ReturnType": "System.Collections.Generic.IDictionary<System.String, System.Object>",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_ServerFeatures",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.Features.IFeatureCollection",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Build",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Http.RequestDelegate",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "New",
+ "Parameters": [],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Use",
+ "Parameters": [
+ {
+ "Name": "middleware",
+ "Type": "System.Func<Microsoft.AspNetCore.Http.RequestDelegate, Microsoft.AspNetCore.Http.RequestDelegate>"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "inner",
+ "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisMiddleware",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Invoke",
+ "Parameters": [
+ {
+ "Name": "httpContext",
+ "Type": "Microsoft.AspNetCore.Http.HttpContext"
+ }
+ ],
+ "ReturnType": "System.Threading.Tasks.Task",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "Microsoft.AspNetCore.Http.RequestDelegate"
+ },
+ {
+ "Name": "diagnosticSource",
+ "Type": "System.Diagnostics.DiagnosticSource"
+ },
+ {
+ "Name": "middlewareName",
+ "Type": "System.String"
+ }
+ ],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.AspNetCore.MiddlewareAnalysis.AnalysisStartupFilter",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Hosting.IStartupFilter"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Configure",
+ "Parameters": [
+ {
+ "Name": "next",
+ "Type": "System.Action<Microsoft.AspNetCore.Builder.IApplicationBuilder>"
+ }
+ ],
+ "ReturnType": "System.Action<Microsoft.AspNetCore.Builder.IApplicationBuilder>",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Hosting.IStartupFilter",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Middleware/MiddlewareAnalysis/test/Microsoft.AspNetCore.MiddlewareAnalysis.Tests.csproj b/src/Middleware/MiddlewareAnalysis/test/Microsoft.AspNetCore.MiddlewareAnalysis.Tests.csproj
new file mode 100644
index 0000000000..1c74cf4f78
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/test/Microsoft.AspNetCore.MiddlewareAnalysis.Tests.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Diagnostics" />
+ <Reference Include="Microsoft.AspNetCore.MiddlewareAnalysis" />
+ <Reference Include="Microsoft.AspNetCore.TestHost" />
+ <Reference Include="Microsoft.Extensions.DependencyInjection" />
+ <Reference Include="Microsoft.Extensions.DiagnosticAdapter" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/MiddlewareAnalysis/test/MiddlewareAnalysisTests.cs b/src/Middleware/MiddlewareAnalysis/test/MiddlewareAnalysisTests.cs
new file mode 100644
index 0000000000..64d14ad492
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/test/MiddlewareAnalysisTests.cs
@@ -0,0 +1,53 @@
+// 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.Diagnostics;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace Microsoft.AspNetCore.MiddlewareAnalysis
+{
+ public class MiddlewareAnalysisTests
+ {
+ [Fact]
+ public async Task ExceptionWrittenToDiagnostics()
+ {
+ DiagnosticListener diagnosticListener = null;
+
+ var builder = new WebHostBuilder()
+ .Configure(app =>
+ {
+ diagnosticListener = app.ApplicationServices.GetRequiredService<DiagnosticListener>();
+
+ app.UseDeveloperExceptionPage();
+ app.Run(context =>
+ {
+ throw new Exception("Test exception");
+ });
+ })
+ .ConfigureServices(services => services.AddMiddlewareAnalysis());
+ var server = new TestServer(builder);
+
+ var listener = new TestDiagnosticListener();
+ diagnosticListener.SubscribeWithAdapter(listener);
+
+ await server.CreateClient().GetAsync(string.Empty);
+
+ // "Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware",
+ // "Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareAnalysisTests.+<>c"
+ Assert.Equal(2, listener.MiddlewareStarting.Count);
+ Assert.Equal("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareAnalysisTests+<>c", listener.MiddlewareStarting[1]);
+ // reversed "RunInlineMiddleware"
+ Assert.Equal(1, listener.MiddlewareException.Count);
+ Assert.Equal("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareAnalysisTests+<>c", listener.MiddlewareException[0]);
+ // reversed "Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware"
+ Assert.Equal(1, listener.MiddlewareFinished.Count);
+ Assert.Equal("Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware", listener.MiddlewareFinished[0]);
+ }
+ }
+}
diff --git a/src/Middleware/MiddlewareAnalysis/test/TestDiagnosticListener.cs b/src/Middleware/MiddlewareAnalysis/test/TestDiagnosticListener.cs
new file mode 100644
index 0000000000..ef171aff35
--- /dev/null
+++ b/src/Middleware/MiddlewareAnalysis/test/TestDiagnosticListener.cs
@@ -0,0 +1,35 @@
+// 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 Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DiagnosticAdapter;
+
+namespace Microsoft.AspNetCore.MiddlewareAnalysis
+{
+ public class TestDiagnosticListener
+ {
+ public IList<string> MiddlewareStarting { get; } = new List<string>();
+ public IList<string> MiddlewareFinished { get; } = new List<string>();
+ public IList<string> MiddlewareException { get; } = new List<string>();
+
+ [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
+ public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
+ {
+ MiddlewareStarting.Add(name);
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException")]
+ public virtual void OnMiddlewareException(Exception exception, string name)
+ {
+ MiddlewareException.Add(name);
+ }
+
+ [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareFinished")]
+ public virtual void OnMiddlewareFinished(HttpContext httpContext, string name)
+ {
+ MiddlewareFinished.Add(name);
+ }
+ }
+}
diff --git a/src/Shared/Diagnostics/AttributeValue.cs b/src/Shared/Diagnostics/AttributeValue.cs
new file mode 100644
index 0000000000..2be902b6ae
--- /dev/null
+++ b/src/Shared/Diagnostics/AttributeValue.cs
@@ -0,0 +1,39 @@
+// 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;
+
+namespace Microsoft.AspNetCore.DiagnosticsViewPage.Views
+{
+ [Obsolete("This type is for internal use only and will be removed in a future version.")]
+ public class AttributeValue
+ {
+ public AttributeValue(string prefix, object value, bool literal)
+ {
+ Prefix = prefix;
+ Value = value;
+ Literal = literal;
+ }
+
+ public string Prefix { get; }
+
+ public object Value { get; }
+
+ public bool Literal { get; }
+
+ public static AttributeValue FromTuple(Tuple<string, object, bool> value)
+ {
+ return new AttributeValue(value.Item1, value.Item2, value.Item3);
+ }
+
+ public static AttributeValue FromTuple(Tuple<string, string, bool> value)
+ {
+ return new AttributeValue(value.Item1, value.Item2, value.Item3);
+ }
+
+ public static implicit operator AttributeValue(Tuple<string, object, bool> value)
+ {
+ return FromTuple(value);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Shared/Diagnostics/BaseView.cs b/src/Shared/Diagnostics/BaseView.cs
new file mode 100644
index 0000000000..76f324df60
--- /dev/null
+++ b/src/Shared/Diagnostics/BaseView.cs
@@ -0,0 +1,312 @@
+// 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.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.DiagnosticsViewPage.Views
+{
+ /// <summary>
+ /// Infrastructure
+ /// </summary>
+ [Obsolete("This type is for internal use only and will be removed in a future version.")]
+ public abstract class BaseView
+ {
+ /// <summary>
+ /// The request context
+ /// </summary>
+ protected HttpContext Context { get; private set; }
+
+ /// <summary>
+ /// The request
+ /// </summary>
+ protected HttpRequest Request { get; private set; }
+
+ /// <summary>
+ /// The response
+ /// </summary>
+ protected HttpResponse Response { get; private set; }
+
+ /// <summary>
+ /// The output stream
+ /// </summary>
+ protected StreamWriter Output { get; private set; }
+
+ /// <summary>
+ /// Html encoder used to encode content.
+ /// </summary>
+ protected HtmlEncoder HtmlEncoder { get; set; } = HtmlEncoder.Default;
+
+ /// <summary>
+ /// Url encoder used to encode content.
+ /// </summary>
+ protected UrlEncoder UrlEncoder { get; set; } = UrlEncoder.Default;
+
+ /// <summary>
+ /// JavaScript encoder used to encode content.
+ /// </summary>
+ protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default;
+
+ /// <summary>
+ /// Execute an individual request
+ /// </summary>
+ /// <param name="context"></param>
+ public async Task ExecuteAsync(HttpContext context)
+ {
+ Context = context;
+ Request = Context.Request;
+ Response = Context.Response;
+ Output = new StreamWriter(Response.Body, Encoding.UTF8, 4096, leaveOpen: true);
+ await ExecuteAsync();
+ Output.Dispose();
+ }
+
+ /// <summary>
+ /// Execute an individual request
+ /// </summary>
+ public abstract Task ExecuteAsync();
+
+ /// <summary>
+ /// Write the given value directly to the output
+ /// </summary>
+ /// <param name="value"></param>
+ protected void WriteLiteral(string value)
+ {
+ WriteLiteralTo(Output, value);
+ }
+
+ /// <summary>
+ /// Write the given value directly to the output
+ /// </summary>
+ /// <param name="value"></param>
+ protected void WriteLiteral(object value)
+ {
+ WriteLiteralTo(Output, value);
+ }
+
+ private List<string> AttributeValues { get; set; }
+
+ protected void WriteAttributeValue(string thingy, int startPostion, object value, int endValue, int dealyo, bool yesno)
+ {
+ if (AttributeValues == null)
+ {
+ AttributeValues = new List<string>();
+ }
+
+ AttributeValues.Add(value.ToString());
+ }
+
+ private string AttributeEnding { get; set; }
+
+ protected void BeginWriteAttribute(string name, string begining, int startPosition, string ending, int endPosition, int thingy)
+ {
+ Debug.Assert(string.IsNullOrEmpty(AttributeEnding));
+
+ Output.Write(begining);
+ AttributeEnding = ending;
+ }
+
+ protected void EndWriteAttribute()
+ {
+ Debug.Assert(!string.IsNullOrEmpty(AttributeEnding));
+
+ var attributes = string.Join(" ", AttributeValues);
+ Output.Write(attributes);
+ AttributeValues = null;
+
+ Output.Write(AttributeEnding);
+ AttributeEnding = null;
+ }
+
+ /// <summary>
+ /// Writes the given attribute to the given writer
+ /// </summary>
+ /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
+ /// <param name="name">The name of the attribute to write</param>
+ /// <param name="leader">The value of the prefix</param>
+ /// <param name="trailer">The value of the suffix</param>
+ /// <param name="values">The <see cref="AttributeValue"/>s to write.</param>
+ protected void WriteAttributeTo(
+ TextWriter writer,
+ string name,
+ string leader,
+ string trailer,
+ params AttributeValue[] values)
+ {
+ if (writer == null)
+ {
+ throw new ArgumentNullException(nameof(writer));
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ if (leader == null)
+ {
+ throw new ArgumentNullException(nameof(leader));
+ }
+
+ if (trailer == null)
+ {
+ throw new ArgumentNullException(nameof(trailer));
+ }
+
+
+ WriteLiteralTo(writer, leader);
+ foreach (var value in values)
+ {
+ WriteLiteralTo(writer, value.Prefix);
+
+ // The special cases here are that the value we're writing might already be a string, or that the
+ // 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;
+ if (value.Value is bool)
+ {
+ if ((bool)value.Value)
+ {
+ stringValue = name;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else
+ {
+ stringValue = value.Value as string;
+ }
+
+ // Call the WriteTo(string) overload when possible
+ if (value.Literal && stringValue != null)
+ {
+ WriteLiteralTo(writer, stringValue);
+ }
+ else if (value.Literal)
+ {
+ WriteLiteralTo(writer, value.Value);
+ }
+ else if (stringValue != null)
+ {
+ WriteTo(writer, stringValue);
+ }
+ else
+ {
+ WriteTo(writer, value.Value);
+ }
+ }
+ WriteLiteralTo(writer, trailer);
+ }
+
+ /// <summary>
+ /// Convert to string and html encode
+ /// </summary>
+ /// <param name="value"></param>
+ protected void Write(object value)
+ {
+ WriteTo(Output, value);
+ }
+
+ /// <summary>
+ /// Html encode and write
+ /// </summary>
+ /// <param name="value"></param>
+ protected void Write(string value)
+ {
+ WriteTo(Output, value);
+ }
+
+ /// <summary>
+ /// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked
+ /// </summary>
+ /// <param name="result">The <see cref="HelperResult"/> to invoke</param>
+ protected void Write(HelperResult result)
+ {
+ WriteTo(Output, result);
+ }
+
+ /// <summary>
+ /// Writes the specified <paramref name="value"/> to <paramref name="writer"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
+ /// <param name="value">The <see cref="object"/> to write.</param>
+ /// <remarks>
+ /// <see cref="HelperResult.WriteTo(TextWriter)"/> is invoked for <see cref="HelperResult"/> types.
+ /// For all other types, the encoded result of <see cref="object.ToString"/> is written to the
+ /// <paramref name="writer"/>.
+ /// </remarks>
+ protected void WriteTo(TextWriter writer, object value)
+ {
+ if (value != null)
+ {
+ var helperResult = value as HelperResult;
+ if (helperResult != null)
+ {
+ helperResult.WriteTo(writer);
+ }
+ else
+ {
+ WriteTo(writer, Convert.ToString(value, CultureInfo.InvariantCulture));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Writes the specified <paramref name="value"/> with HTML encoding to <paramref name="writer"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
+ /// <param name="value">The <see cref="string"/> to write.</param>
+ protected void WriteTo(TextWriter writer, string value)
+ {
+ WriteLiteralTo(writer, HtmlEncoder.Encode(value));
+ }
+
+ /// <summary>
+ /// Writes the specified <paramref name="value"/> without HTML encoding to the <paramref name="writer"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
+ /// <param name="value">The <see cref="object"/> to write.</param>
+ protected void WriteLiteralTo(TextWriter writer, object value)
+ {
+ WriteLiteralTo(writer, Convert.ToString(value, CultureInfo.InvariantCulture));
+ }
+
+ /// <summary>
+ /// Writes the specified <paramref name="value"/> without HTML encoding to <see cref="Output"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
+ /// <param name="value">The <see cref="string"/> to write.</param>
+ protected void WriteLiteralTo(TextWriter writer, string value)
+ {
+ if (!string.IsNullOrEmpty(value))
+ {
+ writer.Write(value);
+ }
+ }
+
+ protected string HtmlEncodeAndReplaceLineBreaks(string input)
+ {
+ if (string.IsNullOrEmpty(input))
+ {
+ return string.Empty;
+ }
+
+ // Split on line breaks before passing it through the encoder.
+ return string.Join("<br />" + Environment.NewLine,
+ input.Split(new[] { "\r\n" }, StringSplitOptions.None)
+ .SelectMany(s => s.Split(new[] { '\r', '\n' }, StringSplitOptions.None))
+ .Select(HtmlEncoder.Encode));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Shared/Diagnostics/Directory.Build.props b/src/Shared/Diagnostics/Directory.Build.props
new file mode 100644
index 0000000000..446e684185
--- /dev/null
+++ b/src/Shared/Diagnostics/Directory.Build.props
@@ -0,0 +1,5 @@
+<Project>
+ <PropertyGroup>
+ <IsPackable>false</IsPackable>
+ </PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/src/Shared/Diagnostics/HelperResult.cs b/src/Shared/Diagnostics/HelperResult.cs
new file mode 100644
index 0000000000..10a3d1603d
--- /dev/null
+++ b/src/Shared/Diagnostics/HelperResult.cs
@@ -0,0 +1,35 @@
+// 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.IO;
+
+namespace Microsoft.AspNetCore.DiagnosticsViewPage.Views
+{
+ /// <summary>
+ /// Represents a deferred write operation in a <see cref="BaseView"/>.
+ /// </summary>
+ [Obsolete("This type is for internal use only and will be removed in a future version.")]
+ public class HelperResult
+ {
+ /// <summary>
+ /// Creates a new instance of <see cref="HelperResult"/>.
+ /// </summary>
+ /// <param name="action">The delegate to invoke when <see cref="WriteTo(TextWriter)"/> is called.</param>
+ public HelperResult(Action<TextWriter> action)
+ {
+ WriteAction = action;
+ }
+
+ public Action<TextWriter> WriteAction { get; }
+
+ /// <summary>
+ /// Method invoked to produce content from the <see cref="HelperResult"/>.
+ /// </summary>
+ /// <param name="writer">The <see cref="TextWriter"/> instance to write to.</param>
+ public void WriteTo(TextWriter writer)
+ {
+ WriteAction(writer);
+ }
+ }
+} \ No newline at end of file