diff --git a/.azure/pipelines/jobs/default-build.yml b/.azure/pipelines/jobs/default-build.yml
index 35074f39e5b4..9aac31c5b31b 100644
--- a/.azure/pipelines/jobs/default-build.yml
+++ b/.azure/pipelines/jobs/default-build.yml
@@ -53,7 +53,6 @@ parameters:
# jobDisplayName: '' - use agentOs by default.
artifacts: []
buildDirectory: $(System.DefaultWorkingDirectory)/eng/
- installTar: true
installNodeJs: true
installJdk: true
timeoutInMinutes: 180
@@ -179,9 +178,6 @@ jobs:
Write-Host "##vso[task.setvariable variable=SeleniumProcessTrackingFolder]$(Build.SourcesDirectory)\artifacts\tmp\selenium\"
./eng/scripts/InstallGoogleChrome.ps1
displayName: Install Chrome
- - ${{ if and(eq(parameters.installTar, 'true'), eq(parameters.agentOs, 'Windows')) }}:
- - powershell: ./eng/scripts/InstallTar.ps1
- displayName: Find or install Tar
- ${{ if eq(parameters.agentOs, 'Windows') }}:
- powershell: Write-Host "##vso[task.prependpath]$(DOTNET_CLI_HOME)\.dotnet\tools"
displayName: Add dotnet tools to path
diff --git a/.azure/pipelines/richnav.yml b/.azure/pipelines/richnav.yml
index 46d8b9e3cb75..e5fe8756d315 100644
--- a/.azure/pipelines/richnav.yml
+++ b/.azure/pipelines/richnav.yml
@@ -6,59 +6,69 @@
trigger:
branches:
include:
- - blazor-wasm
- main
- release/*
- - internal/release/*
+
+# Do not run this pipeline for PR validation.
+pr: none
variables:
- name: _BuildArgs
value: '/p:SkipTestBuild=true'
-- name: Windows86LogArgs
+- name: WindowsNonX64LogArgs
value: -ExcludeCIBinaryLog
stages:
- stage: build
displayName: Build
jobs:
- # Build Windows (x64/x86)
+ # Build Windows (x64/x86/arm64)
- template: jobs/default-build.yml
parameters:
codeSign: false
jobName: Windows_build
- jobDisplayName: "Build: Windows x64/x86"
+ jobDisplayName: "Build: Windows x64/x86/arm64"
enableRichCodeNavigation: true
agentOs: Windows
steps:
- - script: ./build.cmd
+ - script: ./eng/build.cmd
-ci
- -all
-arch x64
- /p:EnableRichCodeNavigation=true
+ -buildNative
+ /p:EnableRichCodeNavigation=false
+ $(_BuildArgs)
+ displayName: Build x64 native assets
+
+ - script: ./eng/build.cmd
+ -ci
+ -arch x64
+ -all
+ -noBuildNative
+ -noBuildRepoTasks
$(_BuildArgs)
displayName: Build x64
# Build the x86 shared framework
# This is going to actually build x86 native assets.
- - script: ./build.cmd
+ - script: ./eng/build.cmd
-ci
- -noBuildRepoTasks
-arch x86
-all
-noBuildJava
-noBuildNative
- /p:EnableRichCodeNavigation=true
+ -noBuildRepoTasks
$(_BuildArgs)
- $(Windows86LogArgs)
+ $(WindowsNonX64LogArgs)
displayName: Build x86
- # Windows installers bundle both x86 and x64 assets
- - script: ./build.cmd
+ # Build the arm64 shared framework
+ - script: ./eng/build.cmd
-ci
- -noBuildRepoTasks
- -buildInstallers
+ -arch arm64
+ -noBuildJava
-noBuildNative
- /p:AssetManifestFileName=aspnetcore-win-x64-x86.xml
- /p:EnableRichCodeNavigation=true
+ -noBuildRepoTasks
$(_BuildArgs)
- displayName: Build Installers
+ $(WindowsNonX64LogArgs)
+ displayName: Build ARM64
+
diff --git a/.editorconfig b/.editorconfig
index d7ec0b83dec4..8583569cfc7d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -208,7 +208,7 @@ dotnet_diagnostic.IDE0044.severity = warning
dotnet_diagnostic.IDE0073.severity = warning
file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.
-[**/{test,samples,perf}/**.{cs,vb}]
+[{eng/tools/**.cs,**/{test,testassets,samples,Samples,perf,scripts}/**.cs}]
# CA1018: Mark attributes with AttributeUsageAttribute
dotnet_diagnostic.CA1018.severity = suggestion
# CA1507: Use nameof to express symbol names
@@ -241,6 +241,10 @@ dotnet_diagnostic.CA1844.severity = suggestion
dotnet_diagnostic.CA1845.severity = suggestion
# CA1846: Prefer AsSpan over Substring
dotnet_diagnostic.CA1846.severity = suggestion
+# CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
+dotnet_diagnostic.CA1847.severity = suggestion
+# CA2007: Consider calling ConfigureAwait on the awaited task
+dotnet_diagnostic.CA2007.severity = suggestion
# CA2008: Do not create tasks without passing a TaskScheduler
dotnet_diagnostic.CA2008.severity = suggestion
# CA2012: Use ValueTask correctly
diff --git a/Directory.Build.props b/Directory.Build.props
index 8f0ac5942284..721f099a7734 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -30,6 +30,9 @@
$(MSBuildProjectName.EndsWith('.Test')) OR
$(MSBuildProjectName.EndsWith('.FunctionalTest')) ) ">true
true
+ true
true
true
true
+ Condition="'$(ExcludeFromSourceBuild)' == '' and
+ '$(DotNetBuildFromSource)' == 'true' and
+ '$(IsAspNetCoreApp)' != 'true' and
+ '$(MSBuildProjectName)' != '$(TargetingPackName)' and
+ '$(IsAnalyzersProject)' != 'true' and
+ '$(IsProjectTemplateProject)' != 'true'">true
diff --git a/NuGet.config b/NuGet.config
index 76ccfd79eeee..06e49041de2f 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -4,10 +4,10 @@
-
+
-
+
@@ -27,10 +27,10 @@
-
+
-
+
diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props
index feec59968ea5..7bbef3953446 100644
--- a/eng/Baseline.Designer.props
+++ b/eng/Baseline.Designer.props
@@ -2,28 +2,28 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
-
-
+
+
+
@@ -34,120 +34,120 @@
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
@@ -155,114 +155,114 @@
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
-
-
-
+
+
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
-
+
+
@@ -270,7 +270,7 @@
- 6.0.3
+ 6.0.4
@@ -278,50 +278,50 @@
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
-
+
+
@@ -331,8 +331,8 @@
-
-
+
+
@@ -340,8 +340,8 @@
-
-
+
+
@@ -352,58 +352,58 @@
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
@@ -411,71 +411,71 @@
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
-
+
+
-
+
-
-
+
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
@@ -491,195 +491,195 @@
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
-
-
+
+
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
-
-
+
+
-
-
+
+
-
-
+
+
- 6.0.3
+ 6.0.4
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
-
-
-
+
+
+
+
- 6.0.3
+ 6.0.4
@@ -688,69 +688,69 @@
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
-
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
@@ -769,7 +769,7 @@
- 6.0.3
+ 6.0.4
@@ -788,7 +788,7 @@
- 6.0.3
+ 6.0.4
@@ -804,46 +804,46 @@
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
-
-
+
+
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
@@ -853,7 +853,7 @@
- 6.0.3
+ 6.0.4
@@ -862,73 +862,73 @@
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
-
+
-
+
-
+
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
@@ -957,11 +957,11 @@
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
@@ -979,13 +979,13 @@
- 6.0.3
+ 6.0.4
- 6.0.3
+ 6.0.4
-
+
\ No newline at end of file
diff --git a/eng/Baseline.xml b/eng/Baseline.xml
index 903713249197..e20bb4bb51a8 100644
--- a/eng/Baseline.xml
+++ b/eng/Baseline.xml
@@ -4,111 +4,111 @@ This file contains a list of all the packages and their versions which were rele
Update this list when preparing for a new patch.
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index a15e7e7e1825..67ad201ccdf6 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -9,37 +9,37 @@
-->
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- aca50aef8604cc23910d18edce820e0fa7c61910
+ 9b03633bb18b617088b32260065ee385bf9c4491
https://github.com/dotnet/runtime
@@ -177,9 +177,9 @@
https://github.com/dotnet/runtime
4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
https://github.com/dotnet/runtime
@@ -193,9 +193,9 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
839cdfb0ecca5e0be3dbccd926e7651ef50fdf10
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 839cdfb0ecca5e0be3dbccd926e7651ef50fdf10
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
https://github.com/dotnet/runtime
@@ -217,9 +217,9 @@
https://github.com/dotnet/runtime
4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
-
- https://github.com/dotnet/runtime
- 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
+
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
https://github.com/dotnet/runtime
@@ -233,9 +233,9 @@
https://github.com/dotnet/runtime
4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
https://github.com/dotnet/runtime
@@ -245,33 +245,33 @@
https://github.com/dotnet/runtime
4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- be98e88c760526452df94ef452fff4602fb5bded
+ 70ae3df4a6f3c92fb6b315afc405edd10ff38579
@@ -280,22 +280,22 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
be98e88c760526452df94ef452fff4602fb5bded
-
+
https://github.com/dotnet/arcade
- 879df783283dfb44c7653493fdf7fd7b07ba6b01
+ 1a6b24397e50146d0fece9cfb9c0b87275691e6f
-
+
https://github.com/dotnet/arcade
- 879df783283dfb44c7653493fdf7fd7b07ba6b01
+ 1a6b24397e50146d0fece9cfb9c0b87275691e6f
-
+
https://github.com/dotnet/arcade
- 879df783283dfb44c7653493fdf7fd7b07ba6b01
+ 1a6b24397e50146d0fece9cfb9c0b87275691e6f
-
+
https://github.com/dotnet/arcade
- 879df783283dfb44c7653493fdf7fd7b07ba6b01
+ 1a6b24397e50146d0fece9cfb9c0b87275691e6f
diff --git a/eng/Versions.props b/eng/Versions.props
index 2d3e47d26e41..2ba62764d9b6 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -8,7 +8,7 @@
6
0
- 4
+ 5
true
6.0.0
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4-servicing.22164.4
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5-servicing.22213.9
6.0.0
6.0.1
6.0.0
@@ -103,36 +103,36 @@
6.0.0
6.0.0
6.0.0
- 6.0.4-servicing.22164.4
+ 6.0.5-servicing.22213.9
6.0.0
6.0.0
6.0.1
- 6.0.2
+ 6.0.3
6.0.0
6.0.1
6.0.1
6.0.0
6.0.0
- 6.0.0
+ 6.0.1
6.0.0
6.0.0
6.0.0
- 6.0.3
+ 6.0.4
6.0.0
6.0.3
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4
- 6.0.4
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5
+ 6.0.5
- 6.0.0-beta.22161.1
- 6.0.0-beta.22161.1
+ 6.0.0-beta.22212.5
+ 6.0.0-beta.22212.5
2.1.1
2.2.0
- 3.1.23-servicing-22123-12
+ 3.1.24-servicing-22180-6
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31Version)
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension31Version)
- 5.0.15-servicing-22116-16
+ 5.0.16-servicing-22167-6
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension50Version)
$(MicrosoftAspNetCoreAzureAppServicesSiteExtension50Version)
diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml
index ba40dc82f141..abb1b2bcda42 100644
--- a/eng/common/templates/steps/source-build.yml
+++ b/eng/common/templates/steps/source-build.yml
@@ -43,8 +43,8 @@ steps:
# In that case, add variables to allow the download of internal runtimes if the specified versions are not found
# in the default public locations.
internalRuntimeDownloadArgs=
- if [ '$(dotnetclimsrc-read-sas-token-base64)' != '$''(dotnetclimsrc-read-sas-token-base64)' ]; then
- internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetclimsrc.blob.core.windows.net/dotnet /p:DotNetRuntimeSourceFeedKey=$(dotnetclimsrc-read-sas-token-base64) --runtimesourcefeed https://dotnetclimsrc.blob.core.windows.net/dotnet --runtimesourcefeedkey $(dotnetclimsrc-read-sas-token-base64)'
+ if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then
+ internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)'
fi
buildConfig=Release
diff --git a/eng/scripts/InstallTar.ps1 b/eng/scripts/InstallTar.ps1
deleted file mode 100644
index 12159a8d0be0..000000000000
--- a/eng/scripts/InstallTar.ps1
+++ /dev/null
@@ -1,74 +0,0 @@
-<#
-.SYNOPSIS
- Finds or installs the Tar command on this system.
-.DESCRIPTION
- This script searches for Tar on this system. If not found, downloads and extracts Git to use its tar.exe. Prefers
- global installation locations even if Git has been downloaded into this repo.
-.PARAMETER GitVersion
- The version of the Git to install. If not set, the default value is read from global.json.
-.PARAMETER Force
- Overwrite the existing installation if one exists in this repo and Tar isn't installed globally.
-#>
-param(
- [string]$GitVersion,
- [switch]$Force
-)
-
-$ErrorActionPreference = 'Stop'
-$ProgressPreference = 'SilentlyContinue' # Workaround PowerShell/PowerShell#2138
-
-Set-StrictMode -Version 1
-
-# Find tar. If not found, install Git to get it.
-$repoRoot = (Join-Path $PSScriptRoot "..\.." -Resolve)
-$installDir = "$repoRoot\.tools\Git\win-x64"
-$tarCommand = "$installDir\usr\bin\tar.exe"
-$finalCommand = "$repoRoot\.tools\tar.exe"
-
-Write-Host "Windows version and other information..."
-cmd.exe /c ver
-systeminfo.exe
-Write-Host "Processor Architecture: $env:PROCESSOR_ARCHITECTURE"
-
-Write-Host "Checking $env:SystemRoot\System32\tar.exe"
-Get-ChildItem "$env:SystemRoot\System32\ta*.exe"
-if (Test-Path "$env:SystemRoot\System32\tar.exe") {
- Write-Host "Found $env:SystemRoot\System32\tar.exe"
- $tarCommand = "$env:SystemRoot\System32\tar.exe"
-}
-elseif (Test-Path "$env:ProgramFiles\Git\usr\bin\tar.exe") {
- $tarCommand = "$env:ProgramFiles\Git\usr\bin\tar.exe"
-}
-elseif (Test-Path "${env:ProgramFiles(x86)}\Git\usr\bin\tar.exe") {
- $tarCommand = "${env:ProgramFiles(x86)}\Git\usr\bin\tar.exe"
-}
-elseif (Test-Path "$env:AGENT_HOMEDIRECTORY\externals\git\usr\bin\tar.exe") {
- $tarCommand = "$env:AGENT_HOMEDIRECTORY\externals\git\usr\bin\tar.exe"
-}
-elseif ((Test-Path $tarCommand) -And (-Not $Force)) {
- Write-Verbose "Repo-local Git installation and $tarCommand already exist, skipping Git install."
-}
-else {
- if (-not $GitVersion) {
- $globalJson = Get-Content "$repoRoot\global.json" | ConvertFrom-Json
- $GitVersion = $globalJson.tools.Git
- }
-
- $Uri = "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/git/Git-${GitVersion}-64-bit.zip"
-
- Import-Module -Name (Join-Path $PSScriptRoot "..\common\native\CommonLibrary.psm1" -Resolve)
- $InstallStatus = CommonLibrary\DownloadAndExtract -Uri $Uri -InstallDirectory "$installDir\" -Force:$Force -Verbose
-
- if ($InstallStatus -Eq $False) {
- Write-Error "Installation failed"
- exit 1
- }
-}
-
-New-Item "$repoRoot\.tools\" -ErrorAction SilentlyContinue -ItemType Directory
-Copy-Item "$tarCommand" "$finalCommand" -Verbose
-Write-Host "Tar now available at '$finalCommand'"
-
-if ($tarCommand -like '*\Git\*') {
- $null >.\.tools\tar.fromGit
-}
diff --git a/global.json b/global.json
index 05ed9cd7faca..30a17d2cce7a 100644
--- a/global.json
+++ b/global.json
@@ -1,9 +1,9 @@
{
"sdk": {
- "version": "6.0.103"
+ "version": "6.0.104"
},
"tools": {
- "dotnet": "6.0.103",
+ "dotnet": "6.0.104",
"runtimes": {
"dotnet/x64": [
"2.1.30",
@@ -13,7 +13,7 @@
"$(MicrosoftNETCoreBrowserDebugHostTransportVersion)"
],
"aspnetcore/x64": [
- "3.1.23"
+ "3.1.24"
]
},
"Git": "2.22.0",
@@ -29,7 +29,7 @@
},
"msbuild-sdks": {
"Yarn.MSBuild": "1.22.10",
- "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.22161.1",
- "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.22161.1"
+ "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.22212.5",
+ "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.22212.5"
}
}
diff --git a/src/Components/WebAssembly/Authentication.Msal/src/Microsoft.Authentication.WebAssembly.Msal.csproj b/src/Components/WebAssembly/Authentication.Msal/src/Microsoft.Authentication.WebAssembly.Msal.csproj
index 8ae5d1c43d7b..1db0368b882c 100644
--- a/src/Components/WebAssembly/Authentication.Msal/src/Microsoft.Authentication.WebAssembly.Msal.csproj
+++ b/src/Components/WebAssembly/Authentication.Msal/src/Microsoft.Authentication.WebAssembly.Msal.csproj
@@ -1,6 +1,6 @@
-
+
$(DefaultNetCoreTargetFramework)
@@ -25,6 +25,7 @@
$(MSBuildThisFileDirectory)Interop\
+ CheckForSourceBuild;
CompileInterop;
IncludeCompileInteropOutput;
$(ResolveStaticWebAssetsInputsDependsOn)
@@ -91,5 +92,11 @@
+
+
+
+
+
+
diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj b/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj
index 8d6a000d74f0..35c79a73eb8b 100644
--- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj
+++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj
@@ -1,6 +1,6 @@
-
+
$(DefaultNetCoreTargetFramework)
@@ -26,6 +26,7 @@
$(MSBuildThisFileDirectory)Interop\
+ CheckForSourceBuild;
CompileInterop;
IncludeCompileInteropOutput;
$(ResolveStaticWebAssetsInputsDependsOn)
@@ -93,4 +94,10 @@
+
+
+
+
+
+
diff --git a/src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj b/src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj
index 07d568ff72e3..4a9dd771989e 100644
--- a/src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj
+++ b/src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj
@@ -85,9 +85,8 @@ This package is an internal implementation of the .NET Core SDK and is not meant
$(TargetingPackLayoutRoot)$(TargetingPackSubPath)
$(LocalDotNetRoot)$(TargetingPackSubPath)
- aspnetcore-targeting-pack-$(PackageVersion)
- $(InstallersOutputPath)$(ArchiveOutputFileName).zip
- $(InstallersOutputPath)$(ArchiveOutputFileName).tar.gz
+ aspnetcore-targeting-pack-$(PackageVersion)-$(TargetRuntimeIdentifier)
+ $(InstallersOutputPath)$(ArchiveOutputFileName)$(ArchiveExtension)
@@ -221,29 +220,17 @@ This package is an internal implementation of the .NET Core SDK and is not meant
-
- <_TarCommand>tar
- <_TarCommand Condition="Exists('$(RepoRoot).tools\tar.exe')">"$(RepoRoot).tools\tar.exe"
-
-
- <_TarArchiveOutputPath>$(TarArchiveOutputPath)
- <_TarArchiveOutputPath
- Condition="Exists('$(repoRoot)\.tools\tar.fromGit')">/$(TarArchiveOutputPath.Replace('\','/').Replace(':',''))
-
-
+ Outputs="$(ArchiveOutputPath)">
-
-
-
-
-
-
+ DestinationFile="$(ArchiveOutputPath)"
+ Overwrite="true"
+ Condition="'$(ArchiveExtension)' == '.zip'" />
+
+
InnerReadFormAsync(CancellationToken cancell
else if (HasMultipartFormContentType(contentType))
{
var formAccumulator = new KeyValueAccumulator();
+ var nonFormOrFileContentDispositionCount = 0;
var boundary = GetBoundary(contentType, _options.MultipartBoundaryLengthLimit);
var multipartReader = new MultipartReader(boundary, _request.Body)
@@ -259,7 +260,11 @@ private async Task InnerReadFormAsync(CancellationToken cancell
}
else
{
- System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + section.ContentDisposition);
+ if (nonFormOrFileContentDispositionCount++ >= _options.ValueCountLimit)
+ {
+ throw new InvalidDataException($"Unrecognized Content-Disposition. Form value count limit {_options.ValueCountLimit} exceeded.");
+
+ }
}
section = await multipartReader.ReadNextSectionAsync(cancellationToken);
diff --git a/src/Http/Http/test/Features/FormFeatureTests.cs b/src/Http/Http/test/Features/FormFeatureTests.cs
index 9426ce6dd1b2..67873e223c4d 100644
--- a/src/Http/Http/test/Features/FormFeatureTests.cs
+++ b/src/Http/Http/test/Features/FormFeatureTests.cs
@@ -165,6 +165,12 @@ private class MockRequestBodyPipeFeature : IRequestBodyPipeFeature
InvalidContentDispositionValue +
"\r\n" +
"\r\n" +
+"Foo\r\n";
+
+ private const string MultipartFormFileNonFormOrFileContentDispositionValue = "--WebKitFormBoundary5pDRpGheQXaM8k3T\r\n" +
+"Content-Disposition:x" +
+"\r\n" +
+"\r\n" +
"Foo\r\n";
private const string MultipartFormWithField =
@@ -468,6 +474,30 @@ public async Task ReadFormAsync_ValueCountLimitExceeded_Throw(bool bufferRequest
Assert.Equal("Form value count limit 2 exceeded.", exception.Message);
}
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task ReadFormAsync_NonFormOrFieldContentDisposition_ValueCountLimitExceeded_Throw(bool bufferRequest)
+ {
+ var formContent = new List();
+ formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormFileNonFormOrFileContentDispositionValue));
+ formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormFileNonFormOrFileContentDispositionValue));
+ formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormFileNonFormOrFileContentDispositionValue));
+ formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormEnd));
+
+ var context = new DefaultHttpContext();
+ var responseFeature = new FakeResponseFeature();
+ context.Features.Set(responseFeature);
+ context.Request.ContentType = MultipartContentType;
+ context.Request.Body = new NonSeekableReadStream(formContent.ToArray());
+
+ IFormFeature formFeature = new FormFeature(context.Request, new FormOptions() { BufferBody = bufferRequest, ValueCountLimit = 2 });
+ context.Features.Set(formFeature);
+
+ var exception = await Assert.ThrowsAsync(() => context.Request.ReadFormAsync());
+ Assert.Equal("Unrecognized Content-Disposition. Form value count limit 2 exceeded.", exception.Message);
+ }
+
[Theory]
[InlineData(true)]
[InlineData(false)]
diff --git a/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj b/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj
index 6966845de3a6..345878cac074 100644
--- a/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj
+++ b/src/Installers/Debian/TargetingPack/Debian.TargetingPack.debproj
@@ -37,7 +37,7 @@
- $(TargetingPackInstallerBaseName)-$(TargetingPackVersion).deb
+ $(TargetingPackInstallerBaseName)-$(TargetingPackVersion)-$(TargetArchitecture).deb
$(TargetDir)$(TargetFileName)
$(TargetingPackVersionPrefix)
diff --git a/src/Installers/Windows/TargetingPack/TargetingPack.wixproj b/src/Installers/Windows/TargetingPack/TargetingPack.wixproj
index 27894df71808..dc20b17662f0 100644
--- a/src/Installers/Windows/TargetingPack/TargetingPack.wixproj
+++ b/src/Installers/Windows/TargetingPack/TargetingPack.wixproj
@@ -66,7 +66,7 @@
$(InstallersOutputPath)
$(TargetingPackVersionPrefix)
$(TargetingPackZipVersion)-$(VersionSuffix)
- $(TargetingPackHarvestRoot)aspnetcore-targeting-pack-$(TargetingPackZipVersion).zip
+ $(TargetingPackHarvestRoot)aspnetcore-targeting-pack-$(TargetingPackZipVersion)-$(TargetRuntimeIdentifier).zip
diff --git a/src/Installers/Windows/WindowsHostingBundle/DotNetCore.wxs b/src/Installers/Windows/WindowsHostingBundle/DotNetCore.wxs
index 25e549b0dedd..3a7095c146a4 100644
--- a/src/Installers/Windows/WindowsHostingBundle/DotNetCore.wxs
+++ b/src/Installers/Windows/WindowsHostingBundle/DotNetCore.wxs
@@ -1,6 +1,18 @@
+
+
+
+
@@ -12,7 +24,7 @@
InstallCondition="VersionNT64 AND (NOT OPT_NO_RUNTIME OR OPT_NO_RUNTIME="0")"
InstallCommand="/quiet /norestart"
RepairCommand="/quiet /repair"
- Permanent="yes"
+ UninstallCommand="/quiet /uninstall"
DetectCondition="DotNetRedistLtsProductVersion_x64 = v$(var.DotNetRedistLtsInstallerProductVersionx64)">
@@ -24,23 +36,9 @@
InstallCondition="(NOT OPT_NO_RUNTIME OR OPT_NO_RUNTIME="0") AND (NOT OPT_NO_X86 OR OPT_NO_X86="0")"
InstallCommand="/quiet /norestart"
RepairCommand="/quiet /repair"
- Permanent="yes"
+ UninstallCommand="/quiet /uninstall"
DetectCondition="DotNetRedistLtsProductVersion_x86 = v$(var.DotNetRedistLtsInstallerProductVersionx86)">
-
-
-
-
-
-
diff --git a/src/Installers/Windows/WindowsHostingBundle/SharedFramework.wxs b/src/Installers/Windows/WindowsHostingBundle/SharedFramework.wxs
index c75b5eb36253..33e84d0921f0 100644
--- a/src/Installers/Windows/WindowsHostingBundle/SharedFramework.wxs
+++ b/src/Installers/Windows/WindowsHostingBundle/SharedFramework.wxs
@@ -1,6 +1,18 @@
+
+
+
+
@@ -12,7 +24,7 @@
InstallCondition="VersionNT64 AND (NOT OPT_NO_SHAREDFX OR OPT_NO_SHAREDFX="0")"
InstallCommand="/quiet /norestart"
RepairCommand="/quiet /repair"
- Permanent="yes"
+ UninstallCommand="/quiet /uninstall"
DetectCondition="SharedFxRedistProductVersion_x64 = v$(var.SharedFxInstallerProductVersionx64)">
@@ -24,23 +36,9 @@
InstallCondition="(NOT OPT_NO_SHAREDFX OR OPT_NO_SHAREDFX="0") AND (NOT OPT_NO_X86 OR OPT_NO_X86="0")"
InstallCommand="/quiet /norestart"
RepairCommand="/quiet /repair"
- Permanent="yes"
+ UninstallCommand="/quiet /uninstall"
DetectCondition="SharedFxRedistProductVersion_x86 = v$(var.SharedFxInstallerProductVersionx86)">
-
-
-
-
-
-
diff --git a/src/Mvc/test/Mvc.FunctionalTests/ErrorPageTests.cs b/src/Mvc/test/Mvc.FunctionalTests/ErrorPageTests.cs
index 11ff6b17d899..ccfb60a40bb4 100644
--- a/src/Mvc/test/Mvc.FunctionalTests/ErrorPageTests.cs
+++ b/src/Mvc/test/Mvc.FunctionalTests/ErrorPageTests.cs
@@ -1,14 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
-using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Encodings.Web;
-using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
using Microsoft.AspNetCore.TestHost;
@@ -16,7 +12,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
-using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@@ -24,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
///
/// Functional test to verify the error reporting of Razor compilation by diagnostic middleware.
///
- public class ErrorPageTests : IClassFixture>, IDisposable
+ public class ErrorPageTests : IClassFixture>
{
private static readonly string PreserveCompilationContextMessage = HtmlEncoder.Default.Encode(
"One or more compilation references may be missing. " +
@@ -189,10 +184,5 @@ public async Task AggregateException_FlattensInnerExceptions()
Assert.Contains(nullReferenceException, content);
Assert.Contains(indexOutOfRangeException, content);
}
-
- public void Dispose()
- {
- _assemblyTestLog.Dispose();
- }
}
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/dotnetcli.host.json
index 3c34f96f85e3..02abad32e7eb 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/dotnetcli.host.json
@@ -85,6 +85,10 @@
"CallsMicrosoftGraph": {
"longName": "calls-graph",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
},
"usageExamples": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/ide.host.json
index e891e5f2df97..947fd0caa52b 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/ide.host.json
@@ -43,6 +43,12 @@
"useHttps": true
}
],
+ "symbolInfo": [
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
+ }
+ ],
"disableHttpsSymbol": "NoHttps",
"supportsDocker": true
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json
index 254598a9cfe2..4b203fdd52b6 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json
@@ -34,6 +34,21 @@
"wwwroot/**"
],
"modifiers": [
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain)",
+ "exclude": [
+ "Program.cs"
+ ],
+ "rename": {
+ "Program.Main.cs": "Program.cs"
+ }
+ },
{
"condition": "(!IndividualLocalAuth || UseLocalDB)",
"exclude": [
@@ -490,6 +505,13 @@
"datatype": "bool",
"description": "If specified, skips the automatic restore of the project on create.",
"defaultValue": "false"
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"primaryOutputs": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs
new file mode 100644
index 000000000000..92eb45d80a91
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs
@@ -0,0 +1,169 @@
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.OpenIdConnect;
+using Microsoft.Identity.Web;
+using Microsoft.Identity.Web.UI;
+#endif
+#if (WindowsAuth)
+using Microsoft.AspNetCore.Authentication.Negotiate;
+#endif
+#if (OrganizationalAuth)
+#if (MultiOrgAuth)
+using Microsoft.AspNetCore.Authentication.OpenIdConnect;
+#endif
+using Microsoft.AspNetCore.Authorization;
+#endif
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Web;
+#if (IndividualLocalAuth)
+using Microsoft.AspNetCore.Components.Authorization;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.UI;
+#endif
+#if (OrganizationalAuth)
+using Microsoft.AspNetCore.Mvc.Authorization;
+#endif
+#if (IndividualLocalAuth)
+using Microsoft.EntityFrameworkCore;
+#endif
+#if (GenerateGraph)
+using Graph = Microsoft.Graph;
+#endif
+#if(MultiOrgAuth)
+using Microsoft.IdentityModel.Tokens;
+#endif
+#if (IndividualLocalAuth)
+using BlazorServerWeb_CSharp.Areas.Identity;
+#endif
+using BlazorServerWeb_CSharp.Data;
+
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ // Add services to the container.
+ #if (IndividualLocalAuth)
+ var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
+ builder.Services.AddDbContext(options =>
+ #if (UseLocalDB)
+ options.UseSqlServer(connectionString));
+ #else
+ options.UseSqlite(connectionString));
+ #endif
+ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
+ builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
+ .AddEntityFrameworkStores();
+ #elif (OrganizationalAuth)
+ #if (GenerateApiOrGraph)
+ var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
+
+ #endif
+ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+ #if (GenerateApiOrGraph)
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ #if (GenerateApi)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ #if (GenerateGraph)
+ .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
+ #endif
+ #elif (IndividualB2CAuth)
+ #if (GenerateApi)
+ var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
+
+ #endif
+ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+ #if (GenerateApi)
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"));
+ #endif
+ #endif
+ #if (OrganizationalAuth || IndividualB2CAuth)
+ builder.Services.AddControllersWithViews()
+ .AddMicrosoftIdentityUI();
+
+ builder.Services.AddAuthorization(options =>
+ {
+ // By default, all incoming requests will be authorized according to the default policy
+ options.FallbackPolicy = options.DefaultPolicy;
+ });
+
+ #elif (WindowsAuth)
+ builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
+ .AddNegotiate();
+
+ builder.Services.AddAuthorization(options =>
+ {
+ // By default, all incoming requests will be authorized according to the default policy.
+ options.FallbackPolicy = options.DefaultPolicy;
+ });
+
+ #endif
+ builder.Services.AddRazorPages();
+ #if (OrganizationalAuth || IndividualB2CAuth)
+ builder.Services.AddServerSideBlazor()
+ .AddMicrosoftIdentityConsentHandler();
+ #else
+ builder.Services.AddServerSideBlazor();
+ #endif
+ #if (IndividualLocalAuth)
+ builder.Services.AddScoped>();
+ #endif
+ builder.Services.AddSingleton();
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ #if (IndividualLocalAuth)
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseMigrationsEndPoint();
+ }
+ else
+ #else
+ if (!app.Environment.IsDevelopment())
+ #endif
+ {
+ app.UseExceptionHandler("/Error");
+ #if (RequiresHttps)
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
+
+ app.UseHttpsRedirection();
+ #else
+ }
+
+ #endif
+
+ app.UseStaticFiles();
+
+ app.UseRouting();
+
+ #if (OrganizationalAuth || IndividualAuth || WindowsAuth)
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ #endif
+ #if (OrganizationalAuth || IndividualAuth)
+ app.MapControllers();
+ #endif
+ app.MapBlazorHub();
+ app.MapFallbackToPage("/_Host");
+
+ app.Run();
+ }
+}
\ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json
index 93b2c5a3bc03..3d2007dc5868 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json
@@ -95,6 +95,10 @@
"CallsMicrosoftGraph": {
"longName": "calls-graph",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
}
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/ide.host.json
index 9005a4d171a3..2ed03203a457 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/ide.host.json
@@ -47,6 +47,10 @@
"text": "_Progressive Web Application"
},
"isVisible": "true"
+ },
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
}
]
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json
index cd44728a4894..9b261b76d232 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json
@@ -94,6 +94,24 @@
"Client/wwwroot/icon-512.png"
]
},
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Server/Program.Main.cs",
+ "Client/Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain)",
+ "exclude": [
+ "Server/Program.cs",
+ "Client/Program.cs"
+ ],
+ "rename": {
+ "Server/Program.Main.cs": "Server/Program.cs",
+ "Client/Program.Main.cs": "Client/Program.cs"
+ }
+ },
{
"condition": "(!IndividualLocalAuth || UseLocalDB)",
"exclude": [
@@ -591,6 +609,13 @@
"GenerateApiOrGraph": {
"type": "computed",
"value": "(GenerateApi || GenerateGraph)"
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"tags": {
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.Main.cs
new file mode 100644
index 000000000000..8b870e5dc9d3
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.Main.cs
@@ -0,0 +1,69 @@
+using Microsoft.AspNetCore.Components.Web;
+#if (!NoAuth && Hosted)
+using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
+#endif
+using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+#if (Hosted)
+using ComponentsWebAssembly_CSharp.Client;
+#else
+using ComponentsWebAssembly_CSharp;
+#endif
+
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static async Task Main(string[] args)
+ {
+ var builder = WebAssemblyHostBuilder.CreateDefault(args);
+ builder.RootComponents.Add("#app");
+ builder.RootComponents.Add("head::after");
+
+ #if (!Hosted || NoAuth)
+ builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
+ #else
+ builder.Services.AddHttpClient("ComponentsWebAssembly_CSharp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
+ .AddHttpMessageHandler();
+
+ // Supply HttpClient instances that include access tokens when making requests to the server project
+ builder.Services.AddScoped(sp => sp.GetRequiredService().CreateClient("ComponentsWebAssembly_CSharp.ServerAPI"));
+ #endif
+ #if(!NoAuth)
+
+ #endif
+ #if (IndividualLocalAuth)
+ #if (Hosted)
+ builder.Services.AddApiAuthorization();
+ #else
+ builder.Services.AddOidcAuthentication(options =>
+ {
+ #if(MissingAuthority)
+ // Configure your authentication provider options here.
+ // For more information, see https://aka.ms/blazor-standalone-auth
+ #endif
+ builder.Configuration.Bind("Local", options.ProviderOptions);
+ });
+ #endif
+ #endif
+ #if (IndividualB2CAuth)
+ builder.Services.AddMsalAuthentication(options =>
+ {
+ builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
+ #if (Hosted)
+ options.ProviderOptions.DefaultAccessTokenScopes.Add("https://qualified.domain.name/api.id.uri/api-scope");
+ #endif
+ });
+ #endif
+ #if(OrganizationalAuth)
+ builder.Services.AddMsalAuthentication(options =>
+ {
+ builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
+ #if (Hosted)
+ options.ProviderOptions.DefaultAccessTokenScopes.Add("api://api.id.uri/api-scope");
+ #endif
+ });
+ #endif
+
+ await builder.Build().RunAsync();
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs
new file mode 100644
index 000000000000..31835439cd28
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs
@@ -0,0 +1,125 @@
+#if (OrganizationalAuth || IndividualB2CAuth || IndividualLocalAuth)
+using Microsoft.AspNetCore.Authentication;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+#endif
+using Microsoft.AspNetCore.ResponseCompression;
+#if (IndividualLocalAuth)
+using Microsoft.EntityFrameworkCore;
+#endif
+#if (GenerateGraph)
+using Graph = Microsoft.Graph;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.Identity.Web;
+#endif
+#if (IndividualLocalAuth)
+using ComponentsWebAssembly_CSharp.Server.Data;
+using ComponentsWebAssembly_CSharp.Server.Models;
+#endif
+
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ // Add services to the container.
+ #if (IndividualLocalAuth)
+ var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
+ builder.Services.AddDbContext(options =>
+ #if (UseLocalDB)
+ options.UseSqlServer(connectionString));
+ #else
+ options.UseSqlite(connectionString));
+ #endif
+ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
+
+ builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
+ .AddEntityFrameworkStores();
+
+ builder.Services.AddIdentityServer()
+ .AddApiAuthorization();
+
+ builder.Services.AddAuthentication()
+ .AddIdentityServerJwt();
+ #endif
+ #if (OrganizationalAuth)
+ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ #if (GenerateApiOrGraph)
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
+ .EnableTokenAcquisitionToCallDownstreamApi()
+ #if (GenerateApi)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ #if (GenerateGraph)
+ .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
+ #endif
+ #elif (IndividualB2CAuth)
+ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ #if (GenerateApi)
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi()
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C"));
+ #endif
+ #endif
+
+ builder.Services.AddControllersWithViews();
+ builder.Services.AddRazorPages();
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ if (app.Environment.IsDevelopment())
+ {
+ #if (IndividualLocalAuth)
+ app.UseMigrationsEndPoint();
+ #endif
+ app.UseWebAssemblyDebugging();
+ }
+ else
+ {
+ app.UseExceptionHandler("/Error");
+ #if (RequiresHttps)
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ #endif
+ }
+
+ #if (RequiresHttps)
+ app.UseHttpsRedirection();
+
+ #endif
+ app.UseBlazorFrameworkFiles();
+ app.UseStaticFiles();
+
+ app.UseRouting();
+
+ #if (IndividualLocalAuth)
+ app.UseIdentityServer();
+ #endif
+ #if (OrganizationalAuth || IndividualAuth)
+ app.UseAuthentication();
+ #endif
+ #if (!NoAuth)
+ app.UseAuthorization();
+
+ #endif
+
+ app.MapRazorPages();
+ app.MapControllers();
+ app.MapFallbackToFile("index.html");
+
+ app.Run();
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/dotnetcli.host.json
index d97858472b80..6f4f4fb8936d 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/dotnetcli.host.json
@@ -27,6 +27,10 @@
"NoHttps": {
"longName": "no-https",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
},
"usageExamples": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/ide.host.json
index 751a95c5b2ff..f30c4753e2a8 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/ide.host.json
@@ -20,5 +20,11 @@
"useHttps": true
}
],
+ "symbolInfo": [
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
+ }
+ ],
"disableHttpsSymbol": "NoHttps"
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json
index 3b7edb411a86..c85130f420f9 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/.template.config/template.json
@@ -29,6 +29,21 @@
"exclude": [
"Properties/launchSettings.json"
]
+ },
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain)",
+ "exclude": [
+ "Program.cs"
+ ],
+ "rename": {
+ "Program.Main.cs": "Program.cs"
+ }
}
]
}
@@ -156,6 +171,13 @@
"datatype": "bool",
"defaultValue": "false",
"description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth."
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"primaryOutputs": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/Program.Main.cs
new file mode 100644
index 000000000000..6a106499828e
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-CSharp/Program.Main.cs
@@ -0,0 +1,14 @@
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+ var app = builder.Build();
+
+ app.MapGet("/", () => "Hello World!");
+
+ app.Run();
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/dotnetcli.host.json
index 684bc1e734f3..ded3cbf35f23 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/dotnetcli.host.json
@@ -14,6 +14,10 @@
"ExcludeLaunchSettings": {
"longName": "exclude-launch-settings",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
},
"usageExamples": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/ide.host.json
index 2cb41909519c..5c3a869512fd 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/ide.host.json
@@ -2,5 +2,11 @@
"$schema": "http://json.schemastore.org/vs-2017.3.host",
"order": 500,
"icon": "ide/gRPC.png",
- "supportsDocker": true
+ "supportsDocker": true,
+ "symbolInfo": [
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
+ }
+ ]
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json
index 2fd14ec9251f..fc4288d87384 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/.template.config/template.json
@@ -26,6 +26,21 @@
"exclude": [
"Properties/launchSettings.json"
]
+ },
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain)",
+ "exclude": [
+ "Program.cs"
+ ],
+ "rename": {
+ "Program.Main.cs": "Program.cs"
+ }
}
]
}
@@ -102,6 +117,13 @@
"fallbackVariableName": "kestrelHttpsPortGenerated"
},
"replaces": "5001"
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"primaryOutputs": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.Main.cs
new file mode 100644
index 000000000000..ec1af1a7e9c6
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.Main.cs
@@ -0,0 +1,25 @@
+using GrpcService_CSharp.Services;
+
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ // Additional configuration is required to successfully run gRPC on macOS.
+ // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
+
+ // Add services to the container.
+ builder.Services.AddGrpc();
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ app.MapGrpcService();
+ app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
+
+ app.Run();
+ }
+}
\ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/dotnetcli.host.json
index 6ce1fbe6eb27..a00ae64f281e 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/dotnetcli.host.json
@@ -85,6 +85,10 @@
"CallsMicrosoftGraph": {
"longName": "calls-graph",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
},
"usageExamples": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/ide.host.json
index f176b8df2a9c..0dc542e09b82 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/ide.host.json
@@ -45,7 +45,10 @@
}
],
"symbolInfo": [
-
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
+ }
],
"disableHttpsSymbol": "NoHttps"
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json
index d7226586d37d..989dbb8ab86b 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/.template.config/template.json
@@ -94,6 +94,21 @@
"exclude": [
"Data/SqlServer/**"
]
+ },
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain)",
+ "exclude": [
+ "Program.cs"
+ ],
+ "rename": {
+ "Program.Main.cs": "Program.cs"
+ }
}
]
}
@@ -406,6 +421,13 @@
"GenerateApiOrGraph": {
"type": "computed",
"value": "(GenerateApi || GenerateGraph)"
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"primaryOutputs": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Program.Main.cs
new file mode 100644
index 000000000000..3a1a1d68cc06
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Program.Main.cs
@@ -0,0 +1,155 @@
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.OpenIdConnect;
+using Microsoft.AspNetCore.Authorization;
+#endif
+#if (WindowsAuth)
+using Microsoft.AspNetCore.Authentication.Negotiate;
+#endif
+#if (IndividualLocalAuth)
+using Microsoft.AspNetCore.Identity;
+#endif
+#if (OrganizationalAuth)
+using Microsoft.AspNetCore.Mvc.Authorization;
+#endif
+#if (IndividualLocalAuth)
+using Microsoft.EntityFrameworkCore;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.Identity.Web;
+using Microsoft.Identity.Web.UI;
+#endif
+#if (MultiOrgAuth)
+using Microsoft.IdentityModel.Tokens;
+#endif
+#if (GenerateGraph)
+using Graph = Microsoft.Graph;
+#endif
+#if (IndividualLocalAuth)
+using Company.WebApplication1.Data;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth || IndividualLocalAuth || MultiOrgAuth || GenerateGraph || WindowsAuth)
+
+#endif
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ // Add services to the container.
+ #if (IndividualLocalAuth)
+ var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
+ builder.Services.AddDbContext(options =>
+ #if (UseLocalDB)
+ options.UseSqlServer(connectionString));
+ #else
+ options.UseSqlite(connectionString));
+ #endif
+ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
+
+ builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
+ .AddEntityFrameworkStores();
+ #elif (OrganizationalAuth)
+ #if (GenerateApiOrGraph)
+ var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
+
+ #endif
+ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+ #if (GenerateApiOrGraph)
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ #if (GenerateApi)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ #if (GenerateGraph)
+ .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
+ #endif
+ #elif (IndividualB2CAuth)
+ #if (GenerateApi)
+ var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
+
+ #endif
+ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+ #if (GenerateApi)
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"));
+ #endif
+ #endif
+ #if (OrganizationalAuth)
+
+ builder.Services.AddAuthorization(options =>
+ {
+ // By default, all incoming requests will be authorized according to the default policy.
+ options.FallbackPolicy = options.DefaultPolicy;
+ });
+ builder.Services.AddRazorPages()
+ .AddMicrosoftIdentityUI();
+ #elif (IndividualB2CAuth)
+ builder.Services.AddRazorPages()
+ .AddMicrosoftIdentityUI();
+ #elif (WindowsAuth)
+
+ builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
+ .AddNegotiate();
+
+ builder.Services.AddAuthorization(options =>
+ {
+ // By default, all incoming requests will be authorized according to the default policy.
+ options.FallbackPolicy = options.DefaultPolicy;
+ });
+ builder.Services.AddRazorPages();
+ #else
+ builder.Services.AddRazorPages();
+ #endif
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ #if (IndividualLocalAuth)
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseMigrationsEndPoint();
+ }
+ else
+ #else
+ if (!app.Environment.IsDevelopment())
+ #endif
+ {
+ app.UseExceptionHandler("/Error");
+ #if (RequiresHttps)
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
+
+ app.UseHttpsRedirection();
+ #else
+ }
+ #endif
+ app.UseStaticFiles();
+
+ app.UseRouting();
+
+ #if (OrganizationalAuth || IndividualAuth || WindowsAuth)
+ app.UseAuthentication();
+ #endif
+ app.UseAuthorization();
+
+ app.MapRazorPages();
+ #if (IndividualB2CAuth || OrganizationalAuth)
+ app.MapControllers();
+ #endif
+
+ app.Run();
+ }
+}
\ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/dotnetcli.host.json
index 3c34f96f85e3..02abad32e7eb 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/dotnetcli.host.json
@@ -85,6 +85,10 @@
"CallsMicrosoftGraph": {
"longName": "calls-graph",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
},
"usageExamples": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/ide.host.json
index 12bb6ec5db17..995a75bea9a0 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/ide.host.json
@@ -45,7 +45,10 @@
}
],
"symbolInfo": [
-
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
+ }
],
"disableHttpsSymbol": "NoHttps"
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json
index b67fe4f719ef..558a4e818d35 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/.template.config/template.json
@@ -90,6 +90,21 @@
"exclude": [
"Data/SqlServer/**"
]
+ },
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain)",
+ "exclude": [
+ "Program.cs"
+ ],
+ "rename": {
+ "Program.Main.cs": "Program.cs"
+ }
}
]
}
@@ -402,6 +417,13 @@
"GenerateApiOrGraph": {
"type": "computed",
"value": "(GenerateApi || GenerateGraph)"
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"primaryOutputs": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Program.Main.cs
new file mode 100644
index 000000000000..5fda9092d06a
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Program.Main.cs
@@ -0,0 +1,159 @@
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Authentication.OpenIdConnect;
+#endif
+#if (WindowsAuth)
+using Microsoft.AspNetCore.Authentication.Negotiate;
+#endif
+#if (IndividualLocalAuth)
+using Microsoft.AspNetCore.Identity;
+#endif
+#if (OrganizationalAuth)
+using Microsoft.AspNetCore.Mvc.Authorization;
+#endif
+#if (IndividualLocalAuth)
+using Microsoft.EntityFrameworkCore;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.Identity.Web;
+using Microsoft.Identity.Web.UI;
+#endif
+#if (MultiOrgAuth)
+using Microsoft.IdentityModel.Tokens;
+#endif
+#if (GenerateGraph)
+using Graph = Microsoft.Graph;
+#endif
+#if (IndividualLocalAuth)
+using Company.WebApplication1.Data;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth || IndividualLocalAuth || MultiOrgAuth || GenerateGraph || WindowsAuth)
+
+#endif
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ // Add services to the container.
+ #if (IndividualLocalAuth)
+ var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
+ builder.Services.AddDbContext(options =>
+ #if (UseLocalDB)
+ options.UseSqlServer(connectionString));
+ #else
+ options.UseSqlite(connectionString));
+ #endif
+ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
+
+ builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true)
+ .AddEntityFrameworkStores();
+ #elif (OrganizationalAuth)
+ #if (GenerateApiOrGraph)
+ var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
+
+ #endif
+ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+ #if (GenerateApiOrGraph)
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ #if (GenerateApi)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ #if (GenerateGraph)
+ .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
+ #endif
+ #elif (IndividualB2CAuth)
+ #if (GenerateApi)
+ var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ');
+
+ #endif
+ builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
+ #if (GenerateApi)
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"));
+ #endif
+ #endif
+ #if (OrganizationalAuth)
+
+ builder.Services.AddControllersWithViews(options =>
+ {
+ var policy = new AuthorizationPolicyBuilder()
+ .RequireAuthenticatedUser()
+ .Build();
+ options.Filters.Add(new AuthorizeFilter(policy));
+ });
+ #else
+ builder.Services.AddControllersWithViews();
+ #endif
+ #if (OrganizationalAuth || IndividualB2CAuth)
+ builder.Services.AddRazorPages()
+ .AddMicrosoftIdentityUI();
+ #endif
+ #if (WindowsAuth)
+
+ builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
+ .AddNegotiate();
+
+ builder.Services.AddAuthorization(options =>
+ {
+ // By default, all incoming requests will be authorized according to the default policy.
+ options.FallbackPolicy = options.DefaultPolicy;
+ });
+ builder.Services.AddRazorPages();
+ #endif
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ #if (IndividualLocalAuth)
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseMigrationsEndPoint();
+ }
+ else
+ #else
+ if (!app.Environment.IsDevelopment())
+ #endif
+ {
+ app.UseExceptionHandler("/Home/Error");
+ #if (RequiresHttps)
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
+
+ app.UseHttpsRedirection();
+ #else
+ }
+ #endif
+ app.UseStaticFiles();
+
+ app.UseRouting();
+
+ #if (OrganizationalAuth || IndividualAuth || WindowsAuth)
+ app.UseAuthentication();
+ #endif
+ app.UseAuthorization();
+
+ app.MapControllerRoute(
+ name: "default",
+ pattern: "{controller=Home}/{action=Index}/{id?}");
+ #if (OrganizationalAuth || IndividualAuth)
+ app.MapRazorPages();
+ #endif
+
+ app.Run();
+ }
+}
\ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json
index 9b97a6182066..79217f16803f 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json
@@ -85,6 +85,10 @@
"DisableOpenAPI": {
"longName": "no-openapi",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
},
"usageExamples": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/ide.host.json
index c8fdd64b6acd..19355a4eecac 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/ide.host.json
@@ -55,6 +55,10 @@
"invertBoolean": true,
"isVisible": true,
"defaultValue": true
+ },
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
}
],
"disableHttpsSymbol": "NoHttps"
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json
index ee1f9c886b38..5c9b53aedcd1 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json
@@ -37,6 +37,21 @@
"Properties/launchSettings.json"
]
},
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain && !UseMinimalAPIs)",
+ "exclude": [
+ "Program.cs"
+ ],
+ "rename": {
+ "Program.Main.cs": "Program.cs"
+ }
+ },
{
"condition": "(UseMinimalAPIs)",
"exclude": [
@@ -364,6 +379,13 @@
"UseControllers": {
"type": "computed",
"value": "(!UseMinimalAPIs)"
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"primaryOutputs": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs
new file mode 100644
index 000000000000..a0c9ad67e817
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs
@@ -0,0 +1,95 @@
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+#endif
+#if (WindowsAuth)
+using Microsoft.AspNetCore.Authentication.Negotiate;
+#endif
+#if (GenerateGraph)
+using Graph = Microsoft.Graph;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth)
+using Microsoft.Identity.Web;
+#endif
+#if (OrganizationalAuth || IndividualB2CAuth || GenerateGraph || WindowsAuth)
+
+#endif
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+
+ // Add services to the container.
+ #if (OrganizationalAuth)
+ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ #if (GenerateApiOrGraph)
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
+ .EnableTokenAcquisitionToCallDownstreamApi()
+ #if (GenerateApi)
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ #if (GenerateGraph)
+ .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
+ #endif
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
+ #endif
+ #elif (IndividualB2CAuth)
+ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+ #if (GenerateApi)
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C"))
+ .EnableTokenAcquisitionToCallDownstreamApi()
+ .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
+ .AddInMemoryTokenCaches();
+ #else
+ .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C"));
+ #endif
+ #endif
+
+ builder.Services.AddControllers();
+ #if (EnableOpenAPI)
+ // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddSwaggerGen();
+ #endif
+ #if (WindowsAuth)
+
+ builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
+ .AddNegotiate();
+
+ builder.Services.AddAuthorization(options =>
+ {
+ // By default, all incoming requests will be authorized according to the default policy.
+ options.FallbackPolicy = options.DefaultPolicy;
+ });
+ #endif
+
+ var app = builder.Build();
+
+ // Configure the HTTP request pipeline.
+ #if (EnableOpenAPI)
+ if (app.Environment.IsDevelopment())
+ {
+ app.UseSwagger();
+ app.UseSwaggerUI();
+ }
+ #endif
+ #if (RequiresHttps)
+
+ app.UseHttpsRedirection();
+ #endif
+
+ #if (OrganizationalAuth || IndividualAuth || WindowsAuth)
+ app.UseAuthentication();
+ #endif
+ app.UseAuthorization();
+
+ app.MapControllers();
+
+ app.Run();
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/dotnetcli.host.json
index b1cf98e39bb8..36493a3a4ac7 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/dotnetcli.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/dotnetcli.host.json
@@ -11,6 +11,10 @@
"ExcludeLaunchSettings": {
"longName": "exclude-launch-settings",
"shortName": ""
+ },
+ "UseProgramMain": {
+ "longName": "use-program-main",
+ "shortName": ""
}
},
"usageExamples": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/ide.host.json
index 13a025d034fa..59f260a583df 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/ide.host.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/ide.host.json
@@ -3,4 +3,10 @@
"order": 300,
"icon": "ide/Worker.png",
"supportsDocker": true,
+ "symbolInfo": [
+ {
+ "id": "UseProgramMain",
+ "isVisible": true
+ }
+ ]
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json
index 19358bf86a30..d2789afed3ff 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/.template.config/template.json
@@ -30,6 +30,21 @@
"exclude": [
"Properties/launchSettings.json"
]
+ },
+ {
+ "condition": "(!UseProgramMain)",
+ "exclude": [
+ "Program.Main.cs"
+ ]
+ },
+ {
+ "condition": "(UseProgramMain)",
+ "exclude": [
+ "Program.cs"
+ ],
+ "rename": {
+ "Program.Main.cs": "Program.cs"
+ }
}
]
}
@@ -67,6 +82,13 @@
"datatype": "bool",
"description": "If specified, skips the automatic restore of the project on create.",
"defaultValue": "false"
+ },
+ "UseProgramMain": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "Do not use top-level statements",
+ "description": "Whether to generate an explicit Program class and Main method instead of top-level statements."
}
},
"primaryOutputs": [
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/Program.Main.cs
new file mode 100644
index 000000000000..d69747f38d51
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/Program.Main.cs
@@ -0,0 +1,18 @@
+using Company.Application1;
+
+namespace Company.WebApplication1;
+
+public class Program
+{
+ public static void Main(string[] args)
+ {
+ IHost host = Host.CreateDefaultBuilder(args)
+ .ConfigureServices(services =>
+ {
+ services.AddHostedService();
+ })
+ .Build();
+
+ host.Run();
+ }
+}
\ No newline at end of file
diff --git a/src/ProjectTemplates/scripts/Run-AngularProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-AngularProgramMain-Locally.ps1
new file mode 100644
index 000000000000..93127bb08be0
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-AngularProgramMain-Locally.ps1
@@ -0,0 +1,12 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "angular" "angular --use-program-main" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-BlazorProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-BlazorProgramMain-Locally.ps1
new file mode 100644
index 000000000000..3d9fdd64a70d
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-BlazorProgramMain-Locally.ps1
@@ -0,0 +1,13 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+# This script packages, installs and creates a template to help with rapid iteration in the templating area.
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "blazorserver" "blazorserver --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-BlazorWasmProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-BlazorWasmProgramMain-Locally.ps1
new file mode 100644
index 000000000000..7c8755a8bba3
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-BlazorWasmProgramMain-Locally.ps1
@@ -0,0 +1,13 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+# This script packages, installs and creates a template to help with rapid iteration in the templating area.
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "blazorwasm" "blazorwasm --use-program-main --hosted --auth Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $true
diff --git a/src/ProjectTemplates/scripts/Run-EmptyWebProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-EmptyWebProgramMain-Locally.ps1
new file mode 100644
index 000000000000..7453063baf21
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-EmptyWebProgramMain-Locally.ps1
@@ -0,0 +1,12 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "web" "web --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-RazorProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-RazorProgramMain-Locally.ps1
new file mode 100644
index 000000000000..4224cf985dd2
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-RazorProgramMain-Locally.ps1
@@ -0,0 +1,9 @@
+#!/usr/bin/env powershell
+#requires -version 4
+
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "webapp" "webapp -au Individual --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-ReactProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-ReactProgramMain-Locally.ps1
new file mode 100644
index 000000000000..df61a5a11740
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-ReactProgramMain-Locally.ps1
@@ -0,0 +1,12 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "react" "react --use-program-main" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-StarterwebProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-StarterwebProgramMain-Locally.ps1
new file mode 100644
index 000000000000..076106d3e861
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-StarterwebProgramMain-Locally.ps1
@@ -0,0 +1,12 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "mvc" "mvc -au Individual --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-WebApiProgamMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-WebApiProgamMain-Locally.ps1
new file mode 100644
index 000000000000..41f794b7eaaf
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-WebApiProgamMain-Locally.ps1
@@ -0,0 +1,12 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "webapi" "webapi --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Run-WorkerProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-WorkerProgramMain-Locally.ps1
new file mode 100644
index 000000000000..9e0aa3d4607b
--- /dev/null
+++ b/src/ProjectTemplates/scripts/Run-WorkerProgramMain-Locally.ps1
@@ -0,0 +1,12 @@
+#!/usr/bin/env pwsh
+#requires -version 4
+
+[CmdletBinding(PositionalBinding = $false)]
+param()
+
+Set-StrictMode -Version 2
+$ErrorActionPreference = 'Stop'
+
+. $PSScriptRoot\Test-Template.ps1
+
+Test-Template "worker" "worker --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false
diff --git a/src/ProjectTemplates/scripts/Test-Template.ps1 b/src/ProjectTemplates/scripts/Test-Template.ps1
index 349174ad3d06..05cf0ef7128b 100644
--- a/src/ProjectTemplates/scripts/Test-Template.ps1
+++ b/src/ProjectTemplates/scripts/Test-Template.ps1
@@ -64,7 +64,9 @@ function Test-Template($templateName, $templateArgs, $templateNupkg, $isBlazorWa
if ($isBlazorWasmHosted) {
Push-Location Server
}
- dotnet.exe ef migrations add mvc
+ if ($templateArgs -match '-au') {
+ dotnet.exe ef migrations add mvc
+ }
dotnet.exe publish --configuration Release
Set-Location .\bin\Release\net6.0\publish
if ($isBlazorWasm -eq $false) {
diff --git a/src/ProjectTemplates/test/BlazorServerTemplateTest.cs b/src/ProjectTemplates/test/BlazorServerTemplateTest.cs
index 432c85043bd7..104f2304020d 100644
--- a/src/ProjectTemplates/test/BlazorServerTemplateTest.cs
+++ b/src/ProjectTemplates/test/BlazorServerTemplateTest.cs
@@ -26,18 +26,26 @@ public BlazorServerTemplateTest(ProjectFactoryFixture projectFactory)
[Fact]
public Task BlazorServerTemplateWorks_NoAuth() => CreateBuildPublishAsync("blazorservernoauth");
+ [Fact]
+ public Task BlazorServerTemplateWorks_ProgamMainNoAuth() => CreateBuildPublishAsync("blazorservernoauth", args: new [] { "--use-program-main" });
+
[Theory]
- [InlineData(true)]
- [InlineData(false)]
+ [InlineData(true, null)]
+ [InlineData(true, new string[] { "--use-program-main" })]
+ [InlineData(false, null)]
+ [InlineData(false, new string[] { "--use-program-main" })]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/30825", Queues = "All.OSX")]
- public Task BlazorServerTemplateWorks_IndividualAuth(bool useLocalDB) => CreateBuildPublishAsync("blazorserverindividual" + (useLocalDB ? "uld" : ""));
+ public Task BlazorServerTemplateWorks_IndividualAuth(bool useLocalDB, string[] args) => CreateBuildPublishAsync("blazorserverindividual" + (useLocalDB ? "uld" : "", args: args));
[Theory]
[InlineData("IndividualB2C", null)]
[InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+ [InlineData("IndividualB2C", new string[] { "--use-program-main", "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", null)]
[InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+ [InlineData("SingleOrg", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", new string[] { "--calls-graph" })]
+ [InlineData("SingleOrg", new string[] { "--use-program-main --calls-graph" })]
public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish(string auth, string[] args)
=> CreateBuildPublishAsync("blazorserveridweb" + Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(), auth, args);
diff --git a/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs
index 97a580dddd8b..dae6148d213a 100644
--- a/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs
+++ b/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs
@@ -37,6 +37,9 @@ public async Task BlazorWasmStandaloneTemplateCanCreateBuildPublish()
[Fact]
public Task BlazorWasmHostedTemplateCanCreateBuildPublish() => CreateBuildPublishAsync("blazorhosted", args: new[] { "--hosted" }, serverProject: true);
+ [Fact]
+ public Task BlazorWasmHostedTemplateWithProgamMainCanCreateBuildPublish() => CreateBuildPublishAsync("blazorhosted", args: new[] { "--use-program-main", "--hosted" }, serverProject: true);
+
[Fact]
public Task BlazorWasmStandalonePwaTemplateCanCreateBuildPublish() => CreateBuildPublishAsync("blazorstandalonepwa", args: new[] { "--pwa" });
diff --git a/src/ProjectTemplates/test/EmptyWebTemplateTest.cs b/src/ProjectTemplates/test/EmptyWebTemplateTest.cs
index e90ccbf5b94c..7468a587b41e 100644
--- a/src/ProjectTemplates/test/EmptyWebTemplateTest.cs
+++ b/src/ProjectTemplates/test/EmptyWebTemplateTest.cs
@@ -38,17 +38,24 @@ public async Task EmptyWebTemplateCSharp()
await EmtpyTemplateCore(languageOverride: null);
}
+ [ConditionalFact]
+ [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+ public async Task EmptyWebTemplateProgramMainCSharp()
+ {
+ await EmtpyTemplateCore(languageOverride: null, args: new [] { "--use-program-main" });
+ }
+
[Fact]
public async Task EmptyWebTemplateFSharp()
{
await EmtpyTemplateCore("F#");
}
- private async Task EmtpyTemplateCore(string languageOverride)
+ private async Task EmtpyTemplateCore(string languageOverride, string[] args = null)
{
var project = await ProjectFactory.GetOrCreateProject("empty" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
- var createResult = await project.RunDotNetNewAsync("web", language: languageOverride);
+ var createResult = await project.RunDotNetNewAsync("web", args: args, language: languageOverride);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
// Avoid the F# compiler. See https://github.com/dotnet/aspnetcore/issues/14022
diff --git a/src/ProjectTemplates/test/GrpcTemplateTest.cs b/src/ProjectTemplates/test/GrpcTemplateTest.cs
index 585ac0bfb457..d26d0f8623a6 100644
--- a/src/ProjectTemplates/test/GrpcTemplateTest.cs
+++ b/src/ProjectTemplates/test/GrpcTemplateTest.cs
@@ -34,14 +34,17 @@ public ITestOutputHelper Output
}
}
- [ConditionalFact]
+ [ConditionalTheory]
[SkipOnHelix("Not supported queues", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
[SkipOnAlpine("https://github.com/grpc/grpc/issues/18338")]
- public async Task GrpcTemplate()
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task GrpcTemplate(bool useProgramMain)
{
var project = await ProjectFactory.GetOrCreateProject("grpc", Output);
- var createResult = await project.RunDotNetNewAsync("grpc");
+ var args = useProgramMain ? new [] { "--use-program-main" } : null;
+ var createResult = await project.RunDotNetNewAsync("grpc", args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
var publishResult = await project.RunDotNetPublishAsync();
diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs
index 47045d6972aa..a5d6bec38066 100644
--- a/src/ProjectTemplates/test/MvcTemplateTest.cs
+++ b/src/ProjectTemplates/test/MvcTemplateTest.cs
@@ -43,11 +43,15 @@ public ITestOutputHelper Output
[SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
public async Task MvcTemplate_NoAuthCSharp() => await MvcTemplateCore(languageOverride: null);
- private async Task MvcTemplateCore(string languageOverride)
+ [ConditionalFact]
+ [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+ public async Task MvcTemplate_ProgramMainNoAuthCSharp() => await MvcTemplateCore(languageOverride: null, new [] { "--use-program-main" });
+
+ private async Task MvcTemplateCore(string languageOverride, string[] args = null)
{
var project = await ProjectFactory.GetOrCreateProject("mvcnoauth" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output);
- var createResult = await project.RunDotNetNewAsync("mvc", language: languageOverride);
+ var createResult = await project.RunDotNetNewAsync("mvc", language: languageOverride, args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
var projectExtension = languageOverride == "F#" ? "fsproj" : "csproj";
@@ -75,10 +79,10 @@ private async Task MvcTemplateCore(string languageOverride)
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", project, buildResult));
IEnumerable menuLinks = new List {
- PageUrls.HomeUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyFullUrl
- };
+ PageUrls.HomeUrl,
+ PageUrls.HomeUrl,
+ PageUrls.PrivacyFullUrl
+ };
var footerLinks = new string[] { PageUrls.PrivacyFullUrl };
@@ -116,14 +120,17 @@ private async Task MvcTemplateCore(string languageOverride)
}
[ConditionalTheory]
- [InlineData(true)]
- [InlineData(false)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
[SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
- public async Task MvcTemplate_IndividualAuth(bool useLocalDB)
+ public async Task MvcTemplate_IndividualAuth(bool useLocalDB, bool useProgramMain)
{
var project = await ProjectFactory.GetOrCreateProject("mvcindividual" + (useLocalDB ? "uld" : ""), Output);
- var createResult = await project.RunDotNetNewAsync("mvc", auth: "Individual", useLocalDB: useLocalDB);
+ var args = useProgramMain ? new [] { "--use-program-main" } : null;
+ var createResult = await project.RunDotNetNewAsync("mvc", auth: "Individual", useLocalDB: useLocalDB, args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
var projectFileContents = project.ReadFile($"{project.ProjectName}.csproj");
@@ -148,72 +155,72 @@ public async Task MvcTemplate_IndividualAuth(bool useLocalDB)
// Note: if any links are updated here, RazorPagesTemplateTest.cs should be updated as well
var pages = new List {
- new Page
- {
- Url = PageUrls.ForgotPassword,
- Links = new string [] {
- PageUrls.HomeUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyUrl,
- PageUrls.RegisterUrl,
- PageUrls.LoginUrl,
- PageUrls.PrivacyUrl
- }
- },
- new Page
- {
- Url = PageUrls.HomeUrl,
- Links = new string[] {
- PageUrls.HomeUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyUrl,
- PageUrls.RegisterUrl,
- PageUrls.LoginUrl,
- PageUrls.DocsUrl,
- PageUrls.PrivacyUrl
- }
- },
- new Page
- {
- Url = PageUrls.PrivacyFullUrl,
- Links = new string[] {
- PageUrls.HomeUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyUrl,
- PageUrls.RegisterUrl,
- PageUrls.LoginUrl,
- PageUrls.PrivacyUrl
- }
- },
- new Page
- {
- Url = PageUrls.LoginUrl,
- Links = new string[] {
- PageUrls.HomeUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyUrl,
- PageUrls.RegisterUrl,
- PageUrls.LoginUrl,
- PageUrls.ForgotPassword,
- PageUrls.RegisterUrl,
- PageUrls.ResendEmailConfirmation,
- PageUrls.ExternalArticle,
- PageUrls.PrivacyUrl }
- },
- new Page
- {
- Url = PageUrls.RegisterUrl,
- Links = new string [] {
- PageUrls.HomeUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyUrl,
- PageUrls.RegisterUrl,
- PageUrls.LoginUrl,
- PageUrls.ExternalArticle,
- PageUrls.PrivacyUrl
+ new Page
+ {
+ Url = PageUrls.ForgotPassword,
+ Links = new string [] {
+ PageUrls.HomeUrl,
+ PageUrls.HomeUrl,
+ PageUrls.PrivacyUrl,
+ PageUrls.RegisterUrl,
+ PageUrls.LoginUrl,
+ PageUrls.PrivacyUrl
+ }
+ },
+ new Page
+ {
+ Url = PageUrls.HomeUrl,
+ Links = new string[] {
+ PageUrls.HomeUrl,
+ PageUrls.HomeUrl,
+ PageUrls.PrivacyUrl,
+ PageUrls.RegisterUrl,
+ PageUrls.LoginUrl,
+ PageUrls.DocsUrl,
+ PageUrls.PrivacyUrl
+ }
+ },
+ new Page
+ {
+ Url = PageUrls.PrivacyFullUrl,
+ Links = new string[] {
+ PageUrls.HomeUrl,
+ PageUrls.HomeUrl,
+ PageUrls.PrivacyUrl,
+ PageUrls.RegisterUrl,
+ PageUrls.LoginUrl,
+ PageUrls.PrivacyUrl
+ }
+ },
+ new Page
+ {
+ Url = PageUrls.LoginUrl,
+ Links = new string[] {
+ PageUrls.HomeUrl,
+ PageUrls.HomeUrl,
+ PageUrls.PrivacyUrl,
+ PageUrls.RegisterUrl,
+ PageUrls.LoginUrl,
+ PageUrls.ForgotPassword,
+ PageUrls.RegisterUrl,
+ PageUrls.ResendEmailConfirmation,
+ PageUrls.ExternalArticle,
+ PageUrls.PrivacyUrl }
+ },
+ new Page
+ {
+ Url = PageUrls.RegisterUrl,
+ Links = new string [] {
+ PageUrls.HomeUrl,
+ PageUrls.HomeUrl,
+ PageUrls.PrivacyUrl,
+ PageUrls.RegisterUrl,
+ PageUrls.LoginUrl,
+ PageUrls.ExternalArticle,
+ PageUrls.PrivacyUrl
+ }
}
- }
- };
+ };
using (var aspNetProcess = project.StartBuiltProjectAsync())
{
@@ -234,67 +241,44 @@ public async Task MvcTemplate_IndividualAuth(bool useLocalDB)
}
}
- [ConditionalFact(Skip = "https://github.com/dotnet/aspnetcore/issues/25103")]
- [SkipOnHelix("cert failure", Queues = "All.OSX")]
+ [ConditionalFact]
+ [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] // Running these requires the rid-specific runtime pack to be available which is not consistent in all our platform builds.
+ [SkipOnHelix("cert failure", Queues = "All.OSX;" + HelixConstants.Windows10Arm64)]
public async Task MvcTemplate_SingleFileExe()
{
// This test verifies publishing an MVC app as a single file exe works. We'll limit testing
// this to a few operating systems to make our lives easier.
- string runtimeIdentifer;
- if (OperatingSystem.IsWindows())
- {
- runtimeIdentifer = "win-x64";
- }
- else if (OperatingSystem.IsLinux())
- {
- runtimeIdentifer = "linux-x64";
- }
- else
- {
- return;
- }
-
+ var runtimeIdentifer = "win-x64";
var project = await ProjectFactory.GetOrCreateProject("mvcsinglefileexe", Output);
project.RuntimeIdentifier = runtimeIdentifer;
- var createResult = await project.RunDotNetNewAsync("mvc", auth: "Individual", useLocalDB: true);
+ var createResult = await project.RunDotNetNewAsync("mvc");
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
var publishResult = await project.RunDotNetPublishAsync(additionalArgs: $"/p:PublishSingleFile=true -r {runtimeIdentifer}", noRestore: false);
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", project, publishResult));
- var pages = new[]
+ var menuLinks = new[]
+ {
+ PageUrls.HomeUrl,
+ PageUrls.HomeUrl,
+ PageUrls.PrivacyFullUrl
+ };
+
+ var footerLinks = new[] { PageUrls.PrivacyFullUrl };
+
+ var pages = new List
{
new Page
{
- // Verify a view from the app works
Url = PageUrls.HomeUrl,
- Links = new []
- {
- PageUrls.HomeUrl,
- PageUrls.RegisterUrl,
- PageUrls.LoginUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyUrl,
- PageUrls.DocsUrl,
- PageUrls.PrivacyUrl
- }
+ Links = menuLinks.Append(PageUrls.DocsUrl).Concat(footerLinks),
},
new Page
{
- // Verify a view from a RCL (in this case IdentityUI) works
- Url = PageUrls.RegisterUrl,
- Links = new []
- {
- PageUrls.HomeUrl,
- PageUrls.RegisterUrl,
- PageUrls.LoginUrl,
- PageUrls.HomeUrl,
- PageUrls.PrivacyUrl,
- PageUrls.ExternalArticle,
- PageUrls.PrivacyUrl
- }
- },
+ Url = PageUrls.PrivacyFullUrl,
+ Links = menuLinks.Concat(footerLinks),
+ }
};
using var aspNetProcess = project.StartPublishedProjectAsync(usePublishedAppHost: true);
diff --git a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
index 1af0a68366ff..3412233f5dd5 100644
--- a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
+++ b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
@@ -34,13 +34,16 @@ public ITestOutputHelper Output
}
}
- [ConditionalFact]
+ [ConditionalTheory]
[SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
- public async Task RazorPagesTemplate_NoAuth()
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task RazorPagesTemplate_NoAuth(bool useProgramMain)
{
var project = await ProjectFactory.GetOrCreateProject("razorpagesnoauth", Output);
- var createResult = await project.RunDotNetNewAsync("razor");
+ var args = useProgramMain ? new [] { "--use-program-main" } : null;
+ var createResult = await project.RunDotNetNewAsync("razor", args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("razor", project, createResult));
var projectFileContents = ReadFile(project.TemplateOutputDir, $"{project.ProjectName}.csproj");
@@ -104,14 +107,17 @@ public async Task RazorPagesTemplate_NoAuth()
}
[ConditionalTheory]
- [InlineData(false)]
- [InlineData(true)]
+ [InlineData(false, false)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(true, true)]
[SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
- public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB)
+ public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB, bool useProgramMain)
{
var project = await ProjectFactory.GetOrCreateProject("razorpagesindividual" + (useLocalDB ? "uld" : ""), Output);
- var createResult = await project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB);
+ var args = useProgramMain ? new [] { "--use-program-main" } : null;
+ var createResult = await project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB, args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
var projectFileContents = ReadFile(project.TemplateOutputDir, $"{project.ProjectName}.csproj");
@@ -226,12 +232,15 @@ public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB)
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
[InlineData("IndividualB2C", null)]
[InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+ [InlineData("IndividualB2C", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", null)]
[InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+ [InlineData("SingleOrg", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
public Task RazorPagesTemplate_IdentityWeb_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args);
[ConditionalTheory]
[InlineData("SingleOrg", new string[] { "--calls-graph" })]
+ [InlineData("SingleOrg", new string[] { "--use-program-main --calls-graph" })]
public Task RazorPagesTemplate_IdentityWeb_BuildsAndPublishes_WithSingleOrg(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args);
private async Task BuildAndPublishRazorPagesTemplate(string auth, string[] args)
diff --git a/src/ProjectTemplates/test/WebApiTemplateTest.cs b/src/ProjectTemplates/test/WebApiTemplateTest.cs
index ddc6e185320e..f727abcc92c0 100644
--- a/src/ProjectTemplates/test/WebApiTemplateTest.cs
+++ b/src/ProjectTemplates/test/WebApiTemplateTest.cs
@@ -35,10 +35,15 @@ public ITestOutputHelper Output
[ConditionalTheory]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
[InlineData("IndividualB2C", null)]
+ [InlineData("IndividualB2C", new string[] { "--use-program-main" })]
[InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+ [InlineData("IndividualB2C", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", null)]
+ [InlineData("SingleOrg", new string[] { "--use-program-main" })]
[InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
+ [InlineData("SingleOrg", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", new string[] { "--calls-graph" })]
+ [InlineData("SingleOrg", new string[] { "--use-program-main --calls-graph" })]
public Task WebApiTemplateCSharp_IdentityWeb_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args);
[Fact]
@@ -50,11 +55,18 @@ public ITestOutputHelper Output
[ConditionalFact]
[SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
- public async Task WebApiTemplateCSharp_WithoutOpenAPI()
+ public Task WebApiTemplateProgramMainCSharp() => WebApiTemplateCore(languageOverride: null, args: new [] { "--use-program-main" });
+
+ [ConditionalTheory]
+ [InlineData(false)]
+ [InlineData(true)]
+ [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)]
+ public async Task WebApiTemplateCSharp_WithoutOpenAPI(bool useProgramMain)
{
var project = await FactoryFixture.GetOrCreateProject("webapinoopenapi", Output);
- var createResult = await project.RunDotNetNewAsync("webapi", args: new[] { "--no-openapi" });
+ var args = useProgramMain ? new[] { "--use-program-main --no-openapi" } : new[] { "--no-openapi" };
+ var createResult = await project.RunDotNetNewAsync("webapi", args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
var buildResult = await project.RunDotNetBuildAsync();
@@ -68,7 +80,7 @@ public async Task WebApiTemplateCSharp_WithoutOpenAPI()
await aspNetProcess.AssertNotFound("swagger");
}
- private async Task PublishAndBuildWebApiTemplate(string languageOverride, string auth, string[] args)
+ private async Task PublishAndBuildWebApiTemplate(string languageOverride, string auth, string[] args = null)
{
var project = await FactoryFixture.GetOrCreateProject("webapi" + (languageOverride == "F#" ? "fsharp" : "csharp") + Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(), Output);
@@ -94,9 +106,9 @@ private async Task PublishAndBuildWebApiTemplate(string languageOverrid
return project;
}
- private async Task WebApiTemplateCore(string languageOverride)
+ private async Task WebApiTemplateCore(string languageOverride, string[] args = null)
{
- var project = await PublishAndBuildWebApiTemplate(languageOverride, null, null);
+ var project = await PublishAndBuildWebApiTemplate(languageOverride, null, args);
// Avoid the F# compiler. See https://github.com/dotnet/aspnetcore/issues/14022
if (languageOverride != null)
diff --git a/src/ProjectTemplates/test/WorkerTemplateTest.cs b/src/ProjectTemplates/test/WorkerTemplateTest.cs
index 80e67c18738e..ba82b5073c0e 100644
--- a/src/ProjectTemplates/test/WorkerTemplateTest.cs
+++ b/src/ProjectTemplates/test/WorkerTemplateTest.cs
@@ -32,16 +32,17 @@ public ITestOutputHelper Output
[ConditionalTheory]
[OSSkipCondition(OperatingSystems.Linux, SkipReason = "https://github.com/dotnet/sdk/issues/12831")]
- [InlineData("C#")]
- [InlineData("F#")]
+ [InlineData("C#", null)]
+ [InlineData("C#", new string[] { "--use-program-main" })]
+ [InlineData("F#", null)]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25404")]
- public async Task WorkerTemplateAsync(string language)
+ public async Task WorkerTemplateAsync(string language, string[] args)
{
var project = await ProjectFactory.GetOrCreateProject(
$"worker-{ language.ToLowerInvariant()[0] }sharp",
Output);
- var createResult = await project.RunDotNetNewAsync("worker", language: language);
+ var createResult = await project.RunDotNetNewAsync("worker", language: language, args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult));
var publishResult = await project.RunDotNetPublishAsync();
diff --git a/src/Security/CookiePolicy/test/CookieChunkingTests.cs b/src/Security/CookiePolicy/test/CookieChunkingTests.cs
index 735d7e23d554..5c958fe0abcd 100644
--- a/src/Security/CookiePolicy/test/CookieChunkingTests.cs
+++ b/src/Security/CookiePolicy/test/CookieChunkingTests.cs
@@ -129,7 +129,7 @@ public void GetLargeChunkedCookieWithMissingChunk_ThrowingDisabled_NotReassemble
public void DeleteChunkedCookieWithOptions_AllDeleted()
{
HttpContext context = new DefaultHttpContext();
- context.Request.Headers.Append("Cookie", "TestCookie=chunks-7");
+ context.Request.Headers.Append("Cookie", "TestCookie=chunks-7;TestCookieC1=1;TestCookieC2=2;TestCookieC3=3;TestCookieC4=4;TestCookieC5=5;TestCookieC6=6;TestCookieC7=7");
new ChunkingCookieManager().DeleteCookie(context, "TestCookie", new CookieOptions() { Domain = "foo.com", Secure = true });
var cookies = context.Response.Headers["Set-Cookie"];
@@ -147,7 +147,40 @@ public void DeleteChunkedCookieWithOptions_AllDeleted()
}, cookies);
}
+ [Fact]
+ public void DeleteChunkedCookieWithMissingRequestCookies_OnlyPresentCookiesDeleted()
+ {
+ HttpContext context = new DefaultHttpContext();
+ context.Request.Headers.Append("Cookie", "TestCookie=chunks-7;TestCookieC1=1;TestCookieC2=2");
+
+ new ChunkingCookieManager().DeleteCookie(context, "TestCookie", new CookieOptions() { Domain = "foo.com", Secure = true });
+ var cookies = context.Response.Headers["Set-Cookie"];
+ Assert.Equal(3, cookies.Count);
+ Assert.Equal(new[]
+ {
+ "TestCookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; secure",
+ "TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; secure",
+ "TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; secure",
+ }, cookies);
+ }
+ [Fact]
+ public void DeleteChunkedCookieWithMissingRequestCookies_StopsAtMissingChunk()
+ {
+ HttpContext context = new DefaultHttpContext();
+ // C3 is missing so we don't try to delete C4 either.
+ context.Request.Headers.Append("Cookie", "TestCookie=chunks-7;TestCookieC1=1;TestCookieC2=2;TestCookieC4=4");
+
+ new ChunkingCookieManager().DeleteCookie(context, "TestCookie", new CookieOptions() { Domain = "foo.com", Secure = true });
+ var cookies = context.Response.Headers["Set-Cookie"];
+ Assert.Equal(3, cookies.Count);
+ Assert.Equal(new[]
+ {
+ "TestCookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; secure",
+ "TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; secure",
+ "TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; secure",
+ }, cookies);
+ }
[Fact]
public void DeleteChunkedCookieWithOptionsAndResponseCookies_AllDeleted()
diff --git a/src/Servers/HttpSys/HttpSysServer.slnf b/src/Servers/HttpSys/HttpSysServer.slnf
index 3990e33925cb..c546d5797a4a 100644
--- a/src/Servers/HttpSys/HttpSysServer.slnf
+++ b/src/Servers/HttpSys/HttpSysServer.slnf
@@ -4,9 +4,11 @@
"projects": [
"src\\DefaultBuilder\\src\\Microsoft.AspNetCore.csproj",
"src\\Extensions\\Features\\src\\Microsoft.Extensions.Features.csproj",
+ "src\\FileProviders\\Embedded\\src\\Microsoft.Extensions.FileProviders.Embedded.csproj",
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
+ "src\\Hosting\\Server.IntegrationTesting\\src\\Microsoft.AspNetCore.Server.IntegrationTesting.csproj",
"src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
"src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj",
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
diff --git a/src/Servers/HttpSys/src/DelegationRule.cs b/src/Servers/HttpSys/src/DelegationRule.cs
index 1f57f8298558..c454952eea9e 100644
--- a/src/Servers/HttpSys/src/DelegationRule.cs
+++ b/src/Servers/HttpSys/src/DelegationRule.cs
@@ -13,17 +13,19 @@ namespace Microsoft.AspNetCore.Server.HttpSys
public class DelegationRule : IDisposable
{
private readonly ILogger _logger;
- private readonly UrlGroup _urlGroup;
private readonly UrlGroup _sourceQueueUrlGroup;
private bool _disposed;
+
///
/// The name of the Http.Sys request queue
///
public string QueueName { get; }
+
///
/// The URL of the Http.Sys Url Prefix
///
public string UrlPrefix { get; }
+
internal RequestQueue Queue { get; }
internal DelegationRule(UrlGroup sourceQueueUrlGroup, string queueName, string urlPrefix, ILogger logger)
@@ -32,8 +34,7 @@ internal DelegationRule(UrlGroup sourceQueueUrlGroup, string queueName, string u
_logger = logger;
QueueName = queueName;
UrlPrefix = urlPrefix;
- Queue = new RequestQueue(queueName, UrlPrefix, _logger, receiver: true);
- _urlGroup = Queue.UrlGroup;
+ Queue = new RequestQueue(queueName, _logger);
}
///
@@ -51,7 +52,6 @@ public void Dispose()
_sourceQueueUrlGroup.UnSetDelegationProperty(Queue, throwOnError: false);
}
catch (ObjectDisposedException) { /* Server may have been shutdown */ }
- _urlGroup.Dispose();
Queue.Dispose();
}
}
diff --git a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs
index 8e25a8c21c37..89638a222575 100644
--- a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs
+++ b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs
@@ -19,25 +19,16 @@ internal class RequestQueue
private readonly ILogger _logger;
private bool _disposed;
- internal RequestQueue(string requestQueueName, string urlPrefix, ILogger logger, bool receiver)
- : this(urlGroup: null!, requestQueueName, RequestQueueMode.Attach, logger, receiver)
+ internal RequestQueue(string requestQueueName, ILogger logger)
+ : this(urlGroup: null, requestQueueName, RequestQueueMode.Attach, logger, receiver: true)
{
- try
- {
- UrlGroup = new UrlGroup(this, UrlPrefix.Create(urlPrefix), logger);
- }
- catch
- {
- Dispose();
- throw;
- }
}
internal RequestQueue(UrlGroup urlGroup, string? requestQueueName, RequestQueueMode mode, ILogger logger)
: this(urlGroup, requestQueueName, mode, logger, false)
{ }
- private RequestQueue(UrlGroup urlGroup, string? requestQueueName, RequestQueueMode mode, ILogger logger, bool receiver)
+ private RequestQueue(UrlGroup? urlGroup, string? requestQueueName, RequestQueueMode mode, ILogger logger, bool receiver)
{
_mode = mode;
UrlGroup = urlGroup;
@@ -117,10 +108,15 @@ private RequestQueue(UrlGroup urlGroup, string? requestQueueName, RequestQueueMo
internal SafeHandle Handle { get; }
internal ThreadPoolBoundHandle BoundHandle { get; }
- internal UrlGroup UrlGroup { get; }
+ internal UrlGroup? UrlGroup { get; }
internal unsafe void AttachToUrlGroup()
{
+ if (UrlGroup == null)
+ {
+ throw new NotSupportedException("Can't attach when UrlGroup is null");
+ }
+
Debug.Assert(Created);
CheckDisposed();
// Set the association between request queue and url group. After this, requests for registered urls will
@@ -138,6 +134,11 @@ internal unsafe void AttachToUrlGroup()
internal unsafe void DetachFromUrlGroup()
{
+ if (UrlGroup == null)
+ {
+ throw new NotSupportedException("Can't detach when UrlGroup is null");
+ }
+
Debug.Assert(Created);
CheckDisposed();
// Break the association between request queue and url group. After this, requests for registered urls
diff --git a/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs b/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs
index d13264889ddd..59a67ca43195 100644
--- a/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs
+++ b/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs
@@ -41,24 +41,6 @@ internal unsafe UrlGroup(ServerSession serverSession, ILogger logger)
Id = urlGroupId;
}
- internal unsafe UrlGroup(RequestQueue requestQueue, UrlPrefix url, ILogger logger)
- {
- _logger = logger;
-
- ulong urlGroupId = 0;
- _created = false;
- var statusCode = HttpApi.HttpFindUrlGroupId(
- url.FullPrefix, requestQueue.Handle, &urlGroupId);
-
- if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
- {
- throw new HttpSysException((int)statusCode);
- }
-
- Debug.Assert(urlGroupId != 0, "Invalid id returned by HttpCreateUrlGroup");
- Id = urlGroupId;
- }
-
internal ulong Id { get; private set; }
internal unsafe void SetMaxConnections(long maxConnections)
diff --git a/src/Servers/HttpSys/src/RequestProcessing/Request.cs b/src/Servers/HttpSys/src/RequestProcessing/Request.cs
index ffffc050672c..880beebf562d 100644
--- a/src/Servers/HttpSys/src/RequestProcessing/Request.cs
+++ b/src/Servers/HttpSys/src/RequestProcessing/Request.cs
@@ -120,7 +120,7 @@ internal Request(RequestContext requestContext)
internal ulong RawConnectionId { get; }
// No ulongs in public APIs...
- public long ConnectionId => (long)RawConnectionId;
+ public long ConnectionId => RawConnectionId != 0 ? (long)RawConnectionId : (long)UConnectionId;
internal ulong RequestId { get; }
diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs
index fabae04d35e5..9c45a262dac8 100644
--- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs
+++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs
@@ -289,10 +289,13 @@ internal unsafe void Delegate(DelegationRule destination)
PropertyInfoLength = (uint)System.Text.Encoding.Unicode.GetByteCount(destination.UrlPrefix)
};
+ // Passing 0 for delegateUrlGroupId allows http.sys to find the right group for the
+ // URL passed in via the property above. If we passed in the receiver's URL group id
+ // instead of 0, then delegation would fail if the receiver restarted.
statusCode = HttpApi.HttpDelegateRequestEx(source.Handle,
destination.Queue.Handle,
Request.RequestId,
- destination.Queue.UrlGroup.Id,
+ delegateUrlGroupId: 0,
propertyInfoSetSize: 1,
&property);
}
diff --git a/src/Servers/HttpSys/src/ServerDelegationPropertyFeature.cs b/src/Servers/HttpSys/src/ServerDelegationPropertyFeature.cs
index 14aee50615c9..a97520a837d7 100644
--- a/src/Servers/HttpSys/src/ServerDelegationPropertyFeature.cs
+++ b/src/Servers/HttpSys/src/ServerDelegationPropertyFeature.cs
@@ -14,18 +14,23 @@ namespace Microsoft.AspNetCore.Server.HttpSys
internal class ServerDelegationPropertyFeature : IServerDelegationFeature
{
private readonly ILogger _logger;
- private readonly RequestQueue _queue;
+ private readonly UrlGroup _urlGroup;
public ServerDelegationPropertyFeature(RequestQueue queue, ILogger logger)
{
- _queue = queue;
+ if (queue.UrlGroup == null)
+ {
+ throw new ArgumentException($"{nameof(queue)}.UrlGroup can't be null");
+ }
+
+ _urlGroup = queue.UrlGroup;
_logger = logger;
}
public DelegationRule CreateDelegationRule(string queueName, string uri)
{
- var rule = new DelegationRule(_queue.UrlGroup, queueName, uri, _logger);
- _queue.UrlGroup.SetDelegationProperty(rule.Queue);
+ var rule = new DelegationRule(_urlGroup, queueName, uri, _logger);
+ _urlGroup.SetDelegationProperty(rule.Queue);
return rule;
}
}
diff --git a/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs b/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs
index ca9dcf3a0736..9e38c7da8b06 100644
--- a/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs
+++ b/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs
@@ -1,13 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
using System.Net.Http;
-using System.Threading.Tasks;
+using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.HttpSys.Internal;
using Microsoft.AspNetCore.Testing;
-using Xunit;
namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
{
@@ -198,6 +196,72 @@ public async Task UpdateDelegationRuleTest()
destination?.Dispose();
}
+ [ConditionalFact]
+ [DelegateSupportedCondition(true)]
+ public async Task DelegateAfterReceiverRestart()
+ {
+ var queueName = Guid.NewGuid().ToString();
+ using var receiver = Utilities.CreateHttpServer(out var receiverAddress, async httpContext =>
+ {
+ await httpContext.Response.WriteAsync(_expectedResponseString);
+ },
+ options =>
+ {
+ options.RequestQueueName = queueName;
+ });
+
+ DelegationRule destination = default;
+ using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
+ {
+ var delegateFeature = httpContext.Features.Get();
+ delegateFeature.DelegateRequest(destination);
+ return Task.CompletedTask;
+ });
+
+ var delegationProperty = delegator.Features.Get();
+ destination = delegationProperty.CreateDelegationRule(queueName, receiverAddress);
+
+ var responseString = await SendRequestAsync(delegatorAddress);
+ Assert.Equal(_expectedResponseString, responseString);
+
+ // Stop the receiver
+ receiver?.Dispose();
+
+ // Start the receiver again but this time we need to attach to the existing queue.
+ // Due to https://github.com/dotnet/aspnetcore/issues/40359, we have to manually
+ // register URL prefixes and attach the server's queue to them.
+ using var receiverRestarted = (MessagePump)Utilities.CreateHttpServer(out receiverAddress, async httpContext =>
+ {
+ await httpContext.Response.WriteAsync(_expectedResponseString);
+ },
+ options =>
+ {
+ options.RequestQueueName = queueName;
+ options.RequestQueueMode = RequestQueueMode.Attach;
+ options.UrlPrefixes.Clear();
+ options.UrlPrefixes.Add(receiverAddress);
+ });
+ AttachToUrlGroup(receiverRestarted.Listener.RequestQueue);
+ receiverRestarted.Listener.Options.UrlPrefixes.RegisterAllPrefixes(receiverRestarted.Listener.UrlGroup);
+
+ responseString = await SendRequestAsync(delegatorAddress);
+ Assert.Equal(_expectedResponseString, responseString);
+
+ destination?.Dispose();
+ }
+
+ private unsafe void AttachToUrlGroup(RequestQueue requestQueue)
+ {
+ var info = new HttpApiTypes.HTTP_BINDING_INFO();
+ info.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
+ info.RequestQueueHandle = requestQueue.Handle.DangerousGetHandle();
+
+ var infoptr = new IntPtr(&info);
+
+ requestQueue.UrlGroup.SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerBindingProperty,
+ infoptr, (uint)Marshal.SizeOf());
+ }
+
private async Task SendRequestAsync(string uri)
{
using var client = new HttpClient();
diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/StartupTests.cs
index e0b0ab828131..e6acee1a6d43 100644
--- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/StartupTests.cs
+++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/StartupTests.cs
@@ -468,7 +468,6 @@ public async Task RemoveInProcessReference_FailedToFindRequestHandler()
[ConditionalFact]
[RequiresNewHandler]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/40036")]
public async Task StartupTimeoutIsApplied()
{
// From what we can tell, this failure is due to ungraceful shutdown.
diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
index 064da12cb634..3c66964cafd4 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpParser.cs
@@ -38,6 +38,14 @@ public HttpParser(bool showErrorDetails)
public bool ParseRequestLine(TRequestHandler handler, ref SequenceReader reader)
{
+ // Skip any leading \r or \n on the request line. This is not technically allowed,
+ // but apparently there are enough clients relying on this that it's worth allowing.
+ // Peek first as a minor performance optimization; it's a quick inlined check.
+ if (reader.TryPeek(out byte b) && (b == ByteCR || b == ByteLF))
+ {
+ reader.AdvancePastAny(ByteCR, ByteLF);
+ }
+
if (reader.TryReadTo(out ReadOnlySpan requestLine, ByteLF, advancePastDelimiter: true))
{
ParseRequestLine(handler, requestLine);
diff --git a/src/Servers/Kestrel/Core/test/StartLineTests.cs b/src/Servers/Kestrel/Core/test/StartLineTests.cs
index 4c65611e95e4..59fdf6dd0cc1 100644
--- a/src/Servers/Kestrel/Core/test/StartLineTests.cs
+++ b/src/Servers/Kestrel/Core/test/StartLineTests.cs
@@ -6,6 +6,7 @@
using System.IO.Pipelines;
using System.Text;
using Microsoft.AspNetCore.Connections;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
@@ -516,6 +517,55 @@ public void AuthorityForms(string rawTarget, string path, string query)
DifferentFormsWorkTogether();
}
+ public static IEnumerable
+ BeforeTargets="GetAssemblyAttributes"
+ Condition="'$(GenerateLoggingTestingAssemblyAttributes)' != 'false'">
true
false
@@ -24,6 +24,7 @@
<_Parameter2>Microsoft.AspNetCore.Testing
+
<_Parameter1>$(PreserveExistingLogsInOutput)
<_Parameter2>$(TargetFramework)
diff --git a/src/Testing/src/xunit/AspNetTestAssemblyRunner.cs b/src/Testing/src/xunit/AspNetTestAssemblyRunner.cs
index a83446375ddd..1d71bdf939be 100644
--- a/src/Testing/src/xunit/AspNetTestAssemblyRunner.cs
+++ b/src/Testing/src/xunit/AspNetTestAssemblyRunner.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
@@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Testing
{
public class AspNetTestAssemblyRunner : XunitTestAssemblyRunner
{
- private readonly Dictionary _assemblyFixtureMappings = new Dictionary();
+ private readonly Dictionary _assemblyFixtureMappings = new();
public AspNetTestAssemblyRunner(
ITestAssembly testAssembly,
@@ -26,6 +27,9 @@ public AspNetTestAssemblyRunner(
{
}
+ // internal for testing
+ internal IEnumerable