diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMismatchedParameterOptionality.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMismatchedParameterOptionality.cs index 8ccde050a525..3f51a55756db 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMismatchedParameterOptionality.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMismatchedParameterOptionality.cs @@ -9,9 +9,9 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; -public partial class DelegateEndpointAnalyzer : DiagnosticAnalyzer +public partial class RouteHandlerAnalyzer : DiagnosticAnalyzer { internal const string DetectMismatchedParameterOptionalityRuleId = "ASP0006"; diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMisplacedLambdaAttribute.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMisplacedLambdaAttribute.cs index cb5d66041056..f4754c9a00d9 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMisplacedLambdaAttribute.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DetectMisplacedLambdaAttribute.cs @@ -7,9 +7,9 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; -public partial class DelegateEndpointAnalyzer : DiagnosticAnalyzer +public partial class RouteHandlerAnalyzer : DiagnosticAnalyzer { private static void DetectMisplacedLambdaAttribute( in OperationAnalysisContext context, diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs index 4cb94d5f26db..00ae39c892e1 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs @@ -3,23 +3,23 @@ using Microsoft.CodeAnalysis; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers { [System.Diagnostics.CodeAnalysis.SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008:Enable analyzer release tracking")] internal static class DiagnosticDescriptors { - internal static readonly DiagnosticDescriptor DoNotUseModelBindingAttributesOnDelegateEndpointParameters = new( + internal static readonly DiagnosticDescriptor DoNotUseModelBindingAttributesOnRouteHandlerParameters = new( "ASP0003", - "Do not use model binding attributes with Map handlers", + "Do not use model binding attributes with route handlers", "{0} should not be specified for a {1} Delegate parameter", "Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://aka.ms/aspnet/analyzers"); - internal static readonly DiagnosticDescriptor DoNotReturnActionResultsFromMapActions = new( + internal static readonly DiagnosticDescriptor DoNotReturnActionResultsFromRouteHandlers = new( "ASP0004", - "Do not use action results with Map actions", + "Do not use action results with route handlers", "IActionResult instances should not be returned from a {0} Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", "Usage", DiagnosticSeverity.Warning, @@ -28,8 +28,8 @@ internal static class DiagnosticDescriptors internal static readonly DiagnosticDescriptor DetectMisplacedLambdaAttribute = new( "ASP0005", - "Do not place attribute on route handlers", - "'{0}' should be placed on the endpoint delegate to be effective", + "Do not place attribute on method called by route handler lambda", + "'{0}' should be placed directly on the route handler lambda to be effective", "Usage", DiagnosticSeverity.Warning, isEnabledByDefault: true, diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowMvcBindArgumentsOnParameters.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowMvcBindArgumentsOnParameters.cs index 19636859c22b..cbf2fc1aa25f 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowMvcBindArgumentsOnParameters.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowMvcBindArgumentsOnParameters.cs @@ -6,9 +6,9 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; -public partial class DelegateEndpointAnalyzer : DiagnosticAnalyzer +public partial class RouteHandlerAnalyzer : DiagnosticAnalyzer { private static void DisallowMvcBindArgumentsOnParameters( in OperationAnalysisContext context, @@ -33,7 +33,7 @@ private static void DisallowMvcBindArgumentsOnParameters( var methodName = invocation.TargetMethod.Name; context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.DoNotUseModelBindingAttributesOnDelegateEndpointParameters, + DiagnosticDescriptors.DoNotUseModelBindingAttributesOnRouteHandlerParameters, location, modelBindingAttribute.AttributeClass.Name, methodName)); diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowReturningActionResultFromMapMethods.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowReturningActionResultFromMapMethods.cs index 1bcc02648907..12e208ea1824 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowReturningActionResultFromMapMethods.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DisallowReturningActionResultFromMapMethods.cs @@ -6,9 +6,9 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; -public partial class DelegateEndpointAnalyzer : DiagnosticAnalyzer +public partial class RouteHandlerAnalyzer : DiagnosticAnalyzer { private static void DisallowReturningActionResultFromMapMethods( in OperationAnalysisContext context, @@ -40,7 +40,7 @@ private static void DisallowReturningActionResultFromMapMethods( { // if we don't have a method body, and the action is IResult or ActionResult returning, produce diagnostics for the entire method. context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, + DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, invocationOperation.Arguments[2].Syntax.GetLocation(), invocationOperation.TargetMethod.Name)); return; @@ -74,7 +74,7 @@ private static void DisallowReturningActionResultFromMapMethods( if (wellKnownTypes.IActionResult.IsAssignableFrom(type)) { context.ReportDiagnostic(Diagnostic.Create( - DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, + DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, returnOperation.Syntax.GetLocation(), invocationOperation.TargetMethod.Name)); } diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DelegateEndpointAnalyzer.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlerAnalyzer.cs similarity index 86% rename from src/Framework/AspNetCoreAnalyzers/src/Analyzers/DelegateEndpointAnalyzer.cs rename to src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlerAnalyzer.cs index 927429ac5728..350dcab2f877 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DelegateEndpointAnalyzer.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/RouteHandlerAnalyzer.cs @@ -9,15 +9,15 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; [DiagnosticAnalyzer(LanguageNames.CSharp)] -public partial class DelegateEndpointAnalyzer : DiagnosticAnalyzer +public partial class RouteHandlerAnalyzer : DiagnosticAnalyzer { public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(new[] { - DiagnosticDescriptors.DoNotUseModelBindingAttributesOnDelegateEndpointParameters, - DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, + DiagnosticDescriptors.DoNotUseModelBindingAttributesOnRouteHandlerParameters, + DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, DiagnosticDescriptors.DetectMisplacedLambdaAttribute, DiagnosticDescriptors.DetectMismatchedParameterOptionality }); @@ -40,7 +40,7 @@ public override void Initialize(AnalysisContext context) { var invocation = (IInvocationOperation)operationAnalysisContext.Operation; var targetMethod = invocation.TargetMethod; - if (IsDelegateHandlerInvocation(wellKnownTypes, invocation, targetMethod)) + if (!IsRouteHandlerInvocation(wellKnownTypes, invocation, targetMethod)) { return; } @@ -109,13 +109,15 @@ public override void Initialize(AnalysisContext context) }); } - private static bool IsDelegateHandlerInvocation( + private static bool IsRouteHandlerInvocation( WellKnownTypes wellKnownTypes, IInvocationOperation invocation, IMethodSymbol targetMethod) { - return !targetMethod.Name.StartsWith("Map", StringComparison.Ordinal) || - !SymbolEqualityComparer.Default.Equals(wellKnownTypes.DelegateEndpointRouteBuilderExtensions, targetMethod.ContainingType) || - invocation.Arguments.Length != 3; + return targetMethod.Name.StartsWith("Map", StringComparison.Ordinal) && + SymbolEqualityComparer.Default.Equals(wellKnownTypes.EndpointRouteBuilderExtensions, targetMethod.ContainingType) && + invocation.Arguments.Length == 3 && + targetMethod.Parameters.Length == 3 && + SymbolEqualityComparer.Default.Equals(wellKnownTypes.Delegate, targetMethod.Parameters[2].Type); } } diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WellKnownTypes.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WellKnownTypes.cs index 040e511ad666..51b98592053b 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WellKnownTypes.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WellKnownTypes.cs @@ -4,15 +4,21 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; internal sealed class WellKnownTypes { public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out WellKnownTypes? wellKnownTypes) { wellKnownTypes = default; - const string DelegateEndpointRouteBuilderExtensions = "Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions"; - if (compilation.GetTypeByMetadataName(DelegateEndpointRouteBuilderExtensions) is not { } delegateEndpointRouteBuilderExtensions) + const string EndpointRouteBuilderExtensions = "Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions"; + if (compilation.GetTypeByMetadataName(EndpointRouteBuilderExtensions) is not { } endpointRouteBuilderExtensions) + { + return false; + } + + const string Delegate = "System.Delegate"; + if (compilation.GetTypeByMetadataName(Delegate) is not { } @delegate) { return false; } @@ -51,7 +57,8 @@ public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out We wellKnownTypes = new WellKnownTypes { - DelegateEndpointRouteBuilderExtensions = delegateEndpointRouteBuilderExtensions, + EndpointRouteBuilderExtensions = endpointRouteBuilderExtensions, + Delegate = @delegate, IBinderTypeProviderMetadata = ibinderTypeProviderMetadata, BindAttribute = bindAttribute, IResult = iResult, @@ -62,7 +69,8 @@ public static bool TryCreate(Compilation compilation, [NotNullWhen(true)] out We return true; } - public ITypeSymbol DelegateEndpointRouteBuilderExtensions { get; private init; } + public INamedTypeSymbol EndpointRouteBuilderExtensions { get; private init; } + public INamedTypeSymbol Delegate { get; private init; } public INamedTypeSymbol IBinderTypeProviderMetadata { get; private init; } public INamedTypeSymbol BindAttribute { get; private init; } public INamedTypeSymbol IResult { get; private init; } diff --git a/src/Framework/AspNetCoreAnalyzers/src/CodeFixes/DetectMismatchedParameterOptionalityFixer.cs b/src/Framework/AspNetCoreAnalyzers/src/CodeFixes/DetectMismatchedParameterOptionalityFixer.cs index 0b4f97ec0fe2..e893fb7147a6 100644 --- a/src/Framework/AspNetCoreAnalyzers/src/CodeFixes/DetectMismatchedParameterOptionalityFixer.cs +++ b/src/Framework/AspNetCoreAnalyzers/src/CodeFixes/DetectMismatchedParameterOptionalityFixer.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Collections.Immutable; using System.Threading.Tasks; -using Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +using Microsoft.AspNetCore.Analyzers.RouteHandlers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -13,7 +13,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editing; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints.Fixers; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers.Fixers; public class DetectMismatchedParameterOptionalityFixer : CodeFixProvider { diff --git a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DetectMismatchedParameterOptionalityTest.cs b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DetectMismatchedParameterOptionalityTest.cs similarity index 96% rename from src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DetectMismatchedParameterOptionalityTest.cs rename to src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DetectMismatchedParameterOptionalityTest.cs index baa4841bb0cb..cdcb1c9bb2e5 100644 --- a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DetectMismatchedParameterOptionalityTest.cs +++ b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DetectMismatchedParameterOptionalityTest.cs @@ -3,11 +3,11 @@ using Microsoft.CodeAnalysis.Testing; using Xunit; -using VerifyCS = Microsoft.AspNetCore.Analyzers.DelegateEndpoints.CSharpDelegateEndpointsCodeFixVerifier< - Microsoft.AspNetCore.Analyzers.DelegateEndpoints.DelegateEndpointAnalyzer, - Microsoft.AspNetCore.Analyzers.DelegateEndpoints.Fixers.DetectMismatchedParameterOptionalityFixer>; +using VerifyCS = Microsoft.AspNetCore.Analyzers.RouteHandlers.CSharpRouteHandlerCodeFixVerifier< + Microsoft.AspNetCore.Analyzers.RouteHandlers.RouteHandlerAnalyzer, + Microsoft.AspNetCore.Analyzers.RouteHandlers.Fixers.DetectMismatchedParameterOptionalityFixer>; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; public partial class DetectMismatchedParameterOptionalityTest { @@ -377,7 +377,7 @@ public async Task MatchingRequiredOptionality_WithDisabledNullability() public void RouteTokenizer_Works_ForSimpleRouteTemplates(string template, string[] expectedNames, string[] expectedQualifiers) { // Arrange - var tokenizer = new DelegateEndpointAnalyzer.RouteTokenEnumerator(template); + var tokenizer = new RouteHandlerAnalyzer.RouteTokenEnumerator(template); var actualNames = new List(); var actualQualifiers = new List(); diff --git a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DetectMisplacedLambdaAttributeTest.cs b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DetectMisplacedLambdaAttributeTest.cs similarity index 88% rename from src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DetectMisplacedLambdaAttributeTest.cs rename to src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DetectMisplacedLambdaAttributeTest.cs index c25bfbcbadb4..a83fe6aadaf8 100644 --- a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DetectMisplacedLambdaAttributeTest.cs +++ b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DetectMisplacedLambdaAttributeTest.cs @@ -5,11 +5,11 @@ using Microsoft.AspNetCore.Analyzer.Testing; using Xunit; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; public partial class DetectMisplacedLambdaAttributeTest { - private TestDiagnosticAnalyzerRunner Runner { get; } = new(new DelegateEndpointAnalyzer()); + private TestDiagnosticAnalyzerRunner Runner { get; } = new(new RouteHandlerAnalyzer()); [Fact] public async Task MinimalAction_WithCorrectlyPlacedAttribute_Works() @@ -48,7 +48,7 @@ void Hello() { } var diagnostic = Assert.Single(diagnostics); Assert.Same(DiagnosticDescriptors.DetectMisplacedLambdaAttribute, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); - Assert.Equal("'AuthorizeAttribute' should be placed on the endpoint delegate to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); + Assert.Equal("'AuthorizeAttribute' should be placed directly on the route handler lambda to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); } [Fact] @@ -70,7 +70,7 @@ public async Task MinimalAction_WithMisplacedAttributeAndBlockSyntax_ProducesDia var diagnostic = Assert.Single(diagnostics); Assert.Same(DiagnosticDescriptors.DetectMisplacedLambdaAttribute, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); - Assert.Equal("'AuthorizeAttribute' should be placed on the endpoint delegate to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); + Assert.Equal("'AuthorizeAttribute' should be placed directly on the route handler lambda to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); } [Fact] @@ -95,12 +95,12 @@ void Hello() { } diagnostic => { Assert.Same(DiagnosticDescriptors.DetectMisplacedLambdaAttribute, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); - Assert.Equal("'AuthorizeAttribute' should be placed on the endpoint delegate to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); + Assert.Equal("'AuthorizeAttribute' should be placed directly on the route handler lambda to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); }, diagnostic => { Assert.Same(DiagnosticDescriptors.DetectMisplacedLambdaAttribute, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); - Assert.Equal("'ProducesAttribute' should be placed on the endpoint delegate to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); + Assert.Equal("'ProducesAttribute' should be placed directly on the route handler lambda to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); } ); } @@ -126,7 +126,7 @@ void Hello() { } diagnostic => { Assert.Same(DiagnosticDescriptors.DetectMisplacedLambdaAttribute, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); - Assert.Equal("'ProducesAttribute' should be placed on the endpoint delegate to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); + Assert.Equal("'ProducesAttribute' should be placed directly on the route handler lambda to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); } ); } @@ -215,7 +215,7 @@ public static void Hello() { } var diagnostic = Assert.Single(diagnostics); Assert.Same(DiagnosticDescriptors.DetectMisplacedLambdaAttribute, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); - Assert.Equal("'AuthorizeAttribute' should be placed on the endpoint delegate to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); + Assert.Equal("'AuthorizeAttribute' should be placed directly on the route handler lambda to be effective", diagnostic.GetMessage(CultureInfo.InvariantCulture)); } [Fact] diff --git a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DisallowMvcBindArgumentsOnParametersTest.cs b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DisallowMvcBindArgumentsOnParametersTest.cs similarity index 93% rename from src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DisallowMvcBindArgumentsOnParametersTest.cs rename to src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DisallowMvcBindArgumentsOnParametersTest.cs index 2f3afa75cf1b..65b3733d0ab9 100644 --- a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DisallowMvcBindArgumentsOnParametersTest.cs +++ b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DisallowMvcBindArgumentsOnParametersTest.cs @@ -5,11 +5,11 @@ using Microsoft.AspNetCore.Analyzer.Testing; using Xunit; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; public partial class DisallowMvcBindArgumentsOnParametersTest { - private TestDiagnosticAnalyzerRunner Runner { get; } = new(new DelegateEndpointAnalyzer()); + private TestDiagnosticAnalyzerRunner Runner { get; } = new(new RouteHandlerAnalyzer()); [Fact] public async Task MinimalAction_WithoutBindAttributes_Works() @@ -59,7 +59,7 @@ public async Task MinimalAction_Lambda_WithBindAttributes_ProducesDiagnostics() // Assert var diagnostic = Assert.Single(diagnostics); - Assert.Same(DiagnosticDescriptors.DoNotUseModelBindingAttributesOnDelegateEndpointParameters, diagnostic.Descriptor); + Assert.Same(DiagnosticDescriptors.DoNotUseModelBindingAttributesOnRouteHandlerParameters, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); Assert.Equal("BindAttribute should not be specified for a MapGet Delegate parameter", diagnostic.GetMessage(CultureInfo.InvariantCulture)); } @@ -81,7 +81,7 @@ static void PostWithBind(/*MM*/[ModelBinder] string name) {} // Assert var diagnostic = Assert.Single(diagnostics); - Assert.Same(DiagnosticDescriptors.DoNotUseModelBindingAttributesOnDelegateEndpointParameters, diagnostic.Descriptor); + Assert.Same(DiagnosticDescriptors.DoNotUseModelBindingAttributesOnRouteHandlerParameters, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); Assert.Equal("ModelBinderAttribute should not be specified for a MapPost Delegate parameter", diagnostic.GetMessage(CultureInfo.InvariantCulture)); } diff --git a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DisallowReturningActionResultsFromMapMethodsTest.cs b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DisallowReturningActionResultsFromMapMethodsTest.cs similarity index 96% rename from src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DisallowReturningActionResultsFromMapMethodsTest.cs rename to src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DisallowReturningActionResultsFromMapMethodsTest.cs index ba1700e5e55c..82571b70a12b 100644 --- a/src/Framework/AspNetCoreAnalyzers/test/MinimalActions/DisallowReturningActionResultsFromMapMethodsTest.cs +++ b/src/Framework/AspNetCoreAnalyzers/test/RouteHandlers/DisallowReturningActionResultsFromMapMethodsTest.cs @@ -3,11 +3,11 @@ using Microsoft.AspNetCore.Analyzer.Testing; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; public partial class DisallowReturningActionResultsFromMapMethodsTest { - private TestDiagnosticAnalyzerRunner Runner { get; } = new(new DelegateEndpointAnalyzer()); + private TestDiagnosticAnalyzerRunner Runner { get; } = new(new RouteHandlerAnalyzer()); [Fact] public async Task MinimalAction_ReturningIResult_Works() @@ -126,7 +126,7 @@ public async Task MinimalAction_ReturningActionResult_ProducesDiagnostics() // Assert var diagnostic = Assert.Single(diagnostics); - Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, diagnostic.Descriptor); + Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); Assert.Equal("IActionResult instances should not be returned from a MapGet Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage()); } @@ -149,7 +149,7 @@ public async Task MinimalAction_ReturningActionResultConditionally_ProducesDiagn // Assert var diagnostic = Assert.Single(diagnostics); - Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, diagnostic.Descriptor); + Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); Assert.Equal("IActionResult instances should not be returned from a MapGet Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage()); } @@ -177,7 +177,7 @@ static object OkObjectResultReturningMethod() // Assert var diagnostic = Assert.Single(diagnostics); - Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, diagnostic.Descriptor); + Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); Assert.Equal("IActionResult instances should not be returned from a MapPost Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage()); } @@ -209,7 +209,7 @@ public record Person(string Name); // Assert var diagnostic = Assert.Single(diagnostics); - Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, diagnostic.Descriptor); + Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); Assert.Equal("IActionResult instances should not be returned from a MapPost Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage()); } @@ -247,7 +247,7 @@ public record Person(string Name); // Assert var diagnostic = Assert.Single(diagnostics); - Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromMapActions, diagnostic.Descriptor); + Assert.Same(DiagnosticDescriptors.DoNotReturnActionResultsFromRouteHandlers, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); Assert.Equal("IActionResult instances should not be returned from a MapPost Delegate parameter. Consider returning an equivalent result from Microsoft.AspNetCore.Http.Results.", diagnostic.GetMessage()); } diff --git a/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpDelegateEndpointsAnalyzerVerifier.cs b/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpRouteHandlerAnalyzerVerifier.cs similarity index 77% rename from src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpDelegateEndpointsAnalyzerVerifier.cs rename to src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpRouteHandlerAnalyzerVerifier.cs index 2fa14f67bccb..e8dc7a55cb61 100644 --- a/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpDelegateEndpointsAnalyzerVerifier.cs +++ b/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpRouteHandlerAnalyzerVerifier.cs @@ -11,13 +11,13 @@ using Microsoft.CodeAnalysis.Testing.Verifiers; using Xunit; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; -public static class CSharpDelegateEndpointsAnalyzerVerifier - where TAnalyzer : DelegateEndpointAnalyzer, new() +public static class CSharpRouteHandlerAnalyzerVerifier + where TAnalyzer : RouteHandlerAnalyzer, new() { public static DiagnosticResult Diagnostic(string diagnosticId = null) - => CSharpDelegateEndpointsAnalyzerVerifier.Diagnostic(diagnosticId); + => CSharpRouteHandlerAnalyzerVerifier.Diagnostic(diagnosticId); public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) => new DiagnosticResult(descriptor); @@ -30,4 +30,4 @@ public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] } public class Test : CSharpCodeFixTest { } -} \ No newline at end of file +} diff --git a/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpDelegateEndpointsCodeFixVerifier.cs b/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpRouteHandlerCodeFixVerifier.cs similarity index 85% rename from src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpDelegateEndpointsCodeFixVerifier.cs rename to src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpRouteHandlerCodeFixVerifier.cs index 2883bb2fd389..9d4db29d17fd 100644 --- a/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpDelegateEndpointsCodeFixVerifier.cs +++ b/src/Framework/AspNetCoreAnalyzers/test/Verifiers/CSharpRouteHandlerCodeFixVerifier.cs @@ -3,7 +3,7 @@ using System.Collections.Immutable; using System.Globalization; -using Microsoft.AspNetCore.Analyzers.DelegateEndpoints.Fixers; +using Microsoft.AspNetCore.Analyzers.RouteHandlers.Fixers; using Microsoft.AspNetCore.Analyzer.Testing; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Testing; @@ -13,10 +13,10 @@ using Microsoft.CodeAnalysis.Testing.Verifiers; using Xunit; -namespace Microsoft.AspNetCore.Analyzers.DelegateEndpoints; +namespace Microsoft.AspNetCore.Analyzers.RouteHandlers; -public static class CSharpDelegateEndpointsCodeFixVerifier - where TAnalyzer : DelegateEndpointAnalyzer, new() +public static class CSharpRouteHandlerCodeFixVerifier + where TAnalyzer : RouteHandlerAnalyzer, new() where TCodeFix : DetectMismatchedParameterOptionalityFixer, new() { public static DiagnosticResult Diagnostic(string diagnosticId = null) @@ -27,7 +27,7 @@ public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) { - var test = new CSharpDelegateEndpointsAnalyzerVerifier.Test { TestCode = source }; + var test = new CSharpRouteHandlerAnalyzerVerifier.Test { TestCode = source }; test.ExpectedDiagnostics.AddRange(expected); return test.RunAsync(); } @@ -43,7 +43,7 @@ public static Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected public static Task VerifyCodeFixAsync(string sources, DiagnosticResult[] expected, string fixedSources, string usageSource = "") { - var test = new DelegateEndpointAnalyzerTest + var test = new RouteHandlerAnalyzerTest { TestState = { @@ -63,16 +63,16 @@ public static Task VerifyCodeFixAsync(string sources, DiagnosticResult[] expecte return test.RunAsync(); } - public class DelegateEndpointAnalyzerTest : CSharpCodeFixTest + public class RouteHandlerAnalyzerTest : CSharpCodeFixTest { - public DelegateEndpointAnalyzerTest() + public RouteHandlerAnalyzerTest() { // We populate the ReferenceAssemblies used in the tests with the locally-built AspNetCore // assemblies that are referenced in a minimal app to ensure that there are no reference // errors during the build. ReferenceAssemblies = ReferenceAssemblies.Net.Net60.AddAssemblies(ImmutableArray.Create( TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.WebApplication).Assembly.Location), - TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions).Assembly.Location), + TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.IApplicationBuilder).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.AspNetCore.Builder.IEndpointConventionBuilder).Assembly.Location), TrimAssemblyExtension(typeof(Microsoft.Extensions.Hosting.IHost).Assembly.Location), @@ -82,4 +82,4 @@ public DelegateEndpointAnalyzerTest() string TrimAssemblyExtension(string fullPath) => fullPath.Replace(".dll", string.Empty); } } -} \ No newline at end of file +} diff --git a/src/Http/Routing/src/Builder/DelegateEndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/DelegateEndpointRouteBuilderExtensions.cs deleted file mode 100644 index 23d815c183cc..000000000000 --- a/src/Http/Routing/src/Builder/DelegateEndpointRouteBuilderExtensions.cs +++ /dev/null @@ -1,348 +0,0 @@ -// 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.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Metadata; -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Routing.Patterns; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Microsoft.AspNetCore.Builder -{ - /// - /// Provides extension methods for to define HTTP API endpoints. - /// - public static class DelegateEndpointRouteBuilderExtensions - { - // Avoid creating a new array every call - private static readonly string[] GetVerb = new[] { "GET" }; - private static readonly string[] PostVerb = new[] { "POST" }; - private static readonly string[] PutVerb = new[] { "PUT" }; - private static readonly string[] DeleteVerb = new[] { "DELETE" }; - - /// - /// Adds a to the that matches HTTP GET requests - /// for the specified pattern. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder MapGet( - this IEndpointRouteBuilder endpoints, - string pattern, - Delegate handler) - { - return MapMethods(endpoints, pattern, GetVerb, handler); - } - - /// - /// Adds a to the that matches HTTP POST requests - /// for the specified pattern. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder MapPost( - this IEndpointRouteBuilder endpoints, - string pattern, - Delegate handler) - { - return MapMethods(endpoints, pattern, PostVerb, handler); - } - - /// - /// Adds a to the that matches HTTP PUT requests - /// for the specified pattern. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder MapPut( - this IEndpointRouteBuilder endpoints, - string pattern, - Delegate handler) - { - return MapMethods(endpoints, pattern, PutVerb, handler); - } - - /// - /// Adds a to the that matches HTTP DELETE requests - /// for the specified pattern. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder MapDelete( - this IEndpointRouteBuilder endpoints, - string pattern, - Delegate handler) - { - return MapMethods(endpoints, pattern, DeleteVerb, handler); - } - - private static DelegateEndpointConventionBuilder Map( - this IEndpointRouteBuilder endpoints, - RoutePattern pattern, - Delegate handler, - bool DisableInferBodyFromParameters) - { - if (endpoints is null) - { - throw new ArgumentNullException(nameof(endpoints)); - } - - if (pattern is null) - { - throw new ArgumentNullException(nameof(pattern)); - } - - if (handler is null) - { - throw new ArgumentNullException(nameof(handler)); - } - - const int defaultOrder = 0; - - var routeParams = new List(pattern.Parameters.Count); - foreach (var part in pattern.Parameters) - { - routeParams.Add(part.Name); - } - - var routeHandlerOptions = endpoints.ServiceProvider?.GetService>(); - - var options = new RequestDelegateFactoryOptions - { - ServiceProvider = endpoints.ServiceProvider, - RouteParameterNames = routeParams, - ThrowOnBadRequest = routeHandlerOptions?.Value.ThrowOnBadRequest ?? false, - DisableInferBodyFromParameters = DisableInferBodyFromParameters, - }; - - var requestDelegateResult = RequestDelegateFactory.Create(handler, options); - - var builder = new RouteEndpointBuilder( - requestDelegateResult.RequestDelegate, - pattern, - defaultOrder) - { - DisplayName = pattern.RawText ?? pattern.DebuggerToString(), - }; - - // REVIEW: Should we add an IActionMethodMetadata with just MethodInfo on it so we are - // explicit about the MethodInfo representing the "handler" and not the RequestDelegate? - - // Add MethodInfo as metadata to assist with OpenAPI generation for the endpoint. - builder.Metadata.Add(handler.Method); - - // Methods defined in a top-level program are generated as statics so the delegate - // target will be null. Inline lambdas are compiler generated method so they can - // be filtered that way. - if (GeneratedNameParser.TryParseLocalFunctionName(handler.Method.Name, out var endpointName) - || !TypeHelper.IsCompilerGeneratedMethod(handler.Method)) - { - endpointName ??= handler.Method.Name; - builder.DisplayName = $"{builder.DisplayName} => {endpointName}"; - } - - // Add delegate attributes as metadata - var attributes = handler.Method.GetCustomAttributes(); - - // Add add request delegate metadata - foreach (var metadata in requestDelegateResult.EndpointMetadata) - { - builder.Metadata.Add(metadata); - } - - // This can be null if the delegate is a dynamic method or compiled from an expression tree - if (attributes is not null) - { - foreach (var attribute in attributes) - { - builder.Metadata.Add(attribute); - } - } - - var dataSource = endpoints.DataSources.OfType().FirstOrDefault(); - if (dataSource is null) - { - dataSource = new ModelEndpointDataSource(); - endpoints.DataSources.Add(dataSource); - } - - return new DelegateEndpointConventionBuilder(dataSource.AddEndpointBuilder(builder)); - } - - /// - /// Adds a to the that matches HTTP requests - /// for the specified HTTP methods and pattern. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// HTTP methods that the endpoint will match. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder MapMethods( - this IEndpointRouteBuilder endpoints, - string pattern, - IEnumerable httpMethods, - Delegate handler) - { - if (httpMethods is null) - { - throw new ArgumentNullException(nameof(httpMethods)); - } - - var disableInferredBody = false; - foreach (var method in httpMethods) - { - disableInferredBody = ShouldDisableInferredBody(method); - if (disableInferredBody is true) - { - break; - } - } - - var builder = endpoints.Map(RoutePatternFactory.Parse(pattern), handler, disableInferredBody); - // Prepends the HTTP method to the DisplayName produced with pattern + method name - builder.Add(b => b.DisplayName = $"HTTP: {string.Join(", ", httpMethods)} {b.DisplayName}"); - builder.WithMetadata(new HttpMethodMetadata(httpMethods)); - return builder; - - static bool ShouldDisableInferredBody(string method) - { - // GET, DELETE, HEAD, CONNECT, TRACE, and OPTIONS normally do not contain bodies - return method.Equals(HttpMethods.Get, StringComparison.Ordinal) || - method.Equals(HttpMethods.Delete, StringComparison.Ordinal) || - method.Equals(HttpMethods.Head, StringComparison.Ordinal) || - method.Equals(HttpMethods.Options, StringComparison.Ordinal) || - method.Equals(HttpMethods.Trace, StringComparison.Ordinal) || - method.Equals(HttpMethods.Connect, StringComparison.Ordinal); - } - } - - /// - /// Adds a to the that matches HTTP requests - /// for the specified pattern. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder Map( - this IEndpointRouteBuilder endpoints, - string pattern, - Delegate handler) - { - return Map(endpoints, RoutePatternFactory.Parse(pattern), handler); - } - - /// - /// Adds a to the that matches HTTP requests - /// for the specified pattern. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder Map( - this IEndpointRouteBuilder endpoints, - RoutePattern pattern, - Delegate handler) - { - return Map(endpoints, pattern, handler, DisableInferBodyFromParameters: false); - } - - /// - /// Adds a specialized to the that will match - /// requests for non-file-names with the lowest possible priority. - /// - /// The to add the route to. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - /// - /// - /// is intended to handle cases where URL path of - /// the request does not contain a file name, and no other endpoint has matched. This is convenient for routing - /// requests for dynamic content to a SPA framework, while also allowing requests for non-existent files to - /// result in an HTTP 404. - /// - /// - /// registers an endpoint using the pattern - /// {*path:nonfile}. The order of the registered endpoint will be int.MaxValue. - /// - /// - public static DelegateEndpointConventionBuilder MapFallback(this IEndpointRouteBuilder endpoints, Delegate handler) - { - if (endpoints == null) - { - throw new ArgumentNullException(nameof(endpoints)); - } - - if (handler == null) - { - throw new ArgumentNullException(nameof(handler)); - } - - return endpoints.MapFallback("{*path:nonfile}", handler); - } - - /// - /// Adds a specialized to the that will match - /// the provided pattern with the lowest possible priority. - /// - /// The to add the route to. - /// The route pattern. - /// The delegate executed when the endpoint is matched. - /// A that can be used to further customize the endpoint. - /// - /// - /// is intended to handle cases where no - /// other endpoint has matched. This is convenient for routing requests to a SPA framework. - /// - /// - /// The order of the registered endpoint will be int.MaxValue. - /// - /// - /// This overload will use the provided verbatim. Use the :nonfile route constraint - /// to exclude requests for static files. - /// - /// - public static DelegateEndpointConventionBuilder MapFallback( - this IEndpointRouteBuilder endpoints, - string pattern, - Delegate handler) - { - if (endpoints == null) - { - throw new ArgumentNullException(nameof(endpoints)); - } - - if (pattern == null) - { - throw new ArgumentNullException(nameof(pattern)); - } - - if (handler == null) - { - throw new ArgumentNullException(nameof(handler)); - } - - var conventionBuilder = endpoints.Map(pattern, handler); - conventionBuilder.WithDisplayName("Fallback " + pattern); - conventionBuilder.Add(b => ((RouteEndpointBuilder)b).Order = int.MaxValue); - return conventionBuilder; - } - } -} diff --git a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs index e2e6f43ad77a..468fd4f54d94 100644 --- a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs @@ -1,13 +1,15 @@ // 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.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Builder { @@ -187,5 +189,324 @@ public static IEndpointConventionBuilder Map( return dataSource.AddEndpointBuilder(builder); } + + + /// + /// Adds a to the that matches HTTP GET requests + /// for the specified pattern. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder MapGet( + this IEndpointRouteBuilder endpoints, + string pattern, + Delegate handler) + { + return MapMethods(endpoints, pattern, GetVerb, handler); + } + + /// + /// Adds a to the that matches HTTP POST requests + /// for the specified pattern. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder MapPost( + this IEndpointRouteBuilder endpoints, + string pattern, + Delegate handler) + { + return MapMethods(endpoints, pattern, PostVerb, handler); + } + + /// + /// Adds a to the that matches HTTP PUT requests + /// for the specified pattern. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder MapPut( + this IEndpointRouteBuilder endpoints, + string pattern, + Delegate handler) + { + return MapMethods(endpoints, pattern, PutVerb, handler); + } + + /// + /// Adds a to the that matches HTTP DELETE requests + /// for the specified pattern. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder MapDelete( + this IEndpointRouteBuilder endpoints, + string pattern, + Delegate handler) + { + return MapMethods(endpoints, pattern, DeleteVerb, handler); + } + + /// + /// Adds a to the that matches HTTP requests + /// for the specified HTTP methods and pattern. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// HTTP methods that the endpoint will match. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder MapMethods( + this IEndpointRouteBuilder endpoints, + string pattern, + IEnumerable httpMethods, + Delegate handler) + { + if (httpMethods is null) + { + throw new ArgumentNullException(nameof(httpMethods)); + } + + var disableInferredBody = false; + foreach (var method in httpMethods) + { + disableInferredBody = ShouldDisableInferredBody(method); + if (disableInferredBody is true) + { + break; + } + } + + var builder = endpoints.Map(RoutePatternFactory.Parse(pattern), handler, disableInferredBody); + // Prepends the HTTP method to the DisplayName produced with pattern + method name + builder.Add(b => b.DisplayName = $"HTTP: {string.Join(", ", httpMethods)} {b.DisplayName}"); + builder.WithMetadata(new HttpMethodMetadata(httpMethods)); + return builder; + + static bool ShouldDisableInferredBody(string method) + { + // GET, DELETE, HEAD, CONNECT, TRACE, and OPTIONS normally do not contain bodies + return method.Equals(HttpMethods.Get, StringComparison.Ordinal) || + method.Equals(HttpMethods.Delete, StringComparison.Ordinal) || + method.Equals(HttpMethods.Head, StringComparison.Ordinal) || + method.Equals(HttpMethods.Options, StringComparison.Ordinal) || + method.Equals(HttpMethods.Trace, StringComparison.Ordinal) || + method.Equals(HttpMethods.Connect, StringComparison.Ordinal); + } + } + + /// + /// Adds a to the that matches HTTP requests + /// for the specified pattern. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder Map( + this IEndpointRouteBuilder endpoints, + string pattern, + Delegate handler) + { + return Map(endpoints, RoutePatternFactory.Parse(pattern), handler); + } + + /// + /// Adds a to the that matches HTTP requests + /// for the specified pattern. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder Map( + this IEndpointRouteBuilder endpoints, + RoutePattern pattern, + Delegate handler) + { + return Map(endpoints, pattern, handler, disableInferBodyFromParameters: false); + } + + /// + /// Adds a specialized to the that will match + /// requests for non-file-names with the lowest possible priority. + /// + /// The to add the route to. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + /// + /// + /// is intended to handle cases where URL path of + /// the request does not contain a file name, and no other endpoint has matched. This is convenient for routing + /// requests for dynamic content to a SPA framework, while also allowing requests for non-existent files to + /// result in an HTTP 404. + /// + /// + /// registers an endpoint using the pattern + /// {*path:nonfile}. The order of the registered endpoint will be int.MaxValue. + /// + /// + public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoints, Delegate handler) + { + if (endpoints == null) + { + throw new ArgumentNullException(nameof(endpoints)); + } + + if (handler == null) + { + throw new ArgumentNullException(nameof(handler)); + } + + return endpoints.MapFallback("{*path:nonfile}", handler); + } + + /// + /// Adds a specialized to the that will match + /// the provided pattern with the lowest possible priority. + /// + /// The to add the route to. + /// The route pattern. + /// The delegate executed when the endpoint is matched. + /// A that can be used to further customize the endpoint. + /// + /// + /// is intended to handle cases where no + /// other endpoint has matched. This is convenient for routing requests to a SPA framework. + /// + /// + /// The order of the registered endpoint will be int.MaxValue. + /// + /// + /// This overload will use the provided verbatim. Use the :nonfile route constraint + /// to exclude requests for static files. + /// + /// + public static RouteHandlerBuilder MapFallback( + this IEndpointRouteBuilder endpoints, + string pattern, + Delegate handler) + { + if (endpoints == null) + { + throw new ArgumentNullException(nameof(endpoints)); + } + + if (pattern == null) + { + throw new ArgumentNullException(nameof(pattern)); + } + + if (handler == null) + { + throw new ArgumentNullException(nameof(handler)); + } + + var conventionBuilder = endpoints.Map(pattern, handler); + conventionBuilder.WithDisplayName("Fallback " + pattern); + conventionBuilder.Add(b => ((RouteEndpointBuilder)b).Order = int.MaxValue); + return conventionBuilder; + } + + private static RouteHandlerBuilder Map( + this IEndpointRouteBuilder endpoints, + RoutePattern pattern, + Delegate handler, + bool disableInferBodyFromParameters) + { + if (endpoints is null) + { + throw new ArgumentNullException(nameof(endpoints)); + } + + if (pattern is null) + { + throw new ArgumentNullException(nameof(pattern)); + } + + if (handler is null) + { + throw new ArgumentNullException(nameof(handler)); + } + + const int defaultOrder = 0; + + var routeParams = new List(pattern.Parameters.Count); + foreach (var part in pattern.Parameters) + { + routeParams.Add(part.Name); + } + + var routeHandlerOptions = endpoints.ServiceProvider?.GetService>(); + + var options = new RequestDelegateFactoryOptions + { + ServiceProvider = endpoints.ServiceProvider, + RouteParameterNames = routeParams, + ThrowOnBadRequest = routeHandlerOptions?.Value.ThrowOnBadRequest ?? false, + DisableInferBodyFromParameters = disableInferBodyFromParameters, + }; + + var requestDelegateResult = RequestDelegateFactory.Create(handler, options); + + var builder = new RouteEndpointBuilder( + requestDelegateResult.RequestDelegate, + pattern, + defaultOrder) + { + DisplayName = pattern.RawText ?? pattern.DebuggerToString(), + }; + + // REVIEW: Should we add an IActionMethodMetadata with just MethodInfo on it so we are + // explicit about the MethodInfo representing the "handler" and not the RequestDelegate? + + // Add MethodInfo as metadata to assist with OpenAPI generation for the endpoint. + builder.Metadata.Add(handler.Method); + + // Methods defined in a top-level program are generated as statics so the delegate + // target will be null. Inline lambdas are compiler generated method so they can + // be filtered that way. + if (GeneratedNameParser.TryParseLocalFunctionName(handler.Method.Name, out var endpointName) + || !TypeHelper.IsCompilerGeneratedMethod(handler.Method)) + { + endpointName ??= handler.Method.Name; + builder.DisplayName = $"{builder.DisplayName} => {endpointName}"; + } + + // Add delegate attributes as metadata + var attributes = handler.Method.GetCustomAttributes(); + + // Add add request delegate metadata + foreach (var metadata in requestDelegateResult.EndpointMetadata) + { + builder.Metadata.Add(metadata); + } + + // This can be null if the delegate is a dynamic method or compiled from an expression tree + if (attributes is not null) + { + foreach (var attribute in attributes) + { + builder.Metadata.Add(attribute); + } + } + + var dataSource = endpoints.DataSources.OfType().FirstOrDefault(); + if (dataSource is null) + { + dataSource = new ModelEndpointDataSource(); + endpoints.DataSources.Add(dataSource); + } + + return new RouteHandlerBuilder(dataSource.AddEndpointBuilder(builder)); + } } } diff --git a/src/Http/Routing/src/Builder/OpenApiDelegateEndpointConventionBuilderExtensions.cs b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs similarity index 71% rename from src/Http/Routing/src/Builder/OpenApiDelegateEndpointConventionBuilderExtensions.cs rename to src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs index c968ce59f72c..34a9d897ad0b 100644 --- a/src/Http/Routing/src/Builder/OpenApiDelegateEndpointConventionBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Http /// Extension methods for adding that is /// meant to be consumed by OpenAPI libraries. /// - public static class OpenApiDelegateEndpointConventionBuilderExtensions + public static class OpenApiRouteHandlerBuilderExtensions { private static readonly ExcludeFromDescriptionAttribute _excludeFromDescriptionMetadataAttribute = new(); @@ -20,9 +20,9 @@ public static class OpenApiDelegateEndpointConventionBuilderExtensions /// Adds the to for all builders /// produced by . /// - /// The . - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder ExcludeFromDescription(this DelegateEndpointConventionBuilder builder) + /// The . + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder ExcludeFromDescription(this RouteHandlerBuilder builder) { builder.WithMetadata(_excludeFromDescriptionMetadataAttribute); @@ -34,13 +34,13 @@ public static DelegateEndpointConventionBuilder ExcludeFromDescription(this Dele /// produced by . /// /// The type of the response. - /// The . - /// The response status code. Defaults to StatusCodes.Status200OK. + /// The . + /// The response status code. Defaults to . /// The response content type. Defaults to "application/json". /// Additional response content types the endpoint produces for the supplied status code. - /// A that can be used to further customize the endpoint. + /// A that can be used to further customize the endpoint. #pragma warning disable RS0026 - public static DelegateEndpointConventionBuilder Produces(this DelegateEndpointConventionBuilder builder, + public static RouteHandlerBuilder Produces(this RouteHandlerBuilder builder, #pragma warning restore RS0026 int statusCode = StatusCodes.Status200OK, string? contentType = null, @@ -53,14 +53,14 @@ public static DelegateEndpointConventionBuilder Produces(this Delegat /// Adds an to for all builders /// produced by . /// - /// The . + /// The . /// The response status code. /// The type of the response. Defaults to null. /// The response content type. Defaults to "application/json" if responseType is not null, otherwise defaults to null. /// Additional response content types the endpoint produces for the supplied status code. - /// A that can be used to further customize the endpoint. + /// A that can be used to further customize the endpoint. #pragma warning disable RS0026 - public static DelegateEndpointConventionBuilder Produces(this DelegateEndpointConventionBuilder builder, + public static RouteHandlerBuilder Produces(this RouteHandlerBuilder builder, #pragma warning restore RS0026 int statusCode, Type? responseType = null, @@ -87,11 +87,11 @@ public static DelegateEndpointConventionBuilder Produces(this DelegateEndpointCo /// Adds an with a type /// to for all builders produced by . /// - /// The . + /// The . /// The response status code. /// The response content type. Defaults to "application/problem+json". - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder ProducesProblem(this DelegateEndpointConventionBuilder builder, + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder ProducesProblem(this RouteHandlerBuilder builder, int statusCode, string? contentType = null) { @@ -107,11 +107,11 @@ public static DelegateEndpointConventionBuilder ProducesProblem(this DelegateEnd /// Adds an with a type /// to for all builders produced by . /// - /// The . - /// The response status code. Defaults to StatusCodes.Status400BadRequest. + /// The . + /// The response status code. Defaults to . /// The response content type. Defaults to "application/problem+json". - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder ProducesValidationProblem(this DelegateEndpointConventionBuilder builder, + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder ProducesValidationProblem(this RouteHandlerBuilder builder, int statusCode = StatusCodes.Status400BadRequest, string? contentType = null) { @@ -132,10 +132,10 @@ public static DelegateEndpointConventionBuilder ProducesValidationProblem(this D /// into related groups. These tags are typically included in the generated specification /// and are typically used to group operations by tags in the UI. /// - /// The . + /// The . /// A collection of tags to be associated with the endpoint. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder WithTags(this DelegateEndpointConventionBuilder builder, params string[] tags) + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder WithTags(this RouteHandlerBuilder builder, params string[] tags) { builder.WithMetadata(new TagsAttribute(tags)); return builder; @@ -146,11 +146,11 @@ public static DelegateEndpointConventionBuilder WithTags(this DelegateEndpointCo /// produced by . /// /// The type of the request body. - /// The . + /// The . /// The request content type that the endpoint accepts. /// The list of additional request content types that the endpoint accepts. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder Accepts(this DelegateEndpointConventionBuilder builder, + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, string contentType, params string[] additionalContentTypes) where TRequest : notnull { Accepts(builder, typeof(TRequest), contentType, additionalContentTypes); @@ -163,12 +163,12 @@ public static DelegateEndpointConventionBuilder Accepts(this DelegateE /// produced by . /// /// The type of the request body. - /// The . + /// The . /// Sets a value that determines if the request body is optional. /// The request content type that the endpoint accepts. /// The list of additional request content types that the endpoint accepts. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder Accepts(this DelegateEndpointConventionBuilder builder, + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, bool isOptional, string contentType, params string[] additionalContentTypes) where TRequest : notnull { Accepts(builder, typeof(TRequest), isOptional, contentType, additionalContentTypes); @@ -180,12 +180,12 @@ public static DelegateEndpointConventionBuilder Accepts(this DelegateE /// Adds to for all builders /// produced by . /// - /// The . + /// The . /// The type of the request body. /// The request content type that the endpoint accepts. /// The list of additional request content types that the endpoint accepts. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder Accepts(this DelegateEndpointConventionBuilder builder, + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, Type requestType, string contentType, params string[] additionalContentTypes) { builder.WithMetadata(new AcceptsMetadata(requestType, false, GetAllContentTypes(contentType, additionalContentTypes))); @@ -197,13 +197,13 @@ public static DelegateEndpointConventionBuilder Accepts(this DelegateEndpointCon /// Adds to for all builders /// produced by . /// - /// The . + /// The . /// The type of the request body. /// Sets a value that determines if the request body is optional. /// The request content type that the endpoint accepts. /// The list of additional request content types that the endpoint accepts. - /// A that can be used to further customize the endpoint. - public static DelegateEndpointConventionBuilder Accepts(this DelegateEndpointConventionBuilder builder, + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, Type requestType, bool isOptional, string contentType, params string[] additionalContentTypes) { builder.WithMetadata(new AcceptsMetadata(requestType, isOptional, GetAllContentTypes(contentType, additionalContentTypes))); diff --git a/src/Http/Routing/src/Builder/DelegateEndpointConventionBuilder.cs b/src/Http/Routing/src/Builder/RouteHandlerBuilder.cs similarity index 77% rename from src/Http/Routing/src/Builder/DelegateEndpointConventionBuilder.cs rename to src/Http/Routing/src/Builder/RouteHandlerBuilder.cs index d9a41767182f..c170e8244f00 100644 --- a/src/Http/Routing/src/Builder/DelegateEndpointConventionBuilder.cs +++ b/src/Http/Routing/src/Builder/RouteHandlerBuilder.cs @@ -9,26 +9,26 @@ namespace Microsoft.AspNetCore.Builder /// /// Builds conventions that will be used for customization of MapAction instances. /// - public sealed class DelegateEndpointConventionBuilder : IEndpointConventionBuilder + public sealed class RouteHandlerBuilder : IEndpointConventionBuilder { private readonly IEnumerable _endpointConventionBuilders; /// - /// Instantiates a new given a single + /// Instantiates a new given a single /// . /// /// The to instantiate with. - internal DelegateEndpointConventionBuilder(IEndpointConventionBuilder endpointConventionBuilder) + internal RouteHandlerBuilder(IEndpointConventionBuilder endpointConventionBuilder) { _endpointConventionBuilders = new List() { endpointConventionBuilder }; } /// - /// Instantiates a new given multiple + /// Instantiates a new given multiple /// instances. /// /// A list of instances. - public DelegateEndpointConventionBuilder(IEnumerable endpointConventionBuilders) + public RouteHandlerBuilder(IEnumerable endpointConventionBuilders) { _endpointConventionBuilders = endpointConventionBuilders; } diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index 03affad7bff7..7ed579ae8db2 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -5,10 +5,10 @@ *REMOVED*Microsoft.AspNetCore.Routing.IRouteNameMetadata.RouteName.get -> string! *REMOVED*Microsoft.AspNetCore.Routing.RouteNameMetadata.RouteName.get -> string! *REMOVED*Microsoft.AspNetCore.Routing.RouteNameMetadata.RouteNameMetadata(string! routeName) -> void -Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder -Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder.DelegateEndpointConventionBuilder(System.Collections.Generic.IEnumerable! endpointConventionBuilders) -> void -Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder.Add(System.Action! convention) -> void -Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions +Microsoft.AspNetCore.Builder.RouteHandlerBuilder +Microsoft.AspNetCore.Builder.RouteHandlerBuilder.Add(System.Action! convention) -> void +Microsoft.AspNetCore.Builder.RouteHandlerBuilder.RouteHandlerBuilder(System.Collections.Generic.IEnumerable! endpointConventionBuilders) -> void +Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions Microsoft.AspNetCore.Routing.DataTokensMetadata.DataTokens.get -> System.Collections.Generic.IReadOnlyDictionary! Microsoft.AspNetCore.Routing.DataTokensMetadata.DataTokensMetadata(System.Collections.Generic.IReadOnlyDictionary! dataTokens) -> void Microsoft.AspNetCore.Routing.IDataTokensMetadata.DataTokens.get -> System.Collections.Generic.IReadOnlyDictionary! @@ -29,15 +29,15 @@ Microsoft.AspNetCore.Routing.EndpointGroupNameAttribute.EndpointGroupName.get -> Microsoft.AspNetCore.Routing.EndpointNameAttribute Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointNameAttribute(string! endpointName) -> void Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointName.get -> string! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, Microsoft.AspNetCore.Routing.Patterns.RoutePattern! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.MapDelete(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.MapGet(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.MapMethods(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Collections.Generic.IEnumerable! httpMethods, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.MapPost(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Builder.DelegateEndpointRouteBuilderExtensions.MapPut(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, Microsoft.AspNetCore.Routing.Patterns.RoutePattern! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.Map(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapDelete(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapFallback(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapGet(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapMethods(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Collections.Generic.IEnumerable! httpMethods, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapPost(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapPut(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! handler) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! static Microsoft.AspNetCore.Builder.RoutingEndpointConventionBuilderExtensions.WithName(this TBuilder builder, string! endpointName) -> TBuilder static Microsoft.AspNetCore.Builder.RoutingEndpointConventionBuilderExtensions.WithGroupName(this TBuilder builder, string! endpointGroupName) -> TBuilder Microsoft.AspNetCore.Routing.IExcludeFromDescriptionMetadata @@ -45,14 +45,13 @@ Microsoft.AspNetCore.Routing.IExcludeFromDescriptionMetadata.ExcludeFromDescript Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute.ExcludeFromDescriptionAttribute() -> void Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute.ExcludeFromDescription.get -> bool -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.WithTags(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, params string![]! tags) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, System.Type! requestType, bool isOptional, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, System.Type! requestType, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, bool isOptional, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.ExcludeFromDescription(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.Produces(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, int statusCode, System.Type? responseType = null, string? contentType = null, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.Produces(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, int statusCode = 200, string? contentType = null, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.ProducesProblem(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, int statusCode, string? contentType = null) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -static Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions.ProducesValidationProblem(this Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! builder, int statusCode = 400, string? contentType = null) -> Microsoft.AspNetCore.Builder.DelegateEndpointConventionBuilder! -Microsoft.AspNetCore.Http.OpenApiDelegateEndpointConventionBuilderExtensions +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Type! requestType, bool isOptional, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, System.Type! requestType, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, bool isOptional, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Accepts(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! contentType, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.ExcludeFromDescription(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Produces(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, int statusCode, System.Type? responseType = null, string? contentType = null, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.Produces(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, int statusCode = 200, string? contentType = null, params string![]! additionalContentTypes) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.ProducesProblem(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, int statusCode, string? contentType = null) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.ProducesValidationProblem(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, int statusCode = 400, string? contentType = null) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithTags(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, params string![]! tags) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! diff --git a/src/Http/Routing/src/RouteHandlerOptions.cs b/src/Http/Routing/src/RouteHandlerOptions.cs index c7297b86fe75..b16f6db6f201 100644 --- a/src/Http/Routing/src/RouteHandlerOptions.cs +++ b/src/Http/Routing/src/RouteHandlerOptions.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Routing { /// - /// Options for controlling the behavior of + /// Options for controlling the behavior of /// and similar methods. /// public sealed class RouteHandlerOptions diff --git a/src/Http/Routing/test/FunctionalTests/DelegateEndpointTest.cs b/src/Http/Routing/test/FunctionalTests/RouteHandlerTest.cs similarity index 98% rename from src/Http/Routing/test/FunctionalTests/DelegateEndpointTest.cs rename to src/Http/Routing/test/FunctionalTests/RouteHandlerTest.cs index a79195788be8..6dc71ddd22da 100644 --- a/src/Http/Routing/test/FunctionalTests/DelegateEndpointTest.cs +++ b/src/Http/Routing/test/FunctionalTests/RouteHandlerTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Routing.FunctionalTests { - public class DelegateEndpointTest + public class RouteHandlerTest { [Fact] public async Task MapPost_FromBodyWorksWithJsonPayload() diff --git a/src/Http/Routing/test/UnitTests/Builder/MapEndpointEndpointDataSourceBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs similarity index 99% rename from src/Http/Routing/test/UnitTests/Builder/MapEndpointEndpointDataSourceBuilderExtensionsTest.cs rename to src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs index 8d713234a316..f9079850eaa7 100644 --- a/src/Http/Routing/test/UnitTests/Builder/MapEndpointEndpointDataSourceBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RequestDelegateEndpointRouteBuilderExtensionsTest.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Builder { - public class MapEndpointEndpointDataSourceBuilderExtensionsTest + public class RequestDelegateEndpointRouteBuilderExtensionsTest { private ModelEndpointDataSource GetBuilderEndpointDataSource(IEndpointRouteBuilder endpointRouteBuilder) { diff --git a/src/Http/Routing/test/UnitTests/Builder/DelegateEndpointRouteBuilderExtensionsTest.cs b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs similarity index 99% rename from src/Http/Routing/test/UnitTests/Builder/DelegateEndpointRouteBuilderExtensionsTest.cs rename to src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs index 0746a5dbf23c..2a5e196fc3c0 100644 --- a/src/Http/Routing/test/UnitTests/Builder/DelegateEndpointRouteBuilderExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/Builder/RouteHandlerEndpointRouteBuilderExtensionsTest.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Builder { - public class DelegateEndpointRouteBuilderExtensionsTest + public class RouteHandlerEndpointRouteBuilderExtensionsTest { private ModelEndpointDataSource GetBuilderEndpointDataSource(IEndpointRouteBuilder endpointRouteBuilder) {