diff --git a/src/Hosting/TestHost/src/PublicAPI.Unshipped.txt b/src/Hosting/TestHost/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..38d66595d1b9 100644 --- a/src/Hosting/TestHost/src/PublicAPI.Unshipped.txt +++ b/src/Hosting/TestHost/src/PublicAPI.Unshipped.txt @@ -1 +1,8 @@ #nullable enable +*REMOVED*static Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder, string! solutionRelativePath, string! applicationBasePath, string! solutionName = "*.sln") -> Microsoft.AspNetCore.Hosting.IWebHostBuilder! +*REMOVED*static Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder, string! solutionRelativePath, string! solutionName = "*.sln") -> Microsoft.AspNetCore.Hosting.IWebHostBuilder! +static Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder, string! solutionRelativePath) -> Microsoft.AspNetCore.Hosting.IWebHostBuilder! +static Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder, string! solutionRelativePath, string! applicationBasePath, string! solutionName) -> Microsoft.AspNetCore.Hosting.IWebHostBuilder! +static Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder, string! solutionRelativePath, string! applicationBasePath, System.ReadOnlySpan solutionNames = default(System.ReadOnlySpan)) -> Microsoft.AspNetCore.Hosting.IWebHostBuilder! +static Microsoft.AspNetCore.TestHost.WebHostBuilderExtensions.UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder, string! solutionRelativePath, string! solutionName) -> Microsoft.AspNetCore.Hosting.IWebHostBuilder! + diff --git a/src/Hosting/TestHost/src/WebHostBuilderExtensions.cs b/src/Hosting/TestHost/src/WebHostBuilderExtensions.cs index 351b568d575a..b2e6b0a9619e 100644 --- a/src/Hosting/TestHost/src/WebHostBuilderExtensions.cs +++ b/src/Hosting/TestHost/src/WebHostBuilderExtensions.cs @@ -16,6 +16,8 @@ namespace Microsoft.AspNetCore.TestHost; /// public static class WebHostBuilderExtensions { + private static readonly string[] _defaultSolutionNames = ["*.sln", "*.slnx"]; + /// /// Enables the service. /// @@ -117,6 +119,19 @@ public static IWebHostBuilder ConfigureTestContainer(this IWebHostBu return webHostBuilder; } + /// + /// Sets the content root of relative to the . + /// + /// The . + /// The directory of the solution file. + /// The . + public static IWebHostBuilder UseSolutionRelativeContentRoot( + this IWebHostBuilder builder, + string solutionRelativePath) + { + return builder.UseSolutionRelativeContentRoot(solutionRelativePath, AppContext.BaseDirectory, _defaultSolutionNames); + } + /// /// Sets the content root of relative to the . /// @@ -124,13 +139,12 @@ public static IWebHostBuilder ConfigureTestContainer(this IWebHostBu /// The directory of the solution file. /// The name of the solution file to make the content root relative to. /// The . - [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] public static IWebHostBuilder UseSolutionRelativeContentRoot( this IWebHostBuilder builder, string solutionRelativePath, - string solutionName = "*.sln") + string solutionName) { - return builder.UseSolutionRelativeContentRoot(solutionRelativePath, AppContext.BaseDirectory, solutionName); + return builder.UseSolutionRelativeContentRoot(solutionRelativePath, AppContext.BaseDirectory, [solutionName]); } /// @@ -141,24 +155,49 @@ public static IWebHostBuilder UseSolutionRelativeContentRoot( /// The root of the app's directory. /// The name of the solution file to make the content root relative to. /// The . - [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] public static IWebHostBuilder UseSolutionRelativeContentRoot( this IWebHostBuilder builder, string solutionRelativePath, string applicationBasePath, - string solutionName = "*.sln") + string solutionName) + { + return builder.UseSolutionRelativeContentRoot(solutionRelativePath, applicationBasePath, [solutionName]); + } + + /// + /// Sets the content root of relative to the . + /// + /// The . + /// The directory of the solution file. + /// The root of the app's directory. + /// The names of the solution files to make the content root relative to. If empty, defaults to *.sln and *.slnx. + /// The . + [SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads", Justification = "Required to maintain compatibility")] + public static IWebHostBuilder UseSolutionRelativeContentRoot( + this IWebHostBuilder builder, + string solutionRelativePath, + string applicationBasePath, + ReadOnlySpan solutionNames = default) { ArgumentNullException.ThrowIfNull(solutionRelativePath); ArgumentNullException.ThrowIfNull(applicationBasePath); + if (solutionNames.IsEmpty) + { + solutionNames = _defaultSolutionNames; + } + var directoryInfo = new DirectoryInfo(applicationBasePath); do { - var solutionPath = Directory.EnumerateFiles(directoryInfo.FullName, solutionName).FirstOrDefault(); - if (solutionPath != null) + foreach (var solutionName in solutionNames) { - builder.UseContentRoot(Path.GetFullPath(Path.Combine(directoryInfo.FullName, solutionRelativePath))); - return builder; + var solutionPath = Directory.EnumerateFiles(directoryInfo.FullName, solutionName).FirstOrDefault(); + if (solutionPath != null) + { + builder.UseContentRoot(Path.GetFullPath(Path.Combine(directoryInfo.FullName, solutionRelativePath))); + return builder; + } } directoryInfo = directoryInfo.Parent; diff --git a/src/Hosting/TestHost/test/UseSolutionRelativeContentRootTests.cs b/src/Hosting/TestHost/test/UseSolutionRelativeContentRootTests.cs new file mode 100644 index 000000000000..96a6466b72ad --- /dev/null +++ b/src/Hosting/TestHost/test/UseSolutionRelativeContentRootTests.cs @@ -0,0 +1,200 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.TestHost; + +#pragma warning disable ASPDEPR004 // WebHostBuilder is obsolete +#pragma warning disable ASPDEPR008 // WebHost is obsolete +public class UseSolutionRelativeContentRootTests : IDisposable +{ + private readonly string _tempDirectory; + private readonly string _contentDirectory; + + public UseSolutionRelativeContentRootTests() + { + _tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")[..8]); + _contentDirectory = Path.Combine(_tempDirectory, "src"); + Directory.CreateDirectory(_contentDirectory); + } + + [Fact] + public void UseSolutionRelativeContentRoot_FindsSlnFile() + { + var solutionFile = Path.Combine(_tempDirectory, "TestApp.sln"); + File.WriteAllText(solutionFile, "Microsoft Visual Studio Solution File, Format Version 12.00"); + + var builder = new WebHostBuilder() + .UseTestServer() + .Configure(app => { }); + + builder.UseSolutionRelativeContentRoot("src", applicationBasePath: _tempDirectory); + + using var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + + Assert.Equal(_contentDirectory, environment.ContentRootPath); + } + + [Fact] + public void UseSolutionRelativeContentRoot_FindsSlnxFile() + { + var solutionFile = Path.Combine(_tempDirectory, "TestApp.slnx"); + File.WriteAllText(solutionFile, """ + + + + + + + """); + + var builder = new WebHostBuilder() + .UseTestServer() + .Configure(app => { }); + + builder.UseSolutionRelativeContentRoot("src", applicationBasePath: _tempDirectory); + + using var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + + Assert.Equal(_contentDirectory, environment.ContentRootPath); + } + + [Fact] + public void UseSolutionRelativeContentRoot_WithSolutionName_FindsSpecifiedFile() + { + var subDirectory = Path.Combine(_tempDirectory, "sub"); + Directory.CreateDirectory(subDirectory); + + var slnFile = Path.Combine(subDirectory, "TestApp.sln"); + var slnxFile = Path.Combine(_tempDirectory, "TestApp.slnx"); + File.WriteAllText(slnFile, "Microsoft Visual Studio Solution File, Format Version 12.00"); + File.WriteAllText(slnxFile, """ + + + + + + """); + + var builder = new WebHostBuilder() + .UseTestServer() + .Configure(app => { }); + + builder.UseSolutionRelativeContentRoot("src", _tempDirectory, "*.slnx"); + + using var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + + Assert.Equal(_contentDirectory, environment.ContentRootPath); + } + + [Fact] + public void UseSolutionRelativeContentRoot_WithMultipleSolutionNames_FindsInCurrentDirectoryFirst() + { + var expectedPath = Path.Combine(_contentDirectory, "sub"); + Directory.CreateDirectory(expectedPath); + + var slnFile = Path.Combine(_tempDirectory, "TestApp.sln"); + var slnxFile = Path.Combine(_contentDirectory, "TestApp.slnx"); + File.WriteAllText(slnFile, "Microsoft Visual Studio Solution File, Format Version 12.00"); + File.WriteAllText(slnxFile, """ + + + + + + """); + + var builder = new WebHostBuilder() + .UseTestServer() + .Configure(app => { }); + + builder.UseSolutionRelativeContentRoot("sub", _contentDirectory, ["*.sln", "*.slnx"]); + + using var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + + Assert.Equal(expectedPath, environment.ContentRootPath); + } + + [Fact] + public void UseSolutionRelativeContentRoot_WithMultipleSolutionNames_WorksWithMultipleFiles() + { + var slnFile = Path.Combine(_tempDirectory, "TestApp.sln"); + var slnxFile = Path.Combine(_tempDirectory, "TestApp.slnx"); + File.WriteAllText(slnFile, "Microsoft Visual Studio Solution File, Format Version 12.00"); + File.WriteAllText(slnxFile, """ + + + + + + """); + + var builder = new WebHostBuilder() + .UseTestServer() + .Configure(app => { }); + + builder.UseSolutionRelativeContentRoot("src", applicationBasePath: _tempDirectory, solutionNames: ["*.sln", "*.slnx"]); + + using var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + + Assert.Equal(_contentDirectory, environment.ContentRootPath); + } + + [Fact] + public void UseSolutionRelativeContentRoot_ThrowsWhenSolutionNotFound() + { + var builder = new WebHostBuilder() + .UseTestServer() + .Configure(app => { }); + + var exception = Assert.Throws(() => + builder.UseSolutionRelativeContentRoot("src", applicationBasePath: _tempDirectory)); + + Assert.Contains("Solution root could not be located", exception.Message); + Assert.Contains(_tempDirectory, exception.Message); + } + + [Fact] + public void UseSolutionRelativeContentRoot_WithSolutionName_SearchesParentDirectories() + { + var subDirectory = Path.Combine(_tempDirectory, "sub", "folder"); + Directory.CreateDirectory(subDirectory); + + var solutionFile = Path.Combine(_tempDirectory, "TestApp.slnx"); + File.WriteAllText(solutionFile, """ + + + + + + """); + + var builder = new WebHostBuilder() + .UseTestServer() + .Configure(app => { }); + + builder.UseSolutionRelativeContentRoot("src", subDirectory, "*.slnx"); + + using var host = builder.Build(); + var environment = host.Services.GetRequiredService(); + + Assert.Equal(_contentDirectory, environment.ContentRootPath); + } + + public void Dispose() + { + if (Directory.Exists(_tempDirectory)) + { + Directory.Delete(_tempDirectory, recursive: true); + } + } +} +#pragma warning restore ASPDEPR008 // WebHost is obsolete +#pragma warning disable ASPDEPR004 // WebHostBuilder is obsolete diff --git a/src/Http/Http.Abstractions/src/Metadata/ApiEndpointMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/ApiEndpointMetadata.cs deleted file mode 100644 index 815bf2c834c9..000000000000 --- a/src/Http/Http.Abstractions/src/Metadata/ApiEndpointMetadata.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http.Metadata; - -/// -/// Metadata that indicates the endpoint is intended for API clients. -/// When present, authentication handlers should prefer returning status codes over browser redirects. -/// -internal sealed class ApiEndpointMetadata : IApiEndpointMetadata -{ - /// - /// Singleton instance of . - /// - public static readonly ApiEndpointMetadata Instance = new(); - - private ApiEndpointMetadata() - { - } -} diff --git a/src/Http/Http.Abstractions/src/Metadata/DisableCookieRedirectMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/DisableCookieRedirectMetadata.cs new file mode 100644 index 000000000000..d7aad7f5c217 --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/DisableCookieRedirectMetadata.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Metadata that indicates the endpoint should disable cookie-based authentication redirects. +/// When present, authentication handlers should prefer returning status codes over browser redirects. +/// +internal sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata +{ + /// + /// Singleton instance of . + /// + public static readonly DisableCookieRedirectMetadata Instance = new(); + + private DisableCookieRedirectMetadata() + { + } +} diff --git a/src/Http/Http.Abstractions/src/Metadata/IAllowCookieRedirectMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IAllowCookieRedirectMetadata.cs new file mode 100644 index 000000000000..63c0de5859e3 --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/IAllowCookieRedirectMetadata.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Metadata that indicates the endpoint should allow cookie-based authentication redirects. +/// This is normally the default behavior, but it exists to override no matter the order. +/// When present, the cookie authentication handler will prefer browser login or access denied redirects over 401 and 403 status codes. +/// +public interface IAllowCookieRedirectMetadata +{ +} diff --git a/src/Http/Http.Abstractions/src/Metadata/IApiEndpointMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IApiEndpointMetadata.cs deleted file mode 100644 index cadaafcae1a9..000000000000 --- a/src/Http/Http.Abstractions/src/Metadata/IApiEndpointMetadata.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Http.Metadata; - -/// -/// Metadata that indicates the endpoint is an API intended for programmatic access rather than direct browser navigation. -/// When present, authentication handlers should prefer returning status codes over browser redirects. -/// -public interface IApiEndpointMetadata -{ -} diff --git a/src/Http/Http.Abstractions/src/Metadata/IDisableCookieRedirectMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IDisableCookieRedirectMetadata.cs new file mode 100644 index 000000000000..c9651833552a --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/IDisableCookieRedirectMetadata.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Metadata that indicates the endpoint should disable cookie-based authentication redirects +/// typically because it is intended for API clients rather than direct browser navigation. +/// +/// overrides this no matter the order. +/// +/// When present and not overridden, the cookie authentication handler will prefer using +/// 401 and 403 status codes over redirecting to the login or access denied paths. +/// +public interface IDisableCookieRedirectMetadata +{ +} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index d0fc0872dd90..6ccd592237b8 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -1,5 +1,6 @@ #nullable enable -Microsoft.AspNetCore.Http.Metadata.IApiEndpointMetadata +Microsoft.AspNetCore.Http.Metadata.IAllowCookieRedirectMetadata +Microsoft.AspNetCore.Http.Metadata.IDisableCookieRedirectMetadata Microsoft.AspNetCore.Http.Metadata.IDisableValidationMetadata Microsoft.AspNetCore.Http.ProducesResponseTypeMetadata.Description.get -> string? Microsoft.AspNetCore.Http.ProducesResponseTypeMetadata.Description.set -> void diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGenerator.cs b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGenerator.cs index f5e2abacaaf1..6823c8746f02 100644 --- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGenerator.cs +++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGenerator.cs @@ -253,7 +253,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) if (hasJsonBody || hasResponseMetadata) { - codeWriter.WriteLine(RequestDelegateGeneratorSources.ApiEndpointMetadataClass); + codeWriter.WriteLine(RequestDelegateGeneratorSources.DisableCookieRedirectMetadataClass); } if (hasFormBody || hasJsonBody || hasResponseMetadata) diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGeneratorSources.cs b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGeneratorSources.cs index b626db274124..4c0e6609382f 100644 --- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGeneratorSources.cs +++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/RequestDelegateGeneratorSources.cs @@ -493,18 +493,18 @@ public AntiforgeryMetadata(bool requiresValidation) } """; - public static string ApiEndpointMetadataClass = """ - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + public static string DisableCookieRedirectMetadataClass = """ + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs index 97fe2dc990ff..e4a35735b65b 100644 --- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs +++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/StaticRouteHandlerModel.Emitter.cs @@ -218,7 +218,7 @@ private static void EmitBuiltinResponseTypeMetadata(this Endpoint endpoint, Code else if (response.ResponseType is { } responseType) { codeWriter.WriteLine($$"""options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof({{responseType.ToDisplayString(EmitterConstants.DisplayFormatWithoutNullability)}}), contentTypes: GeneratedMetadataConstants.JsonContentType));"""); - codeWriter.WriteLine("ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder);"); + codeWriter.WriteLine("DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder);"); } } @@ -336,7 +336,7 @@ public static void EmitJsonAcceptsMetadata(this Endpoint endpoint, CodeWriter co codeWriter.WriteLine("if (!serviceProviderIsService.IsService(type))"); codeWriter.StartBlock(); codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType));"); - codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance);"); + codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance);"); codeWriter.WriteLine("break;"); codeWriter.EndBlock(); codeWriter.EndBlock(); @@ -344,7 +344,7 @@ public static void EmitJsonAcceptsMetadata(this Endpoint endpoint, CodeWriter co else { codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType));"); - codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance);"); + codeWriter.WriteLine("options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance);"); } } diff --git a/src/Http/Http.Extensions/src/AllowCookieRedirectAttribute.cs b/src/Http/Http.Extensions/src/AllowCookieRedirectAttribute.cs new file mode 100644 index 000000000000..b559f3b1081f --- /dev/null +++ b/src/Http/Http.Extensions/src/AllowCookieRedirectAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Http.Metadata; + +namespace Microsoft.AspNetCore.Http; + +/// +/// Specifies that cookie-based authentication redirects are allowed for an endpoint. +/// This is normally the default behavior, but it exists to override no matter the order. +/// When present, the cookie authentication handler will prefer browser login or access denied redirects over 401 and 403 status codes. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +public sealed class AllowCookieRedirectAttribute : Attribute, IAllowCookieRedirectMetadata +{ +} diff --git a/src/Http/Http.Extensions/src/CookieRedirectEndpointConventionBuilderExtensions.cs b/src/Http/Http.Extensions/src/CookieRedirectEndpointConventionBuilderExtensions.cs new file mode 100644 index 000000000000..c7e045448d46 --- /dev/null +++ b/src/Http/Http.Extensions/src/CookieRedirectEndpointConventionBuilderExtensions.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Metadata; + +namespace Microsoft.AspNetCore.Builder; + +/// +/// Cookie redirect extension methods for . +/// +public static class CookieRedirectEndpointConventionBuilderExtensions +{ + private static readonly AllowCookieRedirectAttribute _allowCookieRedirectAttribute = new(); + + /// + /// Specifies that cookie-based authentication redirects are disabled for an endpoint using . + /// When present and not overridden by or , + /// the cookie authentication handler will prefer using 401 and 403 status codes over redirecting to the login or access denied paths. + /// + /// The type of endpoint convention builder. + /// The endpoint convention builder. + /// The original convention builder parameter. + public static TBuilder DisableCookieRedirect(this TBuilder builder) where TBuilder : IEndpointConventionBuilder + { + builder.Add(b => b.Metadata.Add(DisableCookieRedirectMetadata.Instance)); + return builder; + } + + /// + /// Specifies that cookie-based authentication redirects are allowed for an endpoint using . + /// This is normally the default behavior, but it exists to override no matter the order. + /// When present, the cookie authentication handler will prefer browser login or access denied redirects over 401 and 403 status codes. + /// + /// The type of endpoint convention builder. + /// The endpoint convention builder. + /// The original convention builder parameter. + public static TBuilder AllowCookieRedirect(this TBuilder builder) where TBuilder : IEndpointConventionBuilder + { + builder.Add(b => b.Metadata.Add(_allowCookieRedirectAttribute)); + return builder; + } +} diff --git a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..dda4ec8ff207 100644 --- a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt @@ -1 +1,6 @@ #nullable enable +Microsoft.AspNetCore.Builder.CookieRedirectEndpointConventionBuilderExtensions +Microsoft.AspNetCore.Http.AllowCookieRedirectAttribute +Microsoft.AspNetCore.Http.AllowCookieRedirectAttribute.AllowCookieRedirectAttribute() -> void +static Microsoft.AspNetCore.Builder.CookieRedirectEndpointConventionBuilderExtensions.AllowCookieRedirect(this TBuilder builder) -> TBuilder +static Microsoft.AspNetCore.Builder.CookieRedirectEndpointConventionBuilderExtensions.DisableCookieRedirect(this TBuilder builder) -> TBuilder diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index f5182f47c1f5..ee76afb27f69 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -405,7 +405,7 @@ private static Expression[] CreateArgumentsAndInferMetadata(MethodInfo methodInf // When present, authentication handlers should prefer returning status codes over browser redirects. if (factoryContext.JsonRequestBodyParameter is not null) { - factoryContext.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + factoryContext.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } PopulateBuiltInResponseTypeMetadata(methodInfo.ReturnType, factoryContext); @@ -1062,7 +1062,7 @@ private static void PopulateBuiltInResponseTypeMetadata(Type returnType, Request { // Since this endpoint responds with JSON, we assume its an API endpoint not intended for browser navigation, // but we don't want to bother adding this metadata twice if we've already inferred it based on the expected JSON request body. - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } } diff --git a/src/Http/Http.Extensions/test/CookieRedirectEndpointConventionBuilderExtensionsTests.cs b/src/Http/Http.Extensions/test/CookieRedirectEndpointConventionBuilderExtensionsTests.cs new file mode 100644 index 000000000000..18610cdca905 --- /dev/null +++ b/src/Http/Http.Extensions/test/CookieRedirectEndpointConventionBuilderExtensionsTests.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http.Metadata; + +namespace Microsoft.AspNetCore.Http.Extensions.Tests; + +public class CookieRedirectEndpointConventionBuilderExtensionsTests +{ + [Fact] + public void DisableCookieRedirect_AddsMetadata() + { + // Arrange + var builder = new TestEndpointConventionBuilder(); + + // Act + builder.DisableCookieRedirect(); + + // Assert + Assert.IsAssignableFrom(Assert.Single(builder.Metadata)); + } + + [Fact] + public void AllowCookieRedirect_AddsMetadata() + { + // Arrange + var builder = new TestEndpointConventionBuilder(); + + // Act + builder.AllowCookieRedirect(); + + // Assert + Assert.IsAssignableFrom(Assert.Single(builder.Metadata)); + } + + [Fact] + public void DisableCookieRedirect_ReturnsBuilder() + { + // Arrange + var builder = new TestEndpointConventionBuilder(); + + // Act + var result = builder.DisableCookieRedirect(); + + // Assert + Assert.Same(builder, result); + } + + [Fact] + public void AllowCookieRedirect_ReturnsBuilder() + { + // Arrange + var builder = new TestEndpointConventionBuilder(); + + // Act + var result = builder.AllowCookieRedirect(); + + // Assert + Assert.Same(builder, result); + } + + private sealed class TestEndpointConventionBuilder : EndpointBuilder, IEndpointConventionBuilder + { + public void Add(Action convention) + { + convention(this); + } + + public override Endpoint Build() => throw new NotImplementedException(); + } +} diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 7cee04fb8a0f..8ace889b526b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -2825,8 +2825,8 @@ public void Create_CombinesAllMetadata_InCorrectOrder() m => Assert.True(m is AcceptsMetadata am && am.RequestType == typeof(AddsCustomParameterMetadata)), // Inferred ParameterBinding metadata m => Assert.True(m is IParameterBindingMetadata { Name: "param1" }), - // Inferred IApiEndpointMetadata from RDF for complex request and response type - m => Assert.True(m is IApiEndpointMetadata), + // Inferred IDisableCookieRedirectMetadata from RDF for complex request and response type + m => Assert.True(m is IDisableCookieRedirectMetadata), // Inferred ProducesResponseTypeMetadata from RDF for complex type m => Assert.Equal(typeof(CountsDefaultEndpointMetadataPoco), ((IProducesResponseTypeMetadata)m).Type), // Metadata provided by parameters implementing IEndpointParameterMetadataProvider diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt index 18cf2ce8010b..62465758d43e 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/HandlesEndpointsWithAndWithoutDiagnostics.generated.txt @@ -225,17 +225,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt index 04b1dc6c3bbe..2011bb74d023 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_NullableReturn.generated.txt @@ -363,17 +363,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt index 6c1240e6b8f3..d4bed32cff50 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_BindAsync_Snapshot.generated.txt @@ -2225,17 +2225,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt index 830c81d1309b..6159dd40f846 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitBodyParam_ComplexReturn_Snapshot.generated.txt @@ -412,17 +412,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt index bd0ceac25240..2547e7535558 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_ComplexTypeArrayParam.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -260,17 +260,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt index e2c866e2c2bb..b2d3d5182a98 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_NullableStringArrayParam.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -232,17 +232,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt index b2fc71b9158a..3b6dfaa17a49 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitHeader_StringArrayParam.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -232,17 +232,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt index 9aee726d2055..e4de33218466 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_ComplexTypeArrayParam.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -260,17 +260,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional.generated.txt index 01d6808eaf6e..598d6ecd2a8d 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -260,17 +260,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt index 01d6808eaf6e..598d6ecd2a8d 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -260,17 +260,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional.generated.txt index 40a1b29bb120..6bfc8e54f428 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32?[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -260,17 +260,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt index 40a1b29bb120..6bfc8e54f428 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32?[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -260,17 +260,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt index 9bebb6eea5ee..a26bf2b75f56 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_NullableStringArrayParam.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -231,17 +231,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt index 7931fdc136d0..0b1e00050add 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -231,17 +231,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional.generated.txt index 757eb5f81f8d..fa538f0fe524 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.String[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -231,17 +231,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt index 757eb5f81f8d..fa538f0fe524 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.String[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -231,17 +231,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt index 2e18c631a26a..73f90510b5f0 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitServiceParam_SimpleReturn_Snapshot.generated.txt @@ -439,17 +439,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt index 3663350edf78..9b10d9642c06 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ExplicitSource_SimpleReturn_Snapshot.generated.txt @@ -606,17 +606,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt index 07e79ab29820..718e7346565c 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_ComplexTypeArrayParam.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -267,17 +267,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional.generated.txt index e51b39e6f806..93b03a43f10b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -267,17 +267,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt index e51b39e6f806..93b03a43f10b 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_IntArrayParam_Optional_QueryNotPresent.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -267,17 +267,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional.generated.txt index 2e4b0d343e08..a4534206e6cd 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32?[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -267,17 +267,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt index 2e4b0d343e08..a4534206e6cd 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableIntArrayParam_Optional_QueryNotPresent.generated.txt @@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Http.Generated var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: true, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32?[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -267,17 +267,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt index 857501669994..15f5ee3f3a31 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam.generated.txt @@ -81,14 +81,14 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -329,17 +329,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt index 857501669994..15f5ee3f3a31 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_EmptyQueryValues.generated.txt @@ -81,14 +81,14 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -329,17 +329,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt index 857501669994..15f5ee3f3a31 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_NullableStringArrayParam_QueryNotPresent.generated.txt @@ -81,14 +81,14 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -329,17 +329,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt index 9c1fe26e5902..1bfa6b37f0a0 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam.generated.txt @@ -81,14 +81,14 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -329,17 +329,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional.generated.txt index 65ddf7109200..18b9123305b6 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional.generated.txt @@ -81,14 +81,14 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.String[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -329,17 +329,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt index 65ddf7109200..18b9123305b6 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ImplicitQuery_StringArrayParam_Optional_QueryNotPresent.generated.txt @@ -81,14 +81,14 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: true)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.String[]), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -329,17 +329,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt index bc9c6a59d734..2eedb8ebb4e1 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_JsonBodyOrService_HandlesBothJsonAndService.generated.txt @@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } @@ -346,17 +346,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt index 560f0ec1f94a..5f19b3e84abc 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleSpecialTypeParam_StringReturn.generated.txt @@ -234,17 +234,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt index 0c26ae021468..df4453e888a8 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_MultipleStringParam_StringReturn.generated.txt @@ -267,17 +267,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt index 18cf2ce8010b..62465758d43e 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_NoParam_StringReturn_WithFilter.generated.txt @@ -225,17 +225,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt index 18cf2ce8010b..62465758d43e 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsString_Has_Metadata.generated.txt @@ -225,17 +225,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt index 808d55a96981..c13dd9830c01 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_ReturnsTodo_Has_Metadata.generated.txt @@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::Microsoft.AspNetCore.Http.Generators.Tests.Todo), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -219,17 +219,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt index 509930131549..1fcb2d9a7010 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleComplexTypeParam_StringReturn.generated.txt @@ -266,17 +266,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt index b1b71f8eecd5..dca5ca2932c2 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleEnumParam_StringReturn.generated.txt @@ -266,17 +266,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt index b56621cb8c50..44bf443feb7f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_SingleNullableStringParam_WithEmptyQueryStringValueProvided_StringReturn.generated.txt @@ -238,17 +238,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt index 72e03e06b74e..f034206dfb59 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapAction_TakesCustomMetadataEmitter_Has_Metadata.generated.txt @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } @@ -334,17 +334,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt index 7409065d2ce6..48eafa3d6687 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Get_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } @@ -335,17 +335,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt index 7409065d2ce6..48eafa3d6687 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndGet_WithArrayQueryString_AndBody_ShouldUseQueryString.generated.txt @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } @@ -335,17 +335,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index 7409065d2ce6..48eafa3d6687 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_PostAndPut_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } @@ -335,17 +335,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index 7409065d2ce6..48eafa3d6687 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapMethods_Post_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } @@ -335,17 +335,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt index 3fb5a66501a7..d919734fe2bc 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_AndBody_ShouldUseBody.generated.txt @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } @@ -335,17 +335,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt index f34ec56593e5..feb0157220ce 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/MapPost_WithArrayQueryString_ShouldFail.generated.txt @@ -81,14 +81,14 @@ namespace Microsoft.AspNetCore.Http.Generated if (!serviceProviderIsService.IsService(type)) { options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(type: type, isOptional: isOptional, contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); break; } } var parameters = methodInfo.GetParameters(); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("p", parameters[0], hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(global::System.Int32), contentTypes: GeneratedMetadataConstants.JsonContentType)); - ApiEndpointMetadata.AddApiEndpointMetadataIfMissing(options.EndpointBuilder); + DisableCookieRedirectMetadata.AddMetadataIfMissing(options.EndpointBuilder); return new RequestDelegateMetadataResult { EndpointMetadata = options.EndpointBuilder.Metadata.AsReadOnly() }; }; RequestDelegateFactoryFunc createRequestDelegate = (del, options, inferredMetadataResult) => @@ -329,17 +329,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt index bf51a39927f5..93d79c0abaec 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_NoParam_StringReturn.generated.txt @@ -414,17 +414,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt index ffb90a91f7fc..e2b916aea081 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/Multiple_MapAction_WithParams_StringReturn.generated.txt @@ -428,17 +428,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt index fea0e359a92f..2211c3a0291f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsDifferentInterceptorsFromSameLocation.generated.txt @@ -388,17 +388,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt index cafc9280c930..df7e24a9eebc 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/SupportsSameInterceptorsFromDifferentFiles.generated.txt @@ -257,17 +257,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt index 489a8b16b73a..02fc8baf1ac0 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/Baselines/VerifyAsParametersBaseline.generated.txt @@ -446,7 +446,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Todo", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody)!.GetProperty("Todo")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithImplicitFromBody).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.TodoStruct) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); @@ -568,7 +568,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("HttpContext", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("HttpContext")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[0]), hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Value", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType)!.GetProperty("Value")!, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParametersListWithMetadataType).GetConstructor(new[] { typeof(Microsoft.AspNetCore.Http.HttpContext), typeof(Microsoft.AspNetCore.Http.Generators.Tests.AddsCustomParameterMetadataAsProperty) })?.GetParameters()[1]), hasTryParse: false, hasBindAsync: false, isOptional: false)); var parameterInfos = methodInfo.GetParameters(); @@ -685,7 +685,7 @@ namespace Microsoft.AspNetCore.Http.Generated Debug.Assert(options.EndpointBuilder != null, "EndpointBuilder not found."); options.EndpointBuilder.Metadata.Add(new System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.Http.RequestDelegateGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")); options.EndpointBuilder.Metadata.Add(new AcceptsMetadata(contentTypes: GeneratedMetadataConstants.JsonContentType)); - options.EndpointBuilder.Metadata.Add(ApiEndpointMetadata.Instance); + options.EndpointBuilder.Metadata.Add(DisableCookieRedirectMetadata.Instance); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Todo", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterRecordStructWithJsonBodyOrService)!.GetProperty("Todo")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ParameterBindingMetadata("Service", new PropertyAsParameterInfo(false, typeof(Microsoft.AspNetCore.Http.Generators.Tests.ParameterRecordStructWithJsonBodyOrService)!.GetProperty("Service")!), hasTryParse: false, hasBindAsync: false, isOptional: false)); options.EndpointBuilder.Metadata.Add(new ProducesResponseTypeMetadata(statusCode: StatusCodes.Status200OK, type: typeof(string), contentTypes: GeneratedMetadataConstants.PlaintextContentType)); @@ -969,17 +969,17 @@ namespace Microsoft.AspNetCore.Http.Generated } - file sealed class ApiEndpointMetadata : IApiEndpointMetadata + file sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static readonly ApiEndpointMetadata Instance = new(); + public static readonly DisableCookieRedirectMetadata Instance = new(); - private ApiEndpointMetadata() + private DisableCookieRedirectMetadata() { } - public static void AddApiEndpointMetadataIfMissing(EndpointBuilder builder) + public static void AddMetadataIfMissing(EndpointBuilder builder) { - if (!builder.Metadata.Any(m => m is IApiEndpointMetadata)) + if (!builder.Metadata.Any(m => m is IDisableCookieRedirectMetadata)) { builder.Metadata.Add(Instance); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs index 1f3412e54391..2814a136e55f 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Metadata.cs @@ -506,10 +506,10 @@ public async Task InferMetadata_ThenCreate_CombinesAllMetadata_InCorrectOrder() // Act var endpoint = GetEndpointFromCompilation(compilation); - // IApiEndpointMetadata is tricky to order consistently because it depends on whether AddsCustomParameterMetadata is registered - // as a service at runtime. However, the order of IApiEndpointMetadata is not significant since there's no way to override it + // IDisableCookieRedirectMetadata is tricky to order consistently because it depends on whether AddsCustomParameterMetadata is registered + // as a service at runtime. However, the order of IDisableCookieRedirectMetadata is not significant since there's no way to override it // other than removing it. - Assert.Single(endpoint.Metadata, m => m is IApiEndpointMetadata); + Assert.Single(endpoint.Metadata, m => m is IDisableCookieRedirectMetadata); // Assert // NOTE: Depending on whether we are running under RDG or RDG, there are some generated types which @@ -525,7 +525,7 @@ m is not HttpMethodMetadata && m is not Attribute1 && m is not Attribute2 && m is not IRouteDiagnosticsMetadata && - m is not IApiEndpointMetadata); + m is not IDisableCookieRedirectMetadata); Assert.Collection(filteredMetadata, // Inferred AcceptsMetadata from RDF for complex type diff --git a/src/Http/Http.Results/src/Accepted.cs b/src/Http/Http.Results/src/Accepted.cs index d71fe9072c7e..b6b890960848 100644 --- a/src/Http/Http.Results/src/Accepted.cs +++ b/src/Http/Http.Results/src/Accepted.cs @@ -82,6 +82,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/AcceptedAtRoute.cs b/src/Http/Http.Results/src/AcceptedAtRoute.cs index 900ee507d9cf..8c5d5a7c7b04 100644 --- a/src/Http/Http.Results/src/AcceptedAtRoute.cs +++ b/src/Http/Http.Results/src/AcceptedAtRoute.cs @@ -109,6 +109,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status202Accepted, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs b/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs index b8e4304a7237..026a040fc35c 100644 --- a/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs +++ b/src/Http/Http.Results/src/AcceptedAtRouteOfT.cs @@ -123,6 +123,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status202Accepted, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/AcceptedOfT.cs b/src/Http/Http.Results/src/AcceptedOfT.cs index 346070714b43..4fdc346ebabd 100644 --- a/src/Http/Http.Results/src/AcceptedOfT.cs +++ b/src/Http/Http.Results/src/AcceptedOfT.cs @@ -101,6 +101,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status202Accepted, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/BadRequest.cs b/src/Http/Http.Results/src/BadRequest.cs index 0356afedc2c7..70a6eba3199f 100644 --- a/src/Http/Http.Results/src/BadRequest.cs +++ b/src/Http/Http.Results/src/BadRequest.cs @@ -52,6 +52,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status400BadRequest, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/BadRequestOfT.cs b/src/Http/Http.Results/src/BadRequestOfT.cs index b8fa51423d7e..32bb7a0ca613 100644 --- a/src/Http/Http.Results/src/BadRequestOfT.cs +++ b/src/Http/Http.Results/src/BadRequestOfT.cs @@ -66,6 +66,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status400BadRequest, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/Conflict.cs b/src/Http/Http.Results/src/Conflict.cs index f0067ba4aea1..1de48f75a455 100644 --- a/src/Http/Http.Results/src/Conflict.cs +++ b/src/Http/Http.Results/src/Conflict.cs @@ -52,6 +52,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status409Conflict, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/ConflictOfT.cs b/src/Http/Http.Results/src/ConflictOfT.cs index dda54d9dd5cc..32241e2b9424 100644 --- a/src/Http/Http.Results/src/ConflictOfT.cs +++ b/src/Http/Http.Results/src/ConflictOfT.cs @@ -66,6 +66,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status409Conflict, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/Created.cs b/src/Http/Http.Results/src/Created.cs index 1959556280c9..e9eb9fa96655 100644 --- a/src/Http/Http.Results/src/Created.cs +++ b/src/Http/Http.Results/src/Created.cs @@ -82,6 +82,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status201Created, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/CreatedAtRoute.cs b/src/Http/Http.Results/src/CreatedAtRoute.cs index 2ff79596ba36..e194030ee327 100644 --- a/src/Http/Http.Results/src/CreatedAtRoute.cs +++ b/src/Http/Http.Results/src/CreatedAtRoute.cs @@ -109,6 +109,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status201Created, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/CreatedAtRouteOfT.cs b/src/Http/Http.Results/src/CreatedAtRouteOfT.cs index a2868afdc0a1..9b972b29c044 100644 --- a/src/Http/Http.Results/src/CreatedAtRouteOfT.cs +++ b/src/Http/Http.Results/src/CreatedAtRouteOfT.cs @@ -126,6 +126,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status201Created, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/CreatedOfT.cs b/src/Http/Http.Results/src/CreatedOfT.cs index a12e52b9f69f..267f25fae39a 100644 --- a/src/Http/Http.Results/src/CreatedOfT.cs +++ b/src/Http/Http.Results/src/CreatedOfT.cs @@ -100,6 +100,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status201Created, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/InternalServerErrorOfT.cs b/src/Http/Http.Results/src/InternalServerErrorOfT.cs index c726debf67c0..1a813205de25 100644 --- a/src/Http/Http.Results/src/InternalServerErrorOfT.cs +++ b/src/Http/Http.Results/src/InternalServerErrorOfT.cs @@ -66,6 +66,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status500InternalServerError, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/JsonHttpResultOfT.cs b/src/Http/Http.Results/src/JsonHttpResultOfT.cs index fea230609a20..d016be7f97e0 100644 --- a/src/Http/Http.Results/src/JsonHttpResultOfT.cs +++ b/src/Http/Http.Results/src/JsonHttpResultOfT.cs @@ -137,6 +137,6 @@ public Task ExecuteAsync(HttpContext httpContext) static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder) { - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/NoContent.cs b/src/Http/Http.Results/src/NoContent.cs index 35ffb3c56701..e76f8cc0f68c 100644 --- a/src/Http/Http.Results/src/NoContent.cs +++ b/src/Http/Http.Results/src/NoContent.cs @@ -52,6 +52,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status204NoContent, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/NotFoundOfT.cs b/src/Http/Http.Results/src/NotFoundOfT.cs index d37a4ee9707c..6c27054e81bd 100644 --- a/src/Http/Http.Results/src/NotFoundOfT.cs +++ b/src/Http/Http.Results/src/NotFoundOfT.cs @@ -65,6 +65,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status404NotFound, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/Ok.cs b/src/Http/Http.Results/src/Ok.cs index 837a0fa7513c..c348934d1523 100644 --- a/src/Http/Http.Results/src/Ok.cs +++ b/src/Http/Http.Results/src/Ok.cs @@ -51,6 +51,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/OkOfT.cs b/src/Http/Http.Results/src/OkOfT.cs index 7f19598effe9..6939fb290f19 100644 --- a/src/Http/Http.Results/src/OkOfT.cs +++ b/src/Http/Http.Results/src/OkOfT.cs @@ -65,6 +65,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status200OK, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/ProblemHttpResult.cs b/src/Http/Http.Results/src/ProblemHttpResult.cs index 8ac29336f053..2aa3a7418653 100644 --- a/src/Http/Http.Results/src/ProblemHttpResult.cs +++ b/src/Http/Http.Results/src/ProblemHttpResult.cs @@ -79,6 +79,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(method); ArgumentNullException.ThrowIfNull(builder); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/ServerSentEventsResult.cs b/src/Http/Http.Results/src/ServerSentEventsResult.cs index a7951acd1554..5cbb9d871f60 100644 --- a/src/Http/Http.Results/src/ServerSentEventsResult.cs +++ b/src/Http/Http.Results/src/ServerSentEventsResult.cs @@ -105,6 +105,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status200OK, typeof(SseItem), contentTypes: ["text/event-stream"])); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/UnprocessableEntity.cs b/src/Http/Http.Results/src/UnprocessableEntity.cs index 49d3982720b8..298e162c7ae0 100644 --- a/src/Http/Http.Results/src/UnprocessableEntity.cs +++ b/src/Http/Http.Results/src/UnprocessableEntity.cs @@ -52,6 +52,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(new ProducesResponseTypeMetadata(StatusCodes.Status422UnprocessableEntity, typeof(void))); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/UnprocessableEntityOfT.cs b/src/Http/Http.Results/src/UnprocessableEntityOfT.cs index 2d1ee494a271..c59c519ec0fd 100644 --- a/src/Http/Http.Results/src/UnprocessableEntityOfT.cs +++ b/src/Http/Http.Results/src/UnprocessableEntityOfT.cs @@ -66,6 +66,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(TValue), StatusCodes.Status422UnprocessableEntity, ContentTypeConstants.ApplicationJsonContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/src/ValidationProblem.cs b/src/Http/Http.Results/src/ValidationProblem.cs index c4a539c9cd5f..bd4c75dda703 100644 --- a/src/Http/Http.Results/src/ValidationProblem.cs +++ b/src/Http/Http.Results/src/ValidationProblem.cs @@ -77,6 +77,6 @@ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, Endpoi ArgumentNullException.ThrowIfNull(builder); builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(typeof(HttpValidationProblemDetails), StatusCodes.Status400BadRequest, ContentTypeConstants.ProblemDetailsContentTypes)); - builder.Metadata.Add(ApiEndpointMetadata.Instance); + builder.Metadata.Add(DisableCookieRedirectMetadata.Instance); } } diff --git a/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs b/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs index 1d53452fa65e..9423fb06142d 100644 --- a/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedAtRouteOfTResultTests.cs @@ -135,7 +135,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs index 15a93310e80c..67c2657fb42c 100644 --- a/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedAtRouteResultTests.cs @@ -88,7 +88,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status202Accepted, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/AcceptedOfTResultTests.cs b/src/Http/Http.Results/test/AcceptedOfTResultTests.cs index 2ee6e03193d4..b2d16737c4b6 100644 --- a/src/Http/Http.Results/test/AcceptedOfTResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedOfTResultTests.cs @@ -76,7 +76,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/AcceptedResultTests.cs b/src/Http/Http.Results/test/AcceptedResultTests.cs index 171b45112174..fdb97dcc9e88 100644 --- a/src/Http/Http.Results/test/AcceptedResultTests.cs +++ b/src/Http/Http.Results/test/AcceptedResultTests.cs @@ -45,7 +45,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status202Accepted, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/BadRequestOfTResultTests.cs b/src/Http/Http.Results/test/BadRequestOfTResultTests.cs index c930db6ea80a..c58041b203d8 100644 --- a/src/Http/Http.Results/test/BadRequestOfTResultTests.cs +++ b/src/Http/Http.Results/test/BadRequestOfTResultTests.cs @@ -119,7 +119,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/BadRequestResultTests.cs b/src/Http/Http.Results/test/BadRequestResultTests.cs index cca2a4d139ee..c8bac8d0282a 100644 --- a/src/Http/Http.Results/test/BadRequestResultTests.cs +++ b/src/Http/Http.Results/test/BadRequestResultTests.cs @@ -58,7 +58,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status400BadRequest, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/ConflictOfTResultTests.cs b/src/Http/Http.Results/test/ConflictOfTResultTests.cs index be4e43d441df..fb2a0b655915 100644 --- a/src/Http/Http.Results/test/ConflictOfTResultTests.cs +++ b/src/Http/Http.Results/test/ConflictOfTResultTests.cs @@ -97,7 +97,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/ConflictResultTests.cs b/src/Http/Http.Results/test/ConflictResultTests.cs index 2f639948eac6..449eb7665b88 100644 --- a/src/Http/Http.Results/test/ConflictResultTests.cs +++ b/src/Http/Http.Results/test/ConflictResultTests.cs @@ -59,7 +59,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status409Conflict, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/CreatedAtRouteOfTResultTests.cs b/src/Http/Http.Results/test/CreatedAtRouteOfTResultTests.cs index a55f7dd6ef9c..82591bc7fc27 100644 --- a/src/Http/Http.Results/test/CreatedAtRouteOfTResultTests.cs +++ b/src/Http/Http.Results/test/CreatedAtRouteOfTResultTests.cs @@ -105,7 +105,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs b/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs index 5a36fdcce055..077bd18bfd7a 100644 --- a/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs +++ b/src/Http/Http.Results/test/CreatedAtRouteResultTests.cs @@ -86,7 +86,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status201Created, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/CreatedOfTResultTests.cs b/src/Http/Http.Results/test/CreatedOfTResultTests.cs index b4b5747745a1..600370f60386 100644 --- a/src/Http/Http.Results/test/CreatedOfTResultTests.cs +++ b/src/Http/Http.Results/test/CreatedOfTResultTests.cs @@ -111,7 +111,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/CreatedResultTests.cs b/src/Http/Http.Results/test/CreatedResultTests.cs index af1c8a1179b8..fb5e5e7f9056 100644 --- a/src/Http/Http.Results/test/CreatedResultTests.cs +++ b/src/Http/Http.Results/test/CreatedResultTests.cs @@ -76,7 +76,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status201Created, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/InternalServerErrorOfTResultTests.cs b/src/Http/Http.Results/test/InternalServerErrorOfTResultTests.cs index 1ee5bd751053..900cfa3e3a36 100644 --- a/src/Http/Http.Results/test/InternalServerErrorOfTResultTests.cs +++ b/src/Http/Http.Results/test/InternalServerErrorOfTResultTests.cs @@ -119,7 +119,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/JsonResultTests.cs b/src/Http/Http.Results/test/JsonResultTests.cs index 52d76c0a03c9..6e2b05dbcc19 100644 --- a/src/Http/Http.Results/test/JsonResultTests.cs +++ b/src/Http/Http.Results/test/JsonResultTests.cs @@ -327,7 +327,7 @@ public void PopulateMetadata_AddsNonBrowserEndpointMetadata() PopulateMetadata>(((Delegate)MyApi).GetMethodInfo(), builder); // Assert - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } private static void PopulateMetadata(MethodInfo method, EndpointBuilder builder) diff --git a/src/Http/Http.Results/test/NoContentResultTests.cs b/src/Http/Http.Results/test/NoContentResultTests.cs index fc1cae4a95aa..ce6047dd25d7 100644 --- a/src/Http/Http.Results/test/NoContentResultTests.cs +++ b/src/Http/Http.Results/test/NoContentResultTests.cs @@ -56,7 +56,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); // Assert ApiEndpointMetadata is added - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/NotFoundOfTResultTests.cs b/src/Http/Http.Results/test/NotFoundOfTResultTests.cs index ec15a5c8a7e8..4ee3c6a6eed7 100644 --- a/src/Http/Http.Results/test/NotFoundOfTResultTests.cs +++ b/src/Http/Http.Results/test/NotFoundOfTResultTests.cs @@ -79,7 +79,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/OkOfTResultTests.cs b/src/Http/Http.Results/test/OkOfTResultTests.cs index 85693056570b..0fbd02fa3cd8 100644 --- a/src/Http/Http.Results/test/OkOfTResultTests.cs +++ b/src/Http/Http.Results/test/OkOfTResultTests.cs @@ -96,7 +96,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/OkResultTests.cs b/src/Http/Http.Results/test/OkResultTests.cs index 98cac07cbb60..26a777a7c5e1 100644 --- a/src/Http/Http.Results/test/OkResultTests.cs +++ b/src/Http/Http.Results/test/OkResultTests.cs @@ -57,7 +57,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status200OK, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/UnprocessableEntityOfTResultTests.cs b/src/Http/Http.Results/test/UnprocessableEntityOfTResultTests.cs index 8c8161f9955c..16a0dfa3e14e 100644 --- a/src/Http/Http.Results/test/UnprocessableEntityOfTResultTests.cs +++ b/src/Http/Http.Results/test/UnprocessableEntityOfTResultTests.cs @@ -96,7 +96,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(typeof(Todo), producesResponseTypeMetadata.Type); Assert.Single(producesResponseTypeMetadata.ContentTypes, "application/json"); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http.Results/test/UnprocessableEntityResultTests.cs b/src/Http/Http.Results/test/UnprocessableEntityResultTests.cs index 7931b226c065..ea6eba2d668d 100644 --- a/src/Http/Http.Results/test/UnprocessableEntityResultTests.cs +++ b/src/Http/Http.Results/test/UnprocessableEntityResultTests.cs @@ -58,7 +58,7 @@ public void PopulateMetadata_AddsResponseTypeMetadata() Assert.Equal(StatusCodes.Status422UnprocessableEntity, producesResponseTypeMetadata.StatusCode); Assert.Equal(typeof(void), producesResponseTypeMetadata.Type); - Assert.Contains(builder.Metadata, m => m is IApiEndpointMetadata); + Assert.Contains(builder.Metadata, m => m is IDisableCookieRedirectMetadata); } [Fact] diff --git a/src/Http/Http/perf/Microbenchmarks/ValidatableTypesBenchmark.cs b/src/Http/Http/perf/Microbenchmarks/ValidatableTypesBenchmark.cs index 6af879569e80..1d182e89c823 100644 --- a/src/Http/Http/perf/Microbenchmarks/ValidatableTypesBenchmark.cs +++ b/src/Http/Http/perf/Microbenchmarks/ValidatableTypesBenchmark.cs @@ -244,6 +244,7 @@ public IEnumerable Validate(ValidationContext validationContex private class MockValidatableTypeInfo(Type type, ValidatablePropertyInfo[] members) : ValidatableTypeInfo(type, members) { + protected override ValidationAttribute[] GetValidationAttributes() => []; } private class MockValidatablePropertyInfo( diff --git a/src/Mvc/Mvc.Core/src/ApiControllerAttribute.cs b/src/Mvc/Mvc.Core/src/ApiControllerAttribute.cs index f7179b0b447d..cde73404a399 100644 --- a/src/Mvc/Mvc.Core/src/ApiControllerAttribute.cs +++ b/src/Mvc/Mvc.Core/src/ApiControllerAttribute.cs @@ -18,6 +18,6 @@ namespace Microsoft.AspNetCore.Mvc; /// /// [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] -public class ApiControllerAttribute : ControllerAttribute, IApiBehaviorMetadata, IApiEndpointMetadata +public class ApiControllerAttribute : ControllerAttribute, IApiBehaviorMetadata, IDisableCookieRedirectMetadata { } diff --git a/src/Mvc/Mvc.Core/test/ApplicationModels/DefaultApplicationModelProviderTest.cs b/src/Mvc/Mvc.Core/test/ApplicationModels/DefaultApplicationModelProviderTest.cs index c8c6368fe7e8..082b7e498b42 100644 --- a/src/Mvc/Mvc.Core/test/ApplicationModels/DefaultApplicationModelProviderTest.cs +++ b/src/Mvc/Mvc.Core/test/ApplicationModels/DefaultApplicationModelProviderTest.cs @@ -1280,7 +1280,7 @@ public void AddReturnTypeMetadata_ExtractsMetadataFromReturnType() Assert.NotNull(selector.EndpointMetadata); Assert.Equal(2, selector.EndpointMetadata.Count); Assert.Single(selector.EndpointMetadata.OfType()); - Assert.Single(selector.EndpointMetadata.OfType()); + Assert.Single(selector.EndpointMetadata.OfType()); Assert.Equal(200, ((ProducesResponseTypeMetadata)selector.EndpointMetadata[0]).StatusCode); } diff --git a/src/Mvc/test/Mvc.FunctionalTests/WebApplicationFactorySlnxTests.cs b/src/Mvc/test/Mvc.FunctionalTests/WebApplicationFactorySlnxTests.cs new file mode 100644 index 000000000000..60de72aa2e55 --- /dev/null +++ b/src/Mvc/test/Mvc.FunctionalTests/WebApplicationFactorySlnxTests.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using BasicWebSite; + +namespace Microsoft.AspNetCore.Mvc.FunctionalTests; + +public class WebApplicationFactorySlnxTests : IClassFixture>, IDisposable +{ + private readonly string _tempDirectory; + private readonly string _contentDirectory; + + public WebApplicationFactorySlnxTests(WebApplicationFactory factory) + { + Factory = factory; + _tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")[..8]); + _contentDirectory = Path.Combine(_tempDirectory, "BasicWebSite"); + + Directory.CreateDirectory(_tempDirectory); + Directory.CreateDirectory(_contentDirectory); + + // Create a minimal wwwroot directory to satisfy content root expectations + var wwwrootDir = Path.Combine(_contentDirectory, "wwwroot"); + Directory.CreateDirectory(wwwrootDir); + } + + public WebApplicationFactory Factory { get; } + + [Fact] + public async Task WebApplicationFactory_UsesSlnxForSolutionRelativeContentRoot() + { + // Create .slnx file in temp directory + var slnxFile = Path.Combine(_tempDirectory, "TestSolution.slnx"); + File.WriteAllText(slnxFile, """ + + + + + + + + + + """); + + var factory = Factory.WithWebHostBuilder(builder => + { + builder.UseSolutionRelativeContentRoot("BasicWebSite", _tempDirectory, "TestSolution.slnx"); + }); + + using var client = factory.CreateClient(); + + // Verify that the content root was set correctly by accessing the environment + var environment = factory.Services.GetRequiredService(); + Assert.Equal(_contentDirectory, environment.ContentRootPath); + Assert.True(Directory.Exists(environment.ContentRootPath)); + + // Verify the factory is functional with the .slnx-resolved content root + var response = await client.GetAsync("/"); + Assert.True(response.IsSuccessStatusCode); + } + + public void Dispose() + { + if (Directory.Exists(_tempDirectory)) + { + Directory.Delete(_tempDirectory, recursive: true); + } + } +} diff --git a/src/Security/Authentication/Cookies/src/CookieAuthenticationEvents.cs b/src/Security/Authentication/Cookies/src/CookieAuthenticationEvents.cs index 994d999aaf52..35a73901c06b 100644 --- a/src/Security/Authentication/Cookies/src/CookieAuthenticationEvents.cs +++ b/src/Security/Authentication/Cookies/src/CookieAuthenticationEvents.cs @@ -12,6 +12,8 @@ namespace Microsoft.AspNetCore.Authentication.Cookies; /// public class CookieAuthenticationEvents { + private static readonly bool _ignoreCookieRedirectMetadata = AppContext.TryGetSwitch("Microsoft.AspNetCore.Authentication.Cookies.IgnoreRedirectMetadata", out var isEnabled) && isEnabled; + /// /// Invoked to validate the principal. /// @@ -42,7 +44,7 @@ public class CookieAuthenticationEvents /// public Func, Task> OnRedirectToLogin { get; set; } = context => { - if (IsAjaxRequest(context.Request) || IsApiEndpoint(context.HttpContext)) + if (IsAjaxRequest(context.Request) || IsCookieRedirectDisabledByMetadata(context.HttpContext)) { context.Response.Headers.Location = context.RedirectUri; context.Response.StatusCode = 401; @@ -59,7 +61,7 @@ public class CookieAuthenticationEvents /// public Func, Task> OnRedirectToAccessDenied { get; set; } = context => { - if (IsAjaxRequest(context.Request) || IsApiEndpoint(context.HttpContext)) + if (IsAjaxRequest(context.Request) || IsCookieRedirectDisabledByMetadata(context.HttpContext)) { context.Response.Headers.Location = context.RedirectUri; context.Response.StatusCode = 403; @@ -109,10 +111,16 @@ private static bool IsAjaxRequest(HttpRequest request) string.Equals(request.Headers.XRequestedWith, "XMLHttpRequest", StringComparison.Ordinal); } - private static bool IsApiEndpoint(HttpContext context) + private static bool IsCookieRedirectDisabledByMetadata(HttpContext context) { + if (_ignoreCookieRedirectMetadata) + { + return false; + } + var endpoint = context.GetEndpoint(); - return endpoint?.Metadata.GetMetadata() is not null; + return endpoint?.Metadata.GetMetadata() is not null && + endpoint?.Metadata.GetMetadata() is null; } /// diff --git a/src/Security/Authentication/test/CookieTests.cs b/src/Security/Authentication/test/CookieTests.cs index 3dc0d638b8cf..9f209173fa35 100644 --- a/src/Security/Authentication/test/CookieTests.cs +++ b/src/Security/Authentication/test/CookieTests.cs @@ -18,6 +18,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Microsoft.Extensions.Time.Testing; +using Microsoft.AspNetCore.Routing; namespace Microsoft.AspNetCore.Authentication.Cookies; @@ -98,7 +99,7 @@ public async Task AjaxChallengeRedirectTurnsInto200WithLocationHeader() } [Fact] - public async Task ApiEndpointChallengeReturns401WithLocationHeader() + public async Task CanConfigure401ChallengeInsteadOfRedirectWithMetadata() { using var host = await CreateHost(s => { }); using var server = host.GetTestServer(); @@ -110,7 +111,7 @@ public async Task ApiEndpointChallengeReturns401WithLocationHeader() } [Fact] - public async Task ApiEndpointForbidReturns403WithLocationHeader() + public async Task CanConfigure403ForbiddenInsteadOfRedirectWithMetadata() { using var host = await CreateHost(s => { }); using var server = host.GetTestServer(); @@ -121,6 +122,30 @@ public async Task ApiEndpointForbidReturns403WithLocationHeader() Assert.StartsWith("http://example.com/Account/AccessDenied", responded.Single()); } + [Fact] + public async Task CanReenableLoginRedirectWithMetadata() + { + using var host = await CreateHost(s => { }); + using var server = host.GetTestServer(); + var transaction = await SendAsync(server, "http://example.com/api/jk/challenge"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + var responded = transaction.Response.Headers.GetValues("Location"); + Assert.Single(responded); + Assert.StartsWith("http://example.com/Account/Login", responded.Single()); + } + + [Fact] + public async Task CanReenableAccessDeniedRedirectWithMetadata() + { + using var host = await CreateHost(s => { }); + using var server = host.GetTestServer(); + var transaction = await SendAsync(server, "http://example.com/api/jk/forbid"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + var responded = transaction.Response.Headers.GetValues("Location"); + Assert.Single(responded); + Assert.StartsWith("http://example.com/Account/AccessDenied", responded.Single()); + } + [Fact] public async Task ProtectedCustomRequestShouldRedirectToCustomRedirectUri() { @@ -1908,17 +1933,25 @@ private static async Task CreateHostWithServices(Action { - var apiRouteGroup = endpoints.MapGroup("/api").WithMetadata(new TestApiEndpointMetadata()); - // Add endpoints with IApiEndpointMetadata - apiRouteGroup.MapGet("/challenge", async context => + void AddChallengeAndForbidEndpoints(IEndpointRouteBuilder routeGroup) { - await context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme); - }); + routeGroup.MapGet("/challenge", async context => + { + await context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme); + }); - apiRouteGroup.MapGet("/forbid", async context => - { - await context.ForbidAsync(CookieAuthenticationDefaults.AuthenticationScheme); - }); + routeGroup.MapGet("/forbid", async context => + { + await context.ForbidAsync(CookieAuthenticationDefaults.AuthenticationScheme); + }); + } + + var apiRouteGroup = endpoints.MapGroup("/api").DisableCookieRedirect(); + AddChallengeAndForbidEndpoints(apiRouteGroup); + + // IAllowCookieRedirect always wins if present. Adding IDisableCookieRedirect before and afterwards does not override it. + var overriddenRouteGroup = apiRouteGroup.MapGroup("/jk").AllowCookieRedirect().DisableCookieRedirect(); + AddChallengeAndForbidEndpoints(overriddenRouteGroup); }); }) .ConfigureServices(services => @@ -1994,8 +2027,4 @@ private class Transaction public string ResponseText { get; set; } public XElement ResponseElement { get; set; } } - - private class TestApiEndpointMetadata : IApiEndpointMetadata - { - } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs index 664e466ccefb..094ba0058b87 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs @@ -407,21 +407,20 @@ public void Reset() _manuallySetRequestAbortToken = null; - // Lock to prevent CancelRequestAbortedToken from attempting to cancel a disposed CTS. - CancellationTokenSource? localAbortCts = null; - lock (_abortLock) { _preventRequestAbortedCancellation = false; - if (_abortedCts?.TryReset() == false) + + // If the connection has already been aborted, allow that to be observed during the next request. + if (!_connectionAborted && _abortedCts is not null) { - localAbortCts = _abortedCts; - _abortedCts = null; + // _connectionAborted is terminal and only set inside the _abortLock, so if it isn't set here, + // _abortedCts has not been canceled yet. + var resetSuccess = _abortedCts.TryReset(); + Debug.Assert(resetSuccess); } } - localAbortCts?.Dispose(); - Output?.Reset(); _requestHeadersParsed = 0; @@ -760,7 +759,7 @@ private async Task ProcessRequests(IHttpApplication applicat } else if (!HasResponseStarted) { - // If the request was aborted and no response was sent, we use status code 499 for logging + // If the request was aborted and no response was sent, we use status code 499 for logging StatusCode = StatusCodes.Status499ClientClosedRequest; } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index da2b547c03a7..8524b35f3947 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -129,7 +129,6 @@ public bool ReceivedEmptyRequestBody protected override void OnReset() { _keepAlive = true; - _connectionAborted = false; _userTrailers = null; // Reset Http2 Features diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs index 20ec37eb3cc7..c2db22acd8bd 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Connection.cs @@ -602,15 +602,16 @@ private async Task CreateHttp3Stream(ConnectionContext streamContext, // Check whether there is an existing HTTP/3 stream on the transport stream. // A stream will only be cached if the transport stream itself is reused. - if (!persistentStateFeature.State.TryGetValue(StreamPersistentStateKey, out var s)) + if (!persistentStateFeature.State.TryGetValue(StreamPersistentStateKey, out var s) || + s is not Http3Stream { CanReuse: true } reusableStream) { stream = new Http3Stream(application, CreateHttpStreamContext(streamContext)); - persistentStateFeature.State.Add(StreamPersistentStateKey, stream); + persistentStateFeature.State[StreamPersistentStateKey] = stream; } else { - stream = (Http3Stream)s!; - stream.InitializeWithExistingContext(streamContext.Transport); + stream = reusableStream; + reusableStream.InitializeWithExistingContext(streamContext.Transport); } _streamLifetimeHandler.OnStreamCreated(stream); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs index 672c566b7538..7ada6890f867 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs @@ -66,6 +66,8 @@ internal abstract partial class Http3Stream : HttpProtocol, IHttp3Stream, IHttpS private bool IsAbortedRead => (_completionState & StreamCompletionFlags.AbortedRead) == StreamCompletionFlags.AbortedRead; public bool IsCompleted => (_completionState & StreamCompletionFlags.Completed) == StreamCompletionFlags.Completed; + public bool CanReuse => !_connectionAborted && HasResponseCompleted; + public bool ReceivedEmptyRequestBody { get @@ -957,7 +959,6 @@ private Task ProcessDataFrameAsync(in ReadOnlySequence payload) protected override void OnReset() { _keepAlive = true; - _connectionAborted = false; _userTrailers = null; _isWebTransportSessionAccepted = false; _isMethodConnect = false; diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs index f76cd141bb89..e0840b525f6d 100644 --- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs +++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs @@ -879,6 +879,160 @@ public async Task GET_MultipleRequestsInSequence_ReusedState() } } + [ConditionalFact] + [MsQuicSupported] + public async Task GET_RequestAbortedByClient_StateNotReused() + { + // Arrange + object persistedState = null; + var requestCount = 0; + var abortedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var requestStartedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + var builder = CreateHostBuilder(async context => + { + requestCount++; + var persistentStateCollection = context.Features.Get().State; + if (persistentStateCollection.TryGetValue("Counter", out var value)) + { + persistedState = value; + } + persistentStateCollection["Counter"] = requestCount; + + if (requestCount == 1) + { + // For the first request, wait for RequestAborted to fire before returning + context.RequestAborted.Register(() => + { + Logger.LogInformation("Server received cancellation"); + abortedTcs.SetResult(); + }); + + // Signal that the request has started and is ready to be cancelled + requestStartedTcs.SetResult(); + + // Wait for the request to be aborted + await abortedTcs.Task; + } + }); + + using (var host = builder.Build()) + using (var client = HttpHelpers.CreateClient()) + { + await host.StartAsync(); + + // Act - Send first request and cancel it + var cts1 = new CancellationTokenSource(); + var request1 = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/"); + request1.Version = HttpVersion.Version30; + request1.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + + var responseTask1 = client.SendAsync(request1, cts1.Token); + + // Wait for the server to start processing the request + await requestStartedTcs.Task.DefaultTimeout(); + + // Cancel the first request + cts1.Cancel(); + await Assert.ThrowsAnyAsync(() => responseTask1).DefaultTimeout(); + + // Wait for the server to process the abort + await abortedTcs.Task.DefaultTimeout(); + + // Store the state from the first (aborted) request + var firstRequestState = persistedState; + + // Delay to ensure the stream has enough time to return to pool + await Task.Delay(100); + + // Send second request (should not reuse state from aborted request) + var request2 = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/"); + request2.Version = HttpVersion.Version30; + request2.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + + var response2 = await client.SendAsync(request2, CancellationToken.None); + response2.EnsureSuccessStatusCode(); + var secondRequestState = persistedState; + + // Assert + // First request has no persisted state (it was aborted) + Assert.Null(firstRequestState); + + // Second request should also have no persisted state since the first request was aborted + // and state should not be reused from aborted requests + Assert.Null(secondRequestState); + + await host.StopAsync(); + } + } + + [ConditionalFact] + [MsQuicSupported] + public async Task GET_RequestAbortedByServer_StateNotReused() + { + // Arrange + object persistedState = null; + var requestCount = 0; + + var builder = CreateHostBuilder(context => + { + requestCount++; + var persistentStateCollection = context.Features.Get().State; + if (persistentStateCollection.TryGetValue("Counter", out var value)) + { + persistedState = value; + } + persistentStateCollection["Counter"] = requestCount; + + if (requestCount == 1) + { + context.Abort(); + } + + return Task.CompletedTask; + }); + + using (var host = builder.Build()) + using (var client = HttpHelpers.CreateClient()) + { + await host.StartAsync(); + + var request1 = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/"); + request1.Version = HttpVersion.Version30; + request1.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + + var responseTask1 = client.SendAsync(request1, CancellationToken.None); + var ex = await Assert.ThrowsAnyAsync(() => responseTask1).DefaultTimeout(); + var innerEx = Assert.IsType(ex.InnerException); + Assert.Equal(Http3ErrorCode.InternalError, (Http3ErrorCode)innerEx.ErrorCode); + + // Store the state from the first (aborted) request + var firstRequestState = persistedState; + + // Delay to ensure the stream has enough time to return to pool + await Task.Delay(100); + + // Send second request (should not reuse state from aborted request) + var request2 = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/"); + request2.Version = HttpVersion.Version30; + request2.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + + var response2 = await client.SendAsync(request2, CancellationToken.None); + response2.EnsureSuccessStatusCode(); + var secondRequestState = persistedState; + + // Assert + // First request has no persisted state (it was aborted) + Assert.Null(firstRequestState); + + // Second request should also have no persisted state since the first request was aborted + // and state should not be reused from aborted requests + Assert.Null(secondRequestState); + + await host.StopAsync(); + } + } + [ConditionalFact] [MsQuicSupported] public async Task GET_MultipleRequests_RequestVersionOrHigher_UpgradeToHttp3() diff --git a/src/SignalR/clients/ts/signalr/src/HubConnection.ts b/src/SignalR/clients/ts/signalr/src/HubConnection.ts index 2487d0f0b558..53fc6fafa069 100644 --- a/src/SignalR/clients/ts/signalr/src/HubConnection.ts +++ b/src/SignalR/clients/ts/signalr/src/HubConnection.ts @@ -721,10 +721,19 @@ export class HubConnection { // Set the timeout timer this._timeoutHandle = setTimeout(() => this.serverTimeout(), this.serverTimeoutInMilliseconds); + // Immediately fire Keep-Alive ping if nextPing is overdue to avoid dependency on JS timers + let nextPing = this._nextKeepAlive - new Date().getTime(); + if (nextPing < 0) { + if (this._connectionState === HubConnectionState.Connected) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this._trySendPingMessage(); + } + return; + } + // Set keepAlive timer if there isn't one if (this._pingServerHandle === undefined) { - let nextPing = this._nextKeepAlive - new Date().getTime(); if (nextPing < 0) { nextPing = 0; } @@ -732,13 +741,7 @@ export class HubConnection { // The timer needs to be set from a networking callback to avoid Chrome timer throttling from causing timers to run once a minute this._pingServerHandle = setTimeout(async () => { if (this._connectionState === HubConnectionState.Connected) { - try { - await this._sendMessage(this._cachedPingMessage); - } catch { - // We don't care about the error. It should be seen elsewhere in the client. - // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering - this._cleanupPingTimer(); - } + await this._trySendPingMessage(); } }, nextPing); } @@ -1149,4 +1152,14 @@ export class HubConnection { private _createCloseMessage(): CloseMessage { return { type: MessageType.Close }; } + + private async _trySendPingMessage(): Promise { + try { + await this._sendMessage(this._cachedPingMessage); + } catch { + // We don't care about the error. It should be seen elsewhere in the client. + // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering + this._cleanupPingTimer(); + } + } } diff --git a/src/SignalR/common/Http.Connections/src/ConnectionEndpointRouteBuilderExtensions.cs b/src/SignalR/common/Http.Connections/src/ConnectionEndpointRouteBuilderExtensions.cs index f1aad8649e82..cd094cbf300e 100644 --- a/src/SignalR/common/Http.Connections/src/ConnectionEndpointRouteBuilderExtensions.cs +++ b/src/SignalR/common/Http.Connections/src/ConnectionEndpointRouteBuilderExtensions.cs @@ -127,8 +127,8 @@ public static ConnectionEndpointRouteBuilder MapConnections(this IEndpointRouteB e.Metadata.Add(data); } - // Add IApiEndpointMetadata to indicate this is a non-browser endpoint (SignalR) - e.Metadata.Add(ApiEndpointMetadata.Instance); + // Add IDisableCookieRedirectMetadata to indicate this is a non-browser endpoint (SignalR) + e.Metadata.Add(DisableCookieRedirectMetadata.Instance); }); return new ConnectionEndpointRouteBuilder(compositeConventionBuilder); @@ -160,8 +160,8 @@ public void Finally(Action finalConvention) } } - private sealed class ApiEndpointMetadata : IApiEndpointMetadata + private sealed class DisableCookieRedirectMetadata : IDisableCookieRedirectMetadata { - public static ApiEndpointMetadata Instance { get; } = new(); + public static DisableCookieRedirectMetadata Instance { get; } = new(); } } diff --git a/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs b/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs index 45b1426a1955..513e376aee81 100644 --- a/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs +++ b/src/Validation/gen/Emitters/ValidationsGenerator.Emitter.cs @@ -72,7 +72,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } {{GeneratedCodeAttribute}} @@ -81,7 +81,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } {{GeneratedCodeAttribute}} @@ -128,15 +137,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -173,6 +184,20 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } """; diff --git a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs index f0f453c7fec0..7544c9d72feb 100644 --- a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs +++ b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs @@ -82,6 +82,8 @@ internal bool TryExtractValidatableType(ITypeSymbol incomingTypeSymbol, WellKnow visitedTypes.Add(typeSymbol); + var hasValidationAttributes = HasValidationAttributes(typeSymbol, wellKnownTypes); + // Extract validatable types discovered in base types of this type and add them to the top-level list. var current = typeSymbol.BaseType; var hasValidatableBaseType = false; @@ -107,7 +109,7 @@ internal bool TryExtractValidatableType(ITypeSymbol incomingTypeSymbol, WellKnow } // No validatable members or derived types found, so we don't need to add this type. - if (members.IsDefaultOrEmpty && !hasValidatableBaseType && !hasValidatableDerivedTypes) + if (members.IsDefaultOrEmpty && !hasValidationAttributes && !hasValidatableBaseType && !hasValidatableDerivedTypes) { return false; } @@ -283,4 +285,20 @@ internal static ImmutableArray ExtractValidationAttributes( NamedArguments: attribute.NamedArguments.ToDictionary(namedArgument => namedArgument.Key, namedArgument => namedArgument.Value.ToCSharpString()), IsCustomValidationAttribute: SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_CustomValidationAttribute))))]; } + + internal static bool HasValidationAttributes(ISymbol symbol, WellKnownTypes wellKnownTypes) + { + var validationAttributeSymbol = wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_ValidationAttribute); + + foreach (var attribute in symbol.GetAttributes()) + { + if (attribute.AttributeClass is not null && + attribute.AttributeClass.ImplementsValidationAttribute(validationAttributeSymbol)) + { + return true; + } + } + + return false; + } } diff --git a/src/Validation/src/PublicAPI.Unshipped.txt b/src/Validation/src/PublicAPI.Unshipped.txt index e2e20423b5ea..e67bfe44dd6d 100644 --- a/src/Validation/src/PublicAPI.Unshipped.txt +++ b/src/Validation/src/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +abstract Microsoft.Extensions.Validation.ValidatableTypeInfo.GetValidationAttributes() -> System.ComponentModel.DataAnnotations.ValidationAttribute![]! Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions Microsoft.Extensions.Validation.IValidatableInfo Microsoft.Extensions.Validation.IValidatableInfo.ValidateAsync(object? value, Microsoft.Extensions.Validation.ValidateContext! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! diff --git a/src/Validation/src/ValidatableTypeInfo.cs b/src/Validation/src/ValidatableTypeInfo.cs index 8852f674a7e0..e409778f1f1d 100644 --- a/src/Validation/src/ValidatableTypeInfo.cs +++ b/src/Validation/src/ValidatableTypeInfo.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.Validation; public abstract class ValidatableTypeInfo : IValidatableInfo { private readonly int _membersCount; - private readonly List _subTypes; + private readonly List _superTypes; /// /// Creates a new instance of . @@ -28,9 +28,15 @@ protected ValidatableTypeInfo( Type = type; Members = members; _membersCount = members.Count; - _subTypes = type.GetAllImplementedTypes(); + _superTypes = type.GetAllImplementedTypes(); } + /// + /// Gets the validation attributes for this member. + /// + /// An array of validation attributes to apply to this member. + protected abstract ValidationAttribute[] GetValidationAttributes(); + /// /// The type being validated. /// @@ -59,75 +65,139 @@ public virtual async Task ValidateAsync(object? value, ValidateContext context, } var originalPrefix = context.CurrentValidationPath; + var originalErrorCount = context.ValidationErrors?.Count ?? 0; try { + // First validate direct members + await ValidateMembersAsync(value, context, cancellationToken); + var actualType = value.GetType(); - // First validate members - for (var i = 0; i < _membersCount; i++) + // Then validate inherited members + foreach (var superTypeInfo in GetSuperTypeInfos(actualType, context)) + { + await superTypeInfo.ValidateMembersAsync(value, context, cancellationToken); + } + + // If any property-level validation errors were found, return early + if (context.ValidationErrors is not null && context.ValidationErrors.Count > originalErrorCount) + { + return; + } + + // Validate type-level attributes + ValidateTypeAttributes(value, context); + + // If any type-level attribute errors were found, return early + if (context.ValidationErrors is not null && context.ValidationErrors.Count > originalErrorCount) + { + return; + } + + // Finally validate IValidatableObject if implemented + ValidateValidatableObjectInterface(value, context); + } + finally + { + context.CurrentValidationPath = originalPrefix; + } + } + + private async Task ValidateMembersAsync(object? value, ValidateContext context, CancellationToken cancellationToken) + { + var originalPrefix = context.CurrentValidationPath; + + for (var i = 0; i < _membersCount; i++) + { + try { await Members[i].ValidateAsync(value, context, cancellationToken); + + } + finally + { context.CurrentValidationPath = originalPrefix; } + } + } + + private void ValidateTypeAttributes(object? value, ValidateContext context) + { + var validationAttributes = GetValidationAttributes(); + var errorPrefix = context.CurrentValidationPath; - // Then validate sub-types if any - foreach (var subType in _subTypes) + for (var i = 0; i < validationAttributes.Length; i++) + { + var attribute = validationAttributes[i]; + var result = attribute.GetValidationResult(value, context.ValidationContext); + if (result is not null && result != ValidationResult.Success && result.ErrorMessage is not null) { - // Check if the actual type is assignable to the sub-type - // and validate it if it is - if (subType.IsAssignableFrom(actualType)) + // Create a validation error for each member name that is provided + foreach (var memberName in result.MemberNames) { - if (context.ValidationOptions.TryGetValidatableTypeInfo(subType, out var subTypeInfo)) - { - await subTypeInfo.ValidateAsync(value, context, cancellationToken); - context.CurrentValidationPath = originalPrefix; - } + var key = string.IsNullOrEmpty(errorPrefix) ? memberName : $"{errorPrefix}.{memberName}"; + context.AddOrExtendValidationError(memberName, key, result.ErrorMessage, value); + } + + if (!result.MemberNames.Any()) + { + // If no member names are specified, then treat this as a top-level error + context.AddOrExtendValidationError(string.Empty, errorPrefix, result.ErrorMessage, value); } } + } + } - // Finally validate IValidatableObject if implemented - if (Type.ImplementsInterface(typeof(IValidatableObject)) && value is IValidatableObject validatable) + private void ValidateValidatableObjectInterface(object? value, ValidateContext context) + { + if (Type.ImplementsInterface(typeof(IValidatableObject)) && value is IValidatableObject validatable) + { + // Important: Set the DisplayName to the type name for top-level validations + // and restore the original validation context properties + var originalDisplayName = context.ValidationContext.DisplayName; + var originalMemberName = context.ValidationContext.MemberName; + var errorPrefix = context.CurrentValidationPath; + + // Set the display name to the class name for IValidatableObject validation + context.ValidationContext.DisplayName = Type.Name; + context.ValidationContext.MemberName = null; + + var validationResults = validatable.Validate(context.ValidationContext); + foreach (var validationResult in validationResults) { - // Important: Set the DisplayName to the type name for top-level validations - // and restore the original validation context properties - var originalDisplayName = context.ValidationContext.DisplayName; - var originalMemberName = context.ValidationContext.MemberName; - - // Set the display name to the class name for IValidatableObject validation - context.ValidationContext.DisplayName = Type.Name; - context.ValidationContext.MemberName = null; - - var validationResults = validatable.Validate(context.ValidationContext); - foreach (var validationResult in validationResults) + if (validationResult != ValidationResult.Success && validationResult.ErrorMessage is not null) { - if (validationResult != ValidationResult.Success && validationResult.ErrorMessage is not null) + // Create a validation error for each member name that is provided + foreach (var memberName in validationResult.MemberNames) { - // Create a validation error for each member name that is provided - foreach (var memberName in validationResult.MemberNames) - { - var key = string.IsNullOrEmpty(originalPrefix) ? - memberName : - $"{originalPrefix}.{memberName}"; - context.AddOrExtendValidationError(memberName, key, validationResult.ErrorMessage, value); - } - - if (!validationResult.MemberNames.Any()) - { - // If no member names are specified, then treat this as a top-level error - context.AddOrExtendValidationError(string.Empty, string.Empty, validationResult.ErrorMessage, value); - } + var key = string.IsNullOrEmpty(errorPrefix) ? memberName : $"{errorPrefix}.{memberName}"; + context.AddOrExtendValidationError(memberName, key, validationResult.ErrorMessage, value); } - } - // Restore the original validation context properties - context.ValidationContext.DisplayName = originalDisplayName; - context.ValidationContext.MemberName = originalMemberName; + if (!validationResult.MemberNames.Any()) + { + // If no member names are specified, then treat this as a top-level error + context.AddOrExtendValidationError(string.Empty, string.Empty, validationResult.ErrorMessage, value); + } + } } + + // Restore the original validation context properties + context.ValidationContext.DisplayName = originalDisplayName; + context.ValidationContext.MemberName = originalMemberName; } - finally + } + + private IEnumerable GetSuperTypeInfos(Type actualType, ValidateContext context) + { + foreach (var superType in _superTypes.Where(t => t.IsAssignableFrom(actualType))) { - context.CurrentValidationPath = originalPrefix; + if (context.ValidationOptions.TryGetValidatableTypeInfo(superType, out var found) + && found is ValidatableTypeInfo superTypeInfo) + { + yield return superTypeInfo; + } } } } diff --git a/src/Validation/startvscode.sh b/src/Validation/startvscode.sh old mode 100644 new mode 100755 diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ClassAttributes.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ClassAttributes.cs new file mode 100644 index 000000000000..ad2c5ab14b6f --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ClassAttributes.cs @@ -0,0 +1,170 @@ +#pragma warning disable ASP0029 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + +// 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.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Validation; + +namespace Microsoft.Extensions.Validation.GeneratorTests; + +public partial class ValidationsGeneratorTests : ValidationsGeneratorTestBase +{ + [Fact] + public async Task CanValidateValidationAttributesOnClasses() + { + var source = """ +#pragma warning disable ASP0029 + +using System; +using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Validation; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(); + +builder.Services.AddValidation(); + +var app = builder.Build(); + +app.Run(); + +[ValidatableType] +[SumLimit] +public class ComplexType : IPoint +{ + [Range(0, 15)] + public int X { get; set; } = 10; + + [Range(0, 15)] + public int Y { get; set; } = 10; + + public NestedType ObjectProperty { get; set; } = new NestedType(); +} + +// This class does not have any property-level validation attributes, but it has a class-level validation attribute. +// Therefore, its type info should still be emitted in the generator output. +[SumLimit] +public class NestedType : IPoint +{ + public int X { get; set; } = 10; + + public int Y { get; set; } = 10; +} + +public interface IPoint +{ + int X { get; } + int Y { get; } +} + +public class SumLimitAttribute : ValidationAttribute +{ + protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) + { + if (value is IPoint point) + { + if (point.X + point.Y > 20) + { + return new ValidationResult($"Sum is too high"); + } + } + return ValidationResult.Success; + } +} +"""; + await Verify(source, out var compilation); + await VerifyValidatableType(compilation, "ComplexType", async (validationOptions, type) => + { + Assert.True(validationOptions.TryGetValidatableTypeInfo(type, out var validatableTypeInfo)); + + await InvalidPropertyAttributeCheck_ProducesError_AndShortCircuits(validatableTypeInfo); + await ValidClassAttributeCheck_DoesNotProduceError(validatableTypeInfo); + await InvalidClassAttributeCheck_ProducesError(validatableTypeInfo); + await InvalidNestedClassAttributeCheck_ProducesError_AndShortCircuits(validatableTypeInfo); + + async Task InvalidPropertyAttributeCheck_ProducesError_AndShortCircuits(IValidatableInfo validatableInfo) + { + var instance = Activator.CreateInstance(type); + type.GetProperty("X")?.SetValue(instance, 16); + type.GetProperty("Y")?.SetValue(instance, 0); + + var context = new ValidateContext + { + ValidationOptions = validationOptions, + ValidationContext = new ValidationContext(instance) + }; + + await validatableTypeInfo.ValidateAsync(instance, context, CancellationToken.None); + + Assert.NotNull(context.ValidationErrors); + var propertyAttributeError = Assert.Single(context.ValidationErrors); + Assert.Equal("X", propertyAttributeError.Key); + Assert.Equal("The field X must be between 0 and 15.", propertyAttributeError.Value.Single()); + } + + async Task ValidClassAttributeCheck_DoesNotProduceError(IValidatableInfo validatableInfo) + { + var instance = Activator.CreateInstance(type); + + var context = new ValidateContext + { + ValidationOptions = validationOptions, + ValidationContext = new ValidationContext(instance) + }; + + await validatableTypeInfo.ValidateAsync(instance, context, CancellationToken.None); + + Assert.Null(context.ValidationErrors); + } + + async Task InvalidClassAttributeCheck_ProducesError(IValidatableInfo validatableInfo) + { + var instance = Activator.CreateInstance(type); + type.GetProperty("X")?.SetValue(instance, 11); + type.GetProperty("Y")?.SetValue(instance, 12); + + var context = new ValidateContext + { + ValidationOptions = validationOptions, + ValidationContext = new ValidationContext(instance) + }; + + await validatableTypeInfo.ValidateAsync(instance, context, CancellationToken.None); + + Assert.NotNull(context.ValidationErrors); + var classAttributeError = Assert.Single(context.ValidationErrors); + Assert.Equal(string.Empty, classAttributeError.Key); + Assert.Equal("Sum is too high", classAttributeError.Value.Single()); + } + + async Task InvalidNestedClassAttributeCheck_ProducesError_AndShortCircuits(IValidatableInfo validatableInfo) + { + var instance = Activator.CreateInstance(type); + var objectPropertyInstance = type.GetProperty("ObjectProperty").GetValue(instance); + objectPropertyInstance.GetType().GetProperty("X")?.SetValue(objectPropertyInstance, 11); + objectPropertyInstance.GetType().GetProperty("Y")?.SetValue(objectPropertyInstance, 12); + + var context = new ValidateContext + { + ValidationOptions = validationOptions, + ValidationContext = new ValidationContext(instance) + }; + + await validatableTypeInfo.ValidateAsync(instance, context, CancellationToken.None); + + Assert.NotNull(context.ValidationErrors); + var classAttributeError = Assert.Single(context.ValidationErrors); + Assert.Equal("ObjectProperty", classAttributeError.Key); + Assert.Equal("Sum is too high", classAttributeError.Value.Single()); + } + }); + } +} diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.IValidatableObject.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.IValidatableObject.cs index 590195468298..ea4c7c48fb63 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.IValidatableObject.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.IValidatableObject.cs @@ -104,11 +104,10 @@ public class TestService await Verify(source, out var compilation); await VerifyEndpoint(compilation, "/validatable-object", async (endpoint, serviceProvider) => { - await ValidateMethodCalledIfPropertyValidationsFail(); - await ValidateForSubtypeInvokedFirst(); + await ValidateMethodNotCalledIfPropertyValidationsFail(); await ValidateForTopLevelInvoked(); - async Task ValidateMethodCalledIfPropertyValidationsFail() + async Task ValidateMethodNotCalledIfPropertyValidationsFail() { var httpContext = CreateHttpContextWithPayload(""" { @@ -136,46 +135,6 @@ async Task ValidateMethodCalledIfPropertyValidationsFail() { Assert.Equal("SubType.RequiredProperty", error.Key); Assert.Equal("The RequiredProperty field is required.", error.Value.Single()); - }, - error => - { - Assert.Equal("SubType.Value3", error.Key); - Assert.Equal("The field ValidatableSubType must be 'some-value'.", error.Value.Single()); - }, - error => - { - Assert.Equal("Value1", error.Key); - Assert.Equal("The field Value1 must be between 10 and 100.", error.Value.Single()); - }); - } - - async Task ValidateForSubtypeInvokedFirst() - { - var httpContext = CreateHttpContextWithPayload(""" - { - "Value1": 5, - "Value2": "test@test.com", - "SubType": { - "Value3": "foo", - "RequiredProperty": "some-value-2", - "StringWithLength": "element" - } - } - """, serviceProvider); - - await endpoint.RequestDelegate(httpContext); - - var problemDetails = await AssertBadRequest(httpContext); - Assert.Collection(problemDetails.Errors, - error => - { - Assert.Equal("SubType.Value3", error.Key); - Assert.Equal("The field ValidatableSubType must be 'some-value'.", error.Value.Single()); - }, - error => - { - Assert.Equal("Value1", error.Key); - Assert.Equal("The field Value1 must be between 10 and 100.", error.Value.Single()); }); } diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.Polymorphism.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.Polymorphism.cs index 39dbec97a78c..206908d1a059 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.Polymorphism.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.Polymorphism.cs @@ -129,11 +129,6 @@ await VerifyEndpoint(compilation, "/validatable-polymorphism", async (endpoint, { Assert.Equal("Value3", error.Key); Assert.Equal("The Value3 field is not a valid e-mail address.", error.Value.Single()); - }, - error => - { - Assert.Equal("Value1", error.Key); - Assert.Equal("The field Value 1 must be between 10 and 100.", error.Value.Single()); }); httpContext = CreateHttpContextWithPayload(""" diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanDiscoverGeneratedValidatableTypeAttribute#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanDiscoverGeneratedValidatableTypeAttribute#ValidatableInfoResolver.g.verified.cs index 226fc69f7d3a..528d05df6fae 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanDiscoverGeneratedValidatableTypeAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanDiscoverGeneratedValidatableTypeAttribute#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -121,15 +130,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -166,5 +177,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanUseBothFrameworkAndGeneratedValidatableTypeAttributes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanUseBothFrameworkAndGeneratedValidatableTypeAttributes#ValidatableInfoResolver.g.verified.cs index 20a3fe742bd2..7d5887992ec3 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanUseBothFrameworkAndGeneratedValidatableTypeAttributes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanUseBothFrameworkAndGeneratedValidatableTypeAttributes#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -142,15 +151,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -187,5 +198,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs index bba18fd6b92f..9043384c0644 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateClassTypesWithAttribute#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -193,15 +202,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -238,5 +249,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs index 538c884403fe..dc941b30b736 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypes#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -199,15 +208,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -244,5 +255,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs index 496844084845..c6957d8a67ec 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -130,15 +139,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -175,5 +186,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs index 34303dad9370..7c3d76702b7c 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateIValidatableObject#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -150,15 +159,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -195,5 +206,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs index b77ed0ac109d..358791ffd09d 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateMultipleNamespaces#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -130,15 +139,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -175,5 +186,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs index 94c9338a1ab0..81e8cc0ba5a2 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateParameters#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -130,15 +139,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -175,5 +186,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs index 0d90e4a481aa..96f7a763268f 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidatePolymorphicTypes#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -180,15 +189,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -225,5 +236,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs index 786e16b5bca4..e308777967aa 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -226,15 +235,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -271,5 +282,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs index bba18fd6b92f..9043384c0644 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypesWithAttribute#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -193,15 +202,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -238,5 +249,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs index b267e4669007..45888276cc09 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecursiveTypes#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -121,15 +130,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -166,5 +177,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs index 18c30835da75..4664a3bb4b43 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -163,15 +172,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -208,5 +219,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateValidationAttributesOnClasses#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateValidationAttributesOnClasses#ValidatableInfoResolver.g.verified.cs new file mode 100644 index 000000000000..932d48d62b1f --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateValidationAttributesOnClasses#ValidatableInfoResolver.g.verified.cs @@ -0,0 +1,209 @@ +//HintName: ValidatableInfoResolver.g.cs +#nullable enable annotations +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ +#nullable enable +#pragma warning disable ASP0029 + +namespace System.Runtime.CompilerServices +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + file sealed class InterceptsLocationAttribute : System.Attribute + { + public InterceptsLocationAttribute(int version, string data) + { + } + } +} + +namespace Microsoft.Extensions.Validation.Generated +{ + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatablePropertyInfo : global::Microsoft.Extensions.Validation.ValidatablePropertyInfo + { + public GeneratedValidatablePropertyInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + global::System.Type propertyType, + string name, + string displayName) : base(containingType, propertyType, name, displayName) + { + ContainingType = containingType; + Name = name; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + internal global::System.Type ContainingType { get; } + internal string Name { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file sealed class GeneratedValidatableTypeInfo : global::Microsoft.Extensions.Validation.ValidatableTypeInfo + { + public GeneratedValidatableTypeInfo( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type, + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file class GeneratedValidatableInfoResolver : global::Microsoft.Extensions.Validation.IValidatableInfoResolver + { + public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + if (type == typeof(global::NestedType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::NestedType), + members: [] + ); + return true; + } + if (type == typeof(global::ComplexType)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexType), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "X", + displayName: "X" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(int), + name: "Y", + displayName: "Y" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexType), + propertyType: typeof(global::NestedType), + name: "ObjectProperty", + displayName: "ObjectProperty" + ), + ] + ); + return true; + } + + return false; + } + + // No-ops, rely on runtime code for ParameterInfo-based resolution + public bool TryGetValidatableParameterInfo(global::System.Reflection.ParameterInfo parameterInfo, [global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out global::Microsoft.Extensions.Validation.IValidatableInfo? validatableInfo) + { + validatableInfo = null; + return false; + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class GeneratedServiceCollectionExtensions + { + [InterceptsLocation] + public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddValidation(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::System.Action? configureOptions = null) + { + // Use non-extension method to avoid infinite recursion. + return global::Microsoft.Extensions.DependencyInjection.ValidationServiceCollectionExtensions.AddValidation(services, options => + { + options.Resolvers.Insert(0, new GeneratedValidatableInfoResolver()); + if (configureOptions is not null) + { + configureOptions(options); + } + }); + } + } + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] + file static class ValidationAttributeCache + { + private sealed record CacheKey( + [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type ContainingType, + string PropertyName); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + global::System.Type containingType, + string propertyName) + { + var key = new CacheKey(containingType, propertyName); + return _propertyCache.GetOrAdd(key, static k => + { + var results = new global::System.Collections.Generic.List(); + + // Get attributes from the property + var property = k.ContainingType.GetProperty(k.PropertyName); + if (property != null) + { + var propertyAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(property, inherit: true); + + results.AddRange(propertyAttributes); + } + + // Check constructors for parameters that match the property name + // to handle record scenarios + foreach (var constructor in k.ContainingType.GetConstructors()) + { + // Look for parameter with matching name (case insensitive) + var parameter = global::System.Linq.Enumerable.FirstOrDefault( + constructor.GetParameters(), + p => string.Equals(p.Name, k.PropertyName, global::System.StringComparison.OrdinalIgnoreCase)); + + if (parameter != null) + { + var paramAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(parameter, inherit: true); + + results.AddRange(paramAttributes); + + break; + } + } + + return results.ToArray(); + }); + } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } + } +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs index 9db7cb497042..d596a97173f9 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmitForExemptTypes#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -115,15 +124,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -160,5 +171,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnClassProperties#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnClassProperties#ValidatableInfoResolver.g.verified.cs index d8135a12e14c..ef96d04824e1 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnClassProperties#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnClassProperties#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -172,15 +181,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -217,5 +228,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnEndpointParameters#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnEndpointParameters#ValidatableInfoResolver.g.verified.cs index 9db7cb497042..d596a97173f9 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnEndpointParameters#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnEndpointParameters#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -115,15 +124,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -160,5 +171,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnRecordProperties#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnRecordProperties#ValidatableInfoResolver.g.verified.cs index 5fff86fac3f1..6012dcb9cac9 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnRecordProperties#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.DoesNotEmit_ForSkipValidationAttribute_OnRecordProperties#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -130,15 +139,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -175,5 +186,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.SkipsClassesWithNonAccessibleTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.SkipsClassesWithNonAccessibleTypes#ValidatableInfoResolver.g.verified.cs index 0ce09882da8d..5fbf16937735 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.SkipsClassesWithNonAccessibleTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.SkipsClassesWithNonAccessibleTypes#ValidatableInfoResolver.g.verified.cs @@ -44,7 +44,7 @@ public GeneratedValidatablePropertyInfo( internal string Name { get; } protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() - => ValidationAttributeCache.GetValidationAttributes(ContainingType, Name); + => ValidationAttributeCache.GetPropertyValidationAttributes(ContainingType, Name); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -53,7 +53,16 @@ public GeneratedValidatablePropertyInfo( public GeneratedValidatableTypeInfo( [param: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] global::System.Type type, - ValidatablePropertyInfo[] members) : base(type, members) { } + ValidatablePropertyInfo[] members) : base(type, members) + { + Type = type; + } + + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + internal global::System.Type Type { get; } + + protected override global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes() + => ValidationAttributeCache.GetTypeValidationAttributes(Type); } [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Validation.ValidationsGenerator, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] @@ -115,15 +124,17 @@ private sealed record CacheKey( [property: global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type ContainingType, string PropertyName); - private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _cache = new(); + private static readonly global::System.Collections.Concurrent.ConcurrentDictionary _propertyCache = new(); + private static readonly global::System.Lazy> _lazyTypeCache = new (() => new ()); + private static global::System.Collections.Concurrent.ConcurrentDictionary TypeCache => _lazyTypeCache.Value; - public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetValidationAttributes( + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetPropertyValidationAttributes( [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] global::System.Type containingType, string propertyName) { var key = new CacheKey(containingType, propertyName); - return _cache.GetOrAdd(key, static k => + return _propertyCache.GetOrAdd(key, static k => { var results = new global::System.Collections.Generic.List(); @@ -160,5 +171,19 @@ private sealed record CacheKey( return results.ToArray(); }); } + + + public static global::System.ComponentModel.DataAnnotations.ValidationAttribute[] GetTypeValidationAttributes( + [global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.Interfaces)] + global::System.Type type + ) + { + return TypeCache.GetOrAdd(type, static t => + { + var typeAttributes = global::System.Reflection.CustomAttributeExtensions + .GetCustomAttributes(t, inherit: true); + return global::System.Linq.Enumerable.ToArray(typeAttributes); + }); + } } } \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.Tests/TestValidatableTypeInfo.cs b/src/Validation/test/Microsoft.Extensions.Validation.Tests/TestValidatableTypeInfo.cs new file mode 100644 index 000000000000..09c6a8c8d5cf --- /dev/null +++ b/src/Validation/test/Microsoft.Extensions.Validation.Tests/TestValidatableTypeInfo.cs @@ -0,0 +1,18 @@ +#pragma warning disable ASP0029 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel.DataAnnotations; + +namespace Microsoft.Extensions.Validation.Tests; + +internal class TestValidatableTypeInfo( + Type type, + ValidatablePropertyInfo[] members, + ValidationAttribute[]? attributes = default) : ValidatableTypeInfo(type, members) +{ + private readonly ValidationAttribute[] _attributes = attributes ?? []; + + protected override ValidationAttribute[] GetValidationAttributes() => _attributes; +} \ No newline at end of file diff --git a/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableInfoResolverTests.cs b/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableInfoResolverTests.cs index 0a745a62a209..5e19a079a7bb 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableInfoResolverTests.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableInfoResolverTests.cs @@ -214,10 +214,4 @@ public TestValidatableParameterInfo( protected override ValidationAttribute[] GetValidationAttributes() => _validationAttributes; } - - private class TestValidatableTypeInfo( - Type type, - ValidatablePropertyInfo[] members) : ValidatableTypeInfo(type, members) - { - } } diff --git a/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableParameterInfoTests.cs b/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableParameterInfoTests.cs index 3ede11071d3d..0bf7530fecc5 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableParameterInfoTests.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableParameterInfoTests.cs @@ -348,12 +348,6 @@ public TestValidatablePropertyInfo( protected override ValidationAttribute[] GetValidationAttributes() => _validationAttributes; } - private class TestValidatableTypeInfo( - Type type, - ValidatablePropertyInfo[] members) : ValidatableTypeInfo(type, members) - { - } - private class TestValidationOptions : ValidationOptions { public TestValidationOptions(Dictionary typeInfoMappings) diff --git a/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableTypeInfoTests.cs b/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableTypeInfoTests.cs index 002ebb1582b0..dd761996442a 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableTypeInfoTests.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.Tests/ValidatableTypeInfoTests.cs @@ -586,6 +586,68 @@ public async Task Validate_IValidatableObject_WithZeroAndMultipleMemberNames_Beh }); } + // The expected order of validation is: + // 1. Attributes on properties + // 2. Attributes on the type + // 3. IValidatableObject implementation + // If any of these steps report an error, the later steps are skipped. + [Fact] + public async Task Validate_IValidatableObject_WithPropertyErrors_ShortCircuitsProperly() + { + var testTypeInfo = new TestValidatableTypeInfo( + typeof(PropertyAndTypeLevelErrorObject), + [ + CreatePropertyInfo(typeof(PropertyAndTypeLevelErrorObject), typeof(int), "Value", "Value", + [new RangeAttribute(0, int.MaxValue) { ErrorMessage = "Property attribute error" }]) + ], + [ + new CustomValidationAttribute() + ]); + + // First case: + var testTypeInstance = new PropertyAndTypeLevelErrorObject { Value = 15 }; + + var context = new ValidateContext + { + ValidationOptions = new TestValidationOptions(new Dictionary + { + { typeof(PropertyAndTypeLevelErrorObject), testTypeInfo } + }), + ValidationContext = new ValidationContext(testTypeInstance) + }; + + await testTypeInfo.ValidateAsync(testTypeInstance, context, default); + + Assert.NotNull(context.ValidationErrors); + var interfaceError = Assert.Single(context.ValidationErrors); + Assert.Equal(string.Empty, interfaceError.Key); + Assert.Equal("IValidatableObject error", interfaceError.Value.Single()); + + // Second case: + testTypeInstance.Value = 5; + context.ValidationErrors = []; + context.ValidationContext = new ValidationContext(testTypeInstance); + + await testTypeInfo.ValidateAsync(testTypeInstance, context, default); + + Assert.NotNull(context.ValidationErrors); + var classAttributeError = Assert.Single(context.ValidationErrors); + Assert.Equal(string.Empty, classAttributeError.Key); + Assert.Equal("Class attribute error", classAttributeError.Value.Single()); + + // Third case: + testTypeInstance.Value = -5; + context.ValidationErrors = []; + context.ValidationContext = new ValidationContext(testTypeInstance); + + await testTypeInfo.ValidateAsync(testTypeInstance, context, default); + + Assert.NotNull(context.ValidationErrors); + var propertyAttributeError = Assert.Single(context.ValidationErrors); + Assert.Equal("Value", propertyAttributeError.Key); + Assert.Equal("Property attribute error", propertyAttributeError.Value.Single()); + } + // Returns no member names to validate https://github.com/dotnet/aspnetcore/issues/61739 private class GlobalErrorObject : IValidatableObject { @@ -618,6 +680,36 @@ public IEnumerable Validate(ValidationContext validationContex } } + [CustomValidation] + private class PropertyAndTypeLevelErrorObject : IValidatableObject + { + [Range(0, int.MaxValue, ErrorMessage = "Property attribute error")] + public int Value { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (Value < 20) + { + yield return new ValidationResult($"IValidatableObject error"); + } + } + } + + private class CustomValidationAttribute : ValidationAttribute + { + protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) + { + if (value is PropertyAndTypeLevelErrorObject instance) + { + if (instance.Value < 10) + { + return new ValidationResult($"Class attribute error"); + } + } + return ValidationResult.Success; + } + } + private ValidatablePropertyInfo CreatePropertyInfo( Type containingType, Type propertyType, @@ -781,16 +873,6 @@ public TestValidatablePropertyInfo( protected override ValidationAttribute[] GetValidationAttributes() => _validationAttributes; } - private class TestValidatableTypeInfo : ValidatableTypeInfo - { - public TestValidatableTypeInfo( - Type type, - ValidatablePropertyInfo[] members) - : base(type, members) - { - } - } - private class TestValidationOptions : ValidationOptions { public TestValidationOptions(Dictionary typeInfoMappings)