Fix link generation of routes with default values (#8616)

This commit is contained in:
James Newton-King 2018-10-23 16:10:43 +13:00 committed by GitHub
parent 76a30b0911
commit 40959a97e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 985 additions and 174 deletions

View File

@ -7,7 +7,7 @@
is not otherwise referenced. They avoid unnecessary changes to the Universe build graph or to product
dependencies. Do not use these properties elsewhere.
-->
<AngleSharpPackageVersion>0.9.9</AngleSharpPackageVersion>
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>2.1.1</BenchmarksOnlyMicrosoftEntityFrameworkCoreDesignPackageVersion>
@ -16,90 +16,90 @@
<BenchmarksOnlyMySqlConnectorPackageVersion>0.43.0</BenchmarksOnlyMySqlConnectorPackageVersion>
<BenchmarksOnlyNpgsqlEntityFrameworkCorePostgreSQLPackageVersion>2.1.1.1</BenchmarksOnlyNpgsqlEntityFrameworkCorePostgreSQLPackageVersion>
<BenchmarksOnlyPomeloEntityFrameworkCoreMySqlPackageVersion>2.1.1</BenchmarksOnlyPomeloEntityFrameworkCoreMySqlPackageVersion>
<InternalAspNetCoreAnalyzersPackageVersion>2.2.0-preview3-35359</InternalAspNetCoreAnalyzersPackageVersion>
<MicrosoftAspNetCoreAllPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAllPackageVersion>
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
<MicrosoftAspNetCoreAntiforgeryPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAntiforgeryPackageVersion>
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
<MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
<MicrosoftAspNetCoreAuthenticationPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAuthenticationPackageVersion>
<MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreCookiePolicyPackageVersion>
<MicrosoftAspNetCoreCorsPackageVersion>2.2.0-a-preview3-22cors-16556</MicrosoftAspNetCoreCorsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreDiagnosticsPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<InternalAspNetCoreAnalyzersPackageVersion>2.2.0-rtm-35519</InternalAspNetCoreAnalyzersPackageVersion>
<MicrosoftAspNetCoreAllPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAllPackageVersion>
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
<MicrosoftAspNetCoreAntiforgeryPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAntiforgeryPackageVersion>
<MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAuthenticationCookiesPackageVersion>
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
<MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAuthenticationJwtBearerPackageVersion>
<MicrosoftAspNetCoreAuthenticationPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAuthenticationPackageVersion>
<MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreAuthorizationPolicyPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion>
<MicrosoftAspNetCoreCookiePolicyPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreCookiePolicyPackageVersion>
<MicrosoftAspNetCoreCorsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreCorsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreDiagnosticsAbstractionsPackageVersion>
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreDiagnosticsPackageVersion>
<MicrosoftAspNetCoreHostingAbstractions20PackageVersion>2.0.0</MicrosoftAspNetCoreHostingAbstractions20PackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreJsonPatchPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreJsonPatchPackageVersion>
<MicrosoftAspNetCoreLocalizationPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreLocalizationPackageVersion>
<MicrosoftAspNetCoreLocalizationRoutingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreLocalizationRoutingPackageVersion>
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
<MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>
<MicrosoftAspNetCoreRazorDesignPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreRazorDesignPackageVersion>
<MicrosoftAspNetCoreRazorLanguagePackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreRazorLanguagePackageVersion>
<MicrosoftAspNetCoreRazorRuntimePackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreRazorRuntimePackageVersion>
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreResponseCachingPackageVersion>
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-a-preview3-address-scheme-17059</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-a-preview3-address-scheme-17059</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreSessionPackageVersion>
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreStaticFilesPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview3-35359</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreHtmlAbstractionsPackageVersion>
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreJsonPatchPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreJsonPatchPackageVersion>
<MicrosoftAspNetCoreLocalizationPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreLocalizationPackageVersion>
<MicrosoftAspNetCoreLocalizationRoutingPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreLocalizationRoutingPackageVersion>
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
<MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreRangeHelperSourcesPackageVersion>
<MicrosoftAspNetCoreRazorDesignPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreRazorDesignPackageVersion>
<MicrosoftAspNetCoreRazorLanguagePackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreRazorLanguagePackageVersion>
<MicrosoftAspNetCoreRazorRuntimePackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreRazorRuntimePackageVersion>
<MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreRazorTagHelpersTestingSourcesPackageVersion>
<MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreResponseCachingAbstractionsPackageVersion>
<MicrosoftAspNetCoreResponseCachingPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreResponseCachingPackageVersion>
<MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>2.2.0-a-rtm-allow-required-parameters-17081</MicrosoftAspNetCoreRoutingAbstractionsPackageVersion>
<MicrosoftAspNetCoreRoutingPackageVersion>2.2.0-a-rtm-allow-required-parameters-17081</MicrosoftAspNetCoreRoutingPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreSessionPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreSessionPackageVersion>
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreStaticFilesPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-rtm-35519</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftAspNetWebApiClientPackageVersion>5.2.6</MicrosoftAspNetWebApiClientPackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
<MicrosoftCodeAnalysisCSharpPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpPackageVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>2.8.0</MicrosoftCodeAnalysisCSharpWorkspacesPackageVersion>
<MicrosoftCodeAnalysisRazorPackageVersion>2.2.0-preview3-35359</MicrosoftCodeAnalysisRazorPackageVersion>
<MicrosoftCodeAnalysisRazorPackageVersion>2.2.0-rtm-35519</MicrosoftCodeAnalysisRazorPackageVersion>
<MicrosoftDiaSymReaderNativePackageVersion>1.7.0</MicrosoftDiaSymReaderNativePackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsDependencyModelPackageVersion>2.1.0</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsLocalizationPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsLocalizationPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
<MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>2.2.0-preview3-35359</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsLocalizationPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsLocalizationPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsObjectMethodExecutorSourcesPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsPropertyActivatorSourcesPackageVersion>
<MicrosoftExtensionsPropertyHelperSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsPropertyHelperSourcesPackageVersion>
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>2.2.0-rtm-35519</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.3</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview3-26927-02</MicrosoftNETCoreApp22PackageVersion>
<MicrosoftNetHttpHeadersPackageVersion>2.2.0-preview3-35359</MicrosoftNetHttpHeadersPackageVersion>
<MicrosoftNETSdkRazorPackageVersion>2.2.0-preview3-35359</MicrosoftNETSdkRazorPackageVersion>
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview3-27014-02</MicrosoftNETCoreApp22PackageVersion>
<MicrosoftNetHttpHeadersPackageVersion>2.2.0-rtm-35519</MicrosoftNetHttpHeadersPackageVersion>
<MicrosoftNETSdkRazorPackageVersion>2.2.0-rtm-35519</MicrosoftNETSdkRazorPackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MoqPackageVersion>4.7.49</MoqPackageVersion>
<MoqPackageVersion>4.10.0</MoqPackageVersion>
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
<NewtonsoftJsonBsonPackageVersion>1.0.1</NewtonsoftJsonBsonPackageVersion>
<NewtonsoftJsonPackageVersion>11.0.2</NewtonsoftJsonPackageVersion>

View File

@ -222,26 +222,63 @@ namespace Microsoft.AspNetCore.Mvc.Internal
bool suppressPathMatching)
{
var newPathSegments = routePattern.PathSegments.ToList();
var hasLinkGenerationEndpoint = false;
// Create a mutable copy
var nonInlineDefaultsCopy = nonInlineDefaults != null
? new RouteValueDictionary(nonInlineDefaults)
: null;
var resolvedRouteValues = ResolveActionRouteValues(action, allDefaults);
for (var i = 0; i < newPathSegments.Count; i++)
{
// Check if the pattern can be shortened because the remaining parameters are optional
//
// e.g. Matching pattern {controller=Home}/{action=Index}/{id?} against HomeController.Index
// can resolve to the following endpoints:
// - /Home/Index/{id?}
// - /Home
// e.g. Matching pattern {controller=Home}/{action=Index} against HomeController.Index
// can resolve to the following endpoints: (sorted by RouteEndpoint.Order)
// - /
if (UseDefaultValuePlusRemainingSegmentsOptional(i, action, allDefaults, newPathSegments))
// - /Home
// - /Home/Index
if (UseDefaultValuePlusRemainingSegmentsOptional(
i,
action,
resolvedRouteValues,
allDefaults,
ref nonInlineDefaultsCopy,
newPathSegments))
{
// The route pattern has matching default values AND an optional parameter
// For link generation we need to include an endpoint with parameters and default values
// so the link is correctly shortened
// e.g. {controller=Home}/{action=Index}/{id=17}
if (!hasLinkGenerationEndpoint)
{
var ep = CreateEndpoint(
action,
resolvedRouteValues,
name,
GetPattern(ref patternStringBuilder, newPathSegments),
newPathSegments,
nonInlineDefaultsCopy,
routeOrder++,
dataTokens,
suppressLinkGeneration,
true);
endpoints.Add(ep);
hasLinkGenerationEndpoint = true;
}
var subPathSegments = newPathSegments.Take(i);
var subEndpoint = CreateEndpoint(
action,
resolvedRouteValues,
name,
GetPattern(ref patternStringBuilder, subPathSegments),
subPathSegments,
nonInlineDefaults,
nonInlineDefaultsCopy,
routeOrder++,
dataTokens,
suppressLinkGeneration,
@ -249,15 +286,83 @@ namespace Microsoft.AspNetCore.Mvc.Internal
endpoints.Add(subEndpoint);
}
List<RoutePatternPart> segmentParts = null; // Initialize only as needed
var segment = newPathSegments[i];
for (var j = 0; j < segment.Parts.Count; j++)
{
var part = segment.Parts[j];
UpdatePathSegments(i, action, resolvedRouteValues, routePattern, newPathSegments, ref allParameterPolicies);
}
if (part.IsParameter &&
part is RoutePatternParameterPart parameterPart &&
action.RouteValues.ContainsKey(parameterPart.Name))
var finalEndpoint = CreateEndpoint(
action,
resolvedRouteValues,
name,
GetPattern(ref patternStringBuilder, newPathSegments),
newPathSegments,
nonInlineDefaultsCopy,
routeOrder++,
dataTokens,
suppressLinkGeneration,
suppressPathMatching);
endpoints.Add(finalEndpoint);
return routeOrder;
string GetPattern(ref StringBuilder sb, IEnumerable<RoutePatternPathSegment> segments)
{
if (sb == null)
{
sb = new StringBuilder();
}
RoutePatternWriter.WriteString(sb, segments);
var rawPattern = sb.ToString();
sb.Length = 0;
return rawPattern;
}
}
private static IDictionary<string, string> ResolveActionRouteValues(ActionDescriptor action, IReadOnlyDictionary<string, object> allDefaults)
{
Dictionary<string, string> resolvedRequiredValues = null;
foreach (var kvp in action.RouteValues)
{
// Check whether there is a matching default value with a different case
// e.g. {controller=HOME}/{action} with HomeController.Index will have route values:
// - controller = HOME
// - action = Index
if (allDefaults.TryGetValue(kvp.Key, out var value) &&
value is string defaultValue &&
!string.Equals(kvp.Value, defaultValue, StringComparison.Ordinal) &&
string.Equals(kvp.Value, defaultValue, StringComparison.OrdinalIgnoreCase))
{
if (resolvedRequiredValues == null)
{
resolvedRequiredValues = new Dictionary<string, string>(action.RouteValues, StringComparer.OrdinalIgnoreCase);
}
resolvedRequiredValues[kvp.Key] = defaultValue;
}
}
return resolvedRequiredValues ?? action.RouteValues;
}
private void UpdatePathSegments(
int i,
ActionDescriptor action,
IDictionary<string, string> resolvedRequiredValues,
RoutePattern routePattern,
List<RoutePatternPathSegment> newPathSegments,
ref IDictionary<string, IList<IParameterPolicy>> allParameterPolicies)
{
List<RoutePatternPart> segmentParts = null; // Initialize only as needed
var segment = newPathSegments[i];
for (var j = 0; j < segment.Parts.Count; j++)
{
var part = segment.Parts[j];
if (part is RoutePatternParameterPart parameterPart)
{
if (resolvedRequiredValues.TryGetValue(parameterPart.Name, out var parameterRouteValue))
{
if (segmentParts == null)
{
@ -268,9 +373,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
allParameterPolicies = MvcEndpointInfo.BuildParameterPolicies(routePattern.Parameters, _parameterPolicyFactory);
}
// Replace parameter with literal value
var parameterRouteValue = action.RouteValues[parameterPart.Name];
// Route value could be null if it is a "known" route value.
// Do not use the null value to de-normalize the route pattern,
// instead leave the parameter unchanged.
@ -297,47 +399,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
}
}
// A parameter part was replaced so replace segment with updated parts
if (segmentParts != null)
{
newPathSegments[i] = RoutePatternFactory.Segment(segmentParts);
}
}
var endpoint = CreateEndpoint(
action,
name,
GetPattern(ref patternStringBuilder, newPathSegments),
newPathSegments,
nonInlineDefaults,
routeOrder++,
dataTokens,
suppressLinkGeneration,
suppressPathMatching);
endpoints.Add(endpoint);
return routeOrder;
string GetPattern(ref StringBuilder sb, IEnumerable<RoutePatternPathSegment> segments)
// A parameter part was replaced so replace segment with updated parts
if (segmentParts != null)
{
if (sb == null)
{
sb = new StringBuilder();
}
RoutePatternWriter.WriteString(sb, segments);
var rawPattern = sb.ToString();
sb.Length = 0;
return rawPattern;
newPathSegments[i] = RoutePatternFactory.Segment(segmentParts);
}
}
private bool UseDefaultValuePlusRemainingSegmentsOptional(
int segmentIndex,
ActionDescriptor action,
IDictionary<string, string> resolvedRequiredValues,
IReadOnlyDictionary<string, object> allDefaults,
ref RouteValueDictionary nonInlineDefaults,
List<RoutePatternPathSegment> pathSegments)
{
// Check whether the remaining segments are all optional and one or more of them is
@ -352,22 +428,33 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var part = segment.Parts[j];
if (part.IsParameter && part is RoutePatternParameterPart parameterPart)
{
if (parameterPart.IsOptional || parameterPart.IsCatchAll)
if (allDefaults.TryGetValue(parameterPart.Name, out var v))
{
continue;
}
if (action.RouteValues.ContainsKey(parameterPart.Name))
{
if (allDefaults.TryGetValue(parameterPart.Name, out var v)
&& v is string defaultValue
&& action.RouteValues.TryGetValue(parameterPart.Name, out var routeValue)
&& string.Equals(defaultValue, routeValue, StringComparison.OrdinalIgnoreCase))
if (resolvedRequiredValues.TryGetValue(parameterPart.Name, out var routeValue))
{
if (string.Equals(v as string, routeValue, StringComparison.OrdinalIgnoreCase))
{
usedDefaultValue = true;
continue;
}
}
else
{
if (nonInlineDefaults == null)
{
nonInlineDefaults = new RouteValueDictionary();
}
nonInlineDefaults.TryAdd(parameterPart.Name, v);
usedDefaultValue = true;
continue;
}
}
if (parameterPart.IsOptional || parameterPart.IsCatchAll)
{
continue;
}
}
else if (part.IsSeparator && part is RoutePatternSeparatorPart separatorPart
&& separatorPart.Content == ".")
@ -441,6 +528,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private RouteEndpoint CreateEndpoint(
ActionDescriptor action,
IDictionary<string, string> actionRouteValues,
string routeName,
string patternRawText,
IEnumerable<RoutePatternPathSegment> segments,
@ -461,12 +549,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
};
var defaults = new RouteValueDictionary(nonInlineDefaults);
EnsureRequiredValuesInDefaults(action.RouteValues, defaults);
EnsureRequiredValuesInDefaults(actionRouteValues, defaults, segments);
var metadataCollection = BuildEndpointMetadata(
action,
routeName,
new RouteValueDictionary(action.RouteValues),
new RouteValueDictionary(actionRouteValues),
dataTokens,
suppressLinkGeneration,
suppressPathMatching);
@ -554,7 +642,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
return metadataCollection;
}
// Ensure required values are a subset of defaults
// Ensure route values are a subset of defaults
// Examples:
//
// Template: {controller}/{action}/{category}/{id?}
@ -568,9 +656,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Required values: controller=foo, action=bar
// Final constructed pattern: foo/bar/{category}/{id?}
// Final defaults: controller=foo, action=bar, category=products
private void EnsureRequiredValuesInDefaults(IDictionary<string, string> requiredValues, RouteValueDictionary defaults)
private void EnsureRequiredValuesInDefaults(
IDictionary<string, string> routeValues,
RouteValueDictionary defaults,
IEnumerable<RoutePatternPathSegment> segments)
{
foreach (var kvp in requiredValues)
foreach (var kvp in routeValues)
{
if (kvp.Value != null)
{

View File

@ -40,6 +40,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
if (parameterPart.IsCatchAll)
{
sb.Append("*");
if (!parameterPart.EncodeSlashes)
{
sb.Append("*");
}
}
sb.Append(parameterPart.Name);
foreach (var item in parameterPart.ParameterPolicies)

View File

@ -146,22 +146,29 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private static TheoryData GetSingleActionData(bool isConventionalRouting)
{
var data = new TheoryData<string, string[]>
var data = new TheoryData<string, string, string[]>
{
{"{controller}/{action}/{id?}", new[] { "TestController/TestAction/{id?}" }},
{"{controller}/{id?}", isConventionalRouting ? new string[] { } : new[] { "TestController/{id?}" }},
{"{action}/{id?}", isConventionalRouting ? new string[] { } : new[] { "TestAction/{id?}" }},
{"{Controller}/{Action}/{id?}", new[] { "TestController/TestAction/{id?}" }},
{"{CONTROLLER}/{ACTION}/{id?}", new[] { "TestController/TestAction/{id?}" }},
{"{controller}/{action=TestAction}", new[] { "TestController", "TestController/TestAction" }},
{"{controller}/{action=TestAction}/{id?}", new[] { "TestController", "TestController/TestAction/{id?}" }},
{"{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestController", "TestController/TestAction/{id?}" }},
{"{controller}/{action}/{*catchAll}", new[] { "TestController/TestAction/{*catchAll}" }},
{"{controller}/{action=TestAction}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{*catchAll}" }},
{"{controller}/{action=TestAction}/{id?}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{id?}/{*catchAll}" }},
{"{controller}/{action}.{ext?}", new[] { "TestController/TestAction.{ext?}" }},
{"{controller}/{action=TestAction}.{ext?}", new[] { "TestController", "TestController/TestAction.{ext?}" }},
{"{controller:upper-case}/{action=TestAction}.{ext?}", new[] { "TESTCONTROLLER", "TESTCONTROLLER/TestAction.{ext?}" }},
{"{controller}/{action}/{id?}", null, new[] { "TestController/TestAction/{id?}" }},
{"{controller}/{id?}", null, isConventionalRouting ? new string[] { } : new[] { "TestController/{id?}" }},
{"{action}/{id?}", null, isConventionalRouting ? new string[] { } : new[] { "TestAction/{id?}" }},
{"{Controller}/{Action}/{id?}", null, new[] { "TestController/TestAction/{id?}" }},
{"{Controller}/{Action}/{id?}/{more?}", null, new[] { "TestController/TestAction/{id?}/{more?}" }},
{"{CONTROLLER}/{ACTION}/{id?}", null, new[] { "TestController/TestAction/{id?}" }},
{"{controller}/{action=TestAction}", "TestController/{action=TestAction}", new[] { "TestController", "TestController/TestAction" }},
{"{controller}/{action=TestAction}/{id?}", "TestController/{action=TestAction}/{id?}", new[] { "TestController", "TestController/TestAction/{id?}" }},
{"{controller}/{action=TESTACTION}/{id?}", "TestController/{action=TESTACTION}/{id?}", new[] { "TestController", "TestController/TESTACTION/{id?}" }},
{"{controller}/{action=TestAction}/{id?}/{more}", null, new[] { "TestController/TestAction/{id?}/{more}" }},
{"{controller=TestController}/{action=TestAction}/{id?}", "{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestController", "TestController/TestAction/{id?}" }},
{"{controller=TestController}/{action=TestAction}/{id?}/{more?}", "{controller=TestController}/{action=TestAction}/{id?}/{more?}", new[] { "", "TestController", "TestController/TestAction/{id?}/{more?}" }},
{"{controller}/{action}/{*catchAll}", null, new[] { "TestController/TestAction/{*catchAll}" }},
{"{controller}/{action=TestAction}/{*catchAll}", "TestController/{action=TestAction}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{*catchAll}" }},
{"{controller}/{action=TestAction}/{id?}/{*catchAll}", "TestController/{action=TestAction}/{id?}/{*catchAll}", new[] { "TestController", "TestController/TestAction/{id?}/{*catchAll}" }},
{"{controller}/{action=TestAction}/{id?}/{**catchAll}", "TestController/{action=TestAction}/{id?}/{**catchAll}", new[] { "TestController", "TestController/TestAction/{id?}/{**catchAll}" }},
{"{controller}/{action}.{ext?}", null, new[] { "TestController/TestAction.{ext?}" }},
{"{controller}/{action=TestAction}.{ext?}", "TestController/{action=TestAction}.{ext?}", new[] { "TestController", "TestController/TestAction.{ext?}" }},
{"{controller}/{action=TestAction}.{ext?}/{more?}", "TestController/{action=TestAction}.{ext?}/{more?}", new[] { "TestController", "TestController/TestAction.{ext?}/{more?}" }},
{"{controller}/{action=TestAction}.{ext?}/{more}", null, new[] { "TestController/TestAction.{ext?}/{more}" }},
{"{controller:upper-case}/{action:upper-case=TestAction}.{ext?}", "TESTCONTROLLER/{action:upper-case=TestAction}.{ext?}", new[] { "TESTCONTROLLER", "TESTCONTROLLER/TESTACTION.{ext?}" }},
};
return data;
@ -169,7 +176,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
[Theory]
[MemberData(nameof(GetSingleActionData_Conventional))]
public void Endpoints_Conventional_SingleAction(string endpointInfoRoute, string[] finalEndpointPatterns)
public void Endpoints_Conventional_SingleAction(string endpointInfoRoute, string suppressMatchingTemplate, string[] finalEndpointPatterns)
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
@ -178,9 +185,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute));
// Act
var endpoints = dataSource.Endpoints;
var endpoints = dataSource.Endpoints.ToList();
// Assert
// Ensure there are no endpoints with duplicate Order values
Assert.DoesNotContain(endpoints.GroupBy(e => Assert.IsType<RouteEndpoint>(e).Order), g => g.Count() > 1);
endpoints = endpoints.OrderBy(e => Assert.IsType<RouteEndpoint>(e).Order).ToList();
AssertSuppressMatchingTemplate(suppressMatchingTemplate, endpoints);
var inspectors = finalEndpointPatterns
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText)))
.ToArray();
@ -191,7 +206,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
[Theory]
[MemberData(nameof(GetSingleActionData_Attribute))]
public void Endpoints_AttributeRouting_SingleAction(string endpointInfoRoute, string[] finalEndpointPatterns)
public void Endpoints_AttributeRouting_SingleAction(string endpointInfoRoute, string suppressMatchingTemplate, string[] finalEndpointPatterns)
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
@ -200,7 +215,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
// Act
var endpoints = dataSource.Endpoints;
var endpoints = dataSource.Endpoints.ToList();
// Ensure there are no endpoints with duplicate Order values
Assert.DoesNotContain(endpoints.GroupBy(e => Assert.IsType<RouteEndpoint>(e).Order), g => g.Count() > 1);
endpoints = endpoints.OrderBy(e => Assert.IsType<RouteEndpoint>(e).Order).ToList();
AssertSuppressMatchingTemplate(suppressMatchingTemplate, endpoints);
// Assert
var inspectors = finalEndpointPatterns
@ -212,14 +234,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
}
[Theory]
[InlineData("{area}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{controller}/{action}/{id?}", new string[] { })]
[InlineData("{area=TestArea}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{area=TestArea}/{controller}/{action=TestAction}/{id?}", new[] { "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{area=TestArea}/{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestArea", "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{area:exists}/{controller}/{action}/{id?}", new[] { "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{area:exists:upper-case}/{controller}/{action}/{id?}", new[] { "TESTAREA/TestController/TestAction/{id?}" })]
public void Endpoints_AreaSingleAction(string endpointInfoRoute, string[] finalEndpointTemplates)
[InlineData("{area}/{controller}/{action}/{id?}", null, new[] { "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{controller}/{action}/{id?}", null, new string[] { })]
[InlineData("{area=TestArea}/{controller}/{action}/{id?}", null, new[] { "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{area=TestArea}/{controller}/{action=TestAction}/{id?}", "TestArea/TestController/{action=TestAction}/{id?}", new[] { "TestArea/TestController", "TestArea/TestController/TestAction/{id?}"})]
[InlineData("{area=TestArea}/{controller=TestController}/{action=TestAction}/{id?}", "{area=TestArea}/{controller=TestController}/{action=TestAction}/{id?}", new[] { "", "TestArea", "TestArea/TestController", "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{area:exists}/{controller}/{action}/{id?}", null, new[] { "TestArea/TestController/TestAction/{id?}" })]
[InlineData("{area:exists:upper-case}/{controller}/{action}/{id?}", null, new[] { "TESTAREA/TestController/TestAction/{id?}" })]
public void Endpoints_AreaSingleAction(string endpointInfoRoute, string suppressMatchingTemplate, string[] finalEndpointTemplates)
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
@ -240,9 +262,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(string.Empty, endpointInfoRoute, serviceProvider: services.BuildServiceProvider()));
// Act
var endpoints = dataSource.Endpoints;
var endpoints = dataSource.Endpoints.ToList();
// Assert
// Ensure there are no endpoints with duplicate Order values
Assert.DoesNotContain(endpoints.GroupBy(e => Assert.IsType<RouteEndpoint>(e).Order), g => g.Count() > 1);
endpoints = endpoints.OrderBy(e => Assert.IsType<RouteEndpoint>(e).Order).ToList();
AssertSuppressMatchingTemplate(suppressMatchingTemplate, endpoints);
var inspectors = finalEndpointTemplates
.Select(t => new Action<Endpoint>(e => Assert.Equal(t, Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText)))
.ToArray();
@ -251,6 +281,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Collection(endpoints, inspectors);
}
private static void AssertSuppressMatchingTemplate(string suppressMatchingTemplate, List<Endpoint> endpoints)
{
if (suppressMatchingTemplate != null)
{
var suppressMatchingEndpoint = endpoints.First();
Assert.True(suppressMatchingEndpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching);
Assert.Equal(suppressMatchingTemplate, Assert.IsType<RouteEndpoint>(suppressMatchingEndpoint).RoutePattern.RawText);
endpoints.Remove(suppressMatchingEndpoint);
}
}
[Fact]
public void Endpoints_SingleAction_ConventionalRoute_ContainsParameterWithNullRequiredRouteValue()
{
@ -304,6 +345,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
Assert.Collection(endpoints,
(e) =>
{
Assert.Equal("TestController/{action=TestAction}", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText);
Assert.True(e.Metadata.GetMetadata<ISuppressMatchingMetadata>().SuppressMatching);
},
(e) => Assert.Equal("TestController", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
}
@ -332,6 +378,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
// Assert
Assert.Collection(endpoints1,
(e) => Assert.Equal("TestController/{action=TestAction}", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
Assert.Same(endpoints1, endpoints2);
@ -373,6 +420,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var endpoints = dataSource.Endpoints;
Assert.Collection(endpoints,
(e) => Assert.Equal("TestController/{action=TestAction}", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText),
(e) => Assert.Equal("TestController/TestAction", Assert.IsType<RouteEndpoint>(e).RoutePattern.RawText));
@ -720,10 +768,29 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var endpoints = dataSource.Endpoints;
// Assert
var endpoint = Assert.Single(endpoints);
var matcherEndpoint = Assert.IsType<RouteEndpoint>(endpoint);
Assert.Equal("Foo/Bar/{subscription=general}", matcherEndpoint.RoutePattern.RawText);
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Foo/Bar/{subscription=general}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Foo/Bar", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Foo/Bar/{subscription=general}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(3, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
@ -780,6 +847,464 @@ namespace Microsoft.AspNetCore.Mvc.Internal
AssertIsSubset(expectedDefaults, matcherEndpoint.RoutePattern.Defaults);
}
[Fact]
public void Endpoints_ConventionalRoutes_NonDefaultAndDefaultValuesEndingWithOptional_IncludeFullRouteAsHighPriority()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "Home", action = "Index" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
name: string.Empty,
template: "{controller}/{action=Index}/{id?}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Home/{action=Index}/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Home", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(3, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
public void Endpoints_ConventionalRoutes_DefaultValuesEndingWithOptional_IncludeFullRouteAsHighPriority()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "Home", action = "Index" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
name: string.Empty,
template: "{controller=Home}/{action=Index}/{id?}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("{controller=Home}/{action=Index}/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Home", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(3, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("Home/Index/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(4, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
public void Endpoints_ConventionalRoutes_DefaultValues_Shortened()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
name: string.Empty,
template: "{controller=TestController}/{action=TestAction}/{id=17}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("{controller=TestController}/{action=TestAction}/{id=17}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(1, matcherEndpoint.Order);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(2, matcherEndpoint.Order);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(3, matcherEndpoint.Order);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(4, matcherEndpoint.Order);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction/{id=17}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(5, matcherEndpoint.Order);
});
}
[Fact]
public void Endpoints_ConventionalRoutes_DefaultValuesAndCatchAll_EndpointInfoDefaultsNotModified()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
var endpointInfo = CreateEndpointInfo(
name: string.Empty,
defaults: new RouteValueDictionary(),
template: "{controller=TestController}/{action=TestAction}/{id=17}/{**catchAll}");
dataSource.ConventionalEndpointInfos.Add(endpointInfo);
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Empty(endpointInfo.Defaults);
}
[Fact]
public void Endpoints_ConventionalRoutes_DefaultValuesAndCatchAll_Shortened()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
name: string.Empty,
template: "{controller=TestController}/{action=TestAction}/{id=17}/{**catchAll}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("{controller=TestController}/{action=TestAction}/{id=17}/{**catchAll}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(3, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(4, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction/{id=17}/{**catchAll}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(5, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
public void Endpoints_ConventionalRoutes_DefaultValuesAndOptional_Shortened()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
name: string.Empty,
template: "{controller=TestController}/{action=TestAction}/{id=17}/{more?}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("{controller=TestController}/{action=TestAction}/{id=17}/{more?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(3, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(4, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction/{id=17}/{more?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("17", matcherEndpoint.RoutePattern.Defaults["id"]);
Assert.Equal(5, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
public void Endpoints_ConventionalRoutes_OptionalExtension_IncludeFullRouteAsHighPriority()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
name: string.Empty,
template: "{controller}/{action=TestAction}.{ext?}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/{action=TestAction}.{ext?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction.{ext?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(3, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
public void Endpoints_ConventionalRoutes_MultipleOptionalAndCatchAll_IncludeFullRouteAsHighPriority()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
dataSource.ConventionalEndpointInfos.Add(CreateEndpointInfo(
name: string.Empty,
template: "{controller=TestController}/{action=TestAction}/{id?}/{more?}/{**catchAll}"));
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("{controller=TestController}/{action=TestAction}/{id?}/{more?}/{**catchAll}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(3, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TestAction/{id?}/{more?}/{**catchAll}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(4, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
public void Endpoints_AttributeRoutes_CatchAllWithDefault_IncludeFullRouteAsHighPriority()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
"/TeamName/{*Name=DefaultName}/",
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TeamName/{*Name=DefaultName}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal(0, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TeamName", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("DefaultName", matcherEndpoint.RoutePattern.Defaults["Name"]);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TeamName/{*Name=DefaultName}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("DefaultName", matcherEndpoint.RoutePattern.Defaults["Name"]);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
});
}
[Fact]
public void Endpoints_AttributeRoutes_DefaultDifferentCaseFromRouteValue_UseDefaultCase()
{
// Arrange
var actionDescriptorCollection = GetActionDescriptorCollection(
"{controller}/{action=TESTACTION}/{id?}",
new { controller = "TestController", action = "TestAction" });
var dataSource = CreateMvcEndpointDataSource(actionDescriptorCollection);
// Act
var endpoints = dataSource.Endpoints;
// Assert
Assert.Collection(
endpoints,
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/{action=TESTACTION}/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("TESTACTION", matcherEndpoint.RoutePattern.Defaults["action"]);
Assert.Equal(0, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, true);
var routeValuesAddress = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.Equal("TESTACTION", routeValuesAddress.RequiredValues["action"]);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("TESTACTION", matcherEndpoint.RoutePattern.Defaults["action"]);
Assert.Equal(1, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
var routeValuesAddress = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.Equal("TESTACTION", routeValuesAddress.RequiredValues["action"]);
},
(ep) =>
{
var matcherEndpoint = Assert.IsType<RouteEndpoint>(ep);
Assert.Equal("TestController/TESTACTION/{id?}", matcherEndpoint.RoutePattern.RawText);
Assert.Equal("TESTACTION", matcherEndpoint.RoutePattern.Defaults["action"]);
Assert.Equal(2, matcherEndpoint.Order);
AssertMatchingSuppressed(matcherEndpoint, false);
var routeValuesAddress = matcherEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
Assert.Equal("TESTACTION", routeValuesAddress.RequiredValues["action"]);
});
}
private MvcEndpointDataSource CreateMvcEndpointDataSource(
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider = null,
MvcEndpointInvokerFactory mvcEndpointInvokerFactory = null)
@ -898,5 +1423,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
Assert.Equal(subsetPair.Value, fullSetPairValue);
}
}
private void AssertMatchingSuppressed(Endpoint endpoint, bool suppressed)
{
var isEndpointSuppressed = endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching ?? false;
Assert.Equal(suppressed, isEndpointSuppressed);
}
}
}

View File

@ -1041,6 +1041,136 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("Departments", result.RouteName);
}
[Fact]
public async Task ConventionalRoutedAction_DefaultValues_OptionalParameter_LinkToDefaultValuePath()
{
// Arrange
var url = LinkFrom("http://localhost/DefaultValuesRoute/Optional")
.To(new { });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("DefaultValues", result.Controller);
Assert.Equal("OptionalParameter", result.Action);
Assert.Equal("/DefaultValuesRoute/Optional", result.Link);
}
[Fact]
public async Task ConventionalRoutedAction_DefaultValues_OptionalParameter_LinkToFullPath()
{
// Arrange
var url = LinkFrom("http://localhost/DefaultValuesRoute/Optional")
.To(new { id = "123" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("DefaultValues", result.Controller);
Assert.Equal("OptionalParameter", result.Action);
Assert.Equal("/DefaultValuesRoute/Optional/DEFAULTVALUES/OPTIONALPARAMETER/123", result.Link);
}
[Fact]
public async Task ConventionalRoutedAction_DefaultValues_DefaultParameter_LinkToDefaultValuePath()
{
// Arrange
var url = LinkFrom("http://localhost/DefaultValuesRoute/Default")
.To(new { });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("DefaultValues", result.Controller);
Assert.Equal("DefaultParameter", result.Action);
Assert.Equal("17", result.RouteValues["id"]);
Assert.Equal("/DefaultValuesRoute/Default", result.Link);
}
[Fact]
public async Task ConventionalRoutedAction_DefaultValues_DefaultParameterWithCatchAll_LinkToDefaultValuePath()
{
// Arrange
var url = LinkFrom("http://localhost/DefaultValuesRoute/Default")
.To(new { catchAll = "CatchAll" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("DefaultValues", result.Controller);
Assert.Equal("DefaultParameter", result.Action);
Assert.Equal("17", result.RouteValues["id"]);
Assert.Equal("/DefaultValuesRoute/Default/DEFAULTVALUES/DEFAULTPARAMETER/17/CatchAll", result.Link);
}
[Fact]
public async Task ConventionalRoutedAction_DefaultValues_DefaultParameter_LinkToFullPath()
{
// Arrange
var url = LinkFrom("http://localhost/DefaultValuesRoute/Default")
.To(new { id = "123" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("DefaultValues", result.Controller);
Assert.Equal("DefaultParameter", result.Action);
Assert.Equal("17", result.RouteValues["id"]);
Assert.Equal("/DefaultValuesRoute/Default/DEFAULTVALUES/DEFAULTPARAMETER/123", result.Link);
}
[Fact]
public async Task ConventionalRoutedAction_DefaultValues_DefaultParameterMatches_LinkToShortenedPath()
{
// Arrange
var url = LinkFrom("http://localhost/DefaultValuesRoute/Default/DefaultValues/DefaultParameter/123")
.To(new { id = "17" });
// Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<RoutingResult>(body);
Assert.Equal("DefaultValues", result.Controller);
Assert.Equal("DefaultParameter", result.Action);
Assert.Equal("123", result.RouteValues["id"]);
Assert.Equal("/DefaultValuesRoute/Default", result.Link);
}
[Fact]
public virtual async Task ConventionalRoutedAction_LinkToArea()
{

View File

@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
namespace RoutingWebSite
{
public class DefaultValuesController : Controller
{
private readonly TestResponseGenerator _generator;
public DefaultValuesController(TestResponseGenerator generator)
{
_generator = generator;
}
public IActionResult DefaultParameter(string id)
{
return _generator.Generate(id == null
? "/DefaultValuesRoute/DefaultValues"
: "/DefaultValuesRoute/DefaultValues/DefaultParameter/Index/" + id);
}
public IActionResult OptionalParameter(string id)
{
return _generator.Generate(id == "17"
? "/DefaultValuesRoute/DefaultValues"
: "/DefaultValuesRoute/DefaultValues/OptionalParameter/Index/" + id);
}
}
}

View File

@ -62,6 +62,18 @@ namespace RoutingWebSite
defaults: null,
constraints: new { controller = "ConventionalTransformer" });
routes.MapRoute(
"DefaultValuesRoute_OptionalParameter",
"DefaultValuesRoute/Optional/{controller=DEFAULTVALUES}/{action=OPTIONALPARAMETER}/{id?}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "OptionalParameter" });
routes.MapRoute(
"DefaultValuesRoute_DefaultParameter",
"DefaultValuesRoute/Default/{controller=DEFAULTVALUES}/{action=DEFAULTPARAMETER}/{id=17}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "DefaultParameter" });
routes.MapAreaRoute(
"flightRoute",
"adminRoute",

View File

@ -1,14 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
@ -48,7 +45,7 @@ namespace RoutingWebSite
new ControllerToRemove
{
ControllerType = typeof(PageRouteController),
Actions = new [] { nameof(PageRouteController.AttributeRoute) }
Actions = new[] { nameof(PageRouteController.AttributeRoute) }
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<IActionDescriptorProvider>(actionDescriptorProvider));
}
@ -64,6 +61,18 @@ namespace RoutingWebSite
constraints: new { controller = "DataTokens" },
dataTokens: new { hasDataTokens = true });
routes.MapRoute(
"DefaultValuesRoute_OptionalParameter",
"DefaultValuesRoute/Optional/{controller=DEFAULTVALUES}/{action=OPTIONALPARAMETER}/{id?}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "OptionalParameter" });
routes.MapRoute(
"DefaultValuesRoute_DefaultParameter",
"DefaultValuesRoute/Default/{controller=DEFAULTVALUES}/{action=DEFAULTPARAMETER}/{id=17}/{**catchAll}",
defaults: null,
constraints: new { controller = "DefaultValues", action = "DefaultParameter" });
routes.MapAreaRoute(
"flightRoute",
"adminRoute",