diff --git a/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs b/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs
index 6a6551f..67e9631 100644
--- a/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs
+++ b/src/GeneratedEndpoints/AddEndpointHandlersGenerator.cs
@@ -18,7 +18,7 @@ public static void GenerateSource(SourceProductionContext context, ImmutableSort
context.CancellationToken.ThrowIfCancellationRequested();
var nonStaticClassNames = grouped.Keys
- .Where(x => !x.IsStatic)
+ .Where(x => !x.IsStatic && !x.IsAbstract)
.Select(x => x.Name)
.ToList();
var source = new StringBuilder();
diff --git a/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs b/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs
new file mode 100644
index 0000000..25c413a
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs
@@ -0,0 +1,100 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string AcceptsAttributeName = "AcceptsAttribute";
+ internal const string AcceptsAttributeHint = $"{AcceptsAttributeFullyQualifiedName}.gs.cs";
+
+ private const string AcceptsAttributeFullyQualifiedName = $"{AttributesNamespace}.{AcceptsAttributeName}";
+
+ internal static readonly SourceText AcceptsAttributeSourceText = CreateAcceptsAttributeSourceText();
+
+ private static SourceText CreateAcceptsAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies the request type and content types accepted by the annotated endpoint or class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{AcceptsAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the CLR type of the endpoint filter.
+ ///
+ public global::System.Type Type { get; }
+
+ ///
+ /// Gets the primary content type accepted by the endpoint.
+ ///
+ public string ContentType { get; }
+
+ ///
+ /// Gets the additional content types accepted by the endpoint.
+ ///
+ public string[] AdditionalContentTypes { get; }
+
+ ///
+ /// Gets a value indicating whether the request body is optional.
+ ///
+ public bool IsOptional { get; init; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The CLR type of the request body.
+ /// The primary content type accepted by the endpoint.
+ /// Additional content types accepted by the endpoint.
+ public {{AcceptsAttributeName}}(global::System.Type type, string contentType = "application/json", params string[] additionalContentTypes)
+ {
+ Type = type;
+ ContentType = contentType;
+ AdditionalContentTypes = additionalContentTypes;
+ }
+ }
+
+ ///
+ /// Specifies the request type using a generic argument and the content types accepted by the annotated endpoint or class.
+ ///
+ /// The CLR type of the request body.
+ [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{AcceptsAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the CLR type of the endpoint filter.
+ ///
+ public global::System.Type Type => typeof(TRequest);
+
+ ///
+ /// Gets the primary content type accepted by the endpoint.
+ ///
+ public string ContentType { get; }
+
+ ///
+ /// Gets the additional content types accepted by the endpoint.
+ ///
+ public string[] AdditionalContentTypes { get; }
+
+ ///
+ /// Gets a value indicating whether the request body is optional.
+ ///
+ public bool IsOptional { get; init; }
+
+ ///
+ /// Initializes a new instance of the generic Accepts attribute class.
+ ///
+ /// The primary content type accepted by the endpoint.
+ /// Additional content types accepted by the endpoint.
+ public {{AcceptsAttributeName}}(string contentType = "application/json", params string[] additionalContentTypes)
+ {
+ ContentType = contentType;
+ AdditionalContentTypes = additionalContentTypes;
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs
new file mode 100644
index 0000000..7973b79
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs
@@ -0,0 +1,29 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string DisableAntiforgeryAttributeName = "DisableAntiforgeryAttribute";
+ internal const string DisableAntiforgeryAttributeHint = $"{DisableAntiforgeryAttributeFullyQualifiedName}.gs.cs";
+
+ private const string DisableAntiforgeryAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableAntiforgeryAttributeName}";
+
+ internal static readonly SourceText DisableAntiforgeryAttributeSourceText = CreateDisableAntiforgeryAttributeSourceText();
+
+ private static SourceText CreateDisableAntiforgeryAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Disables antiforgery protection for the annotated endpoint or class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{DisableAntiforgeryAttributeName}} : global::System.Attribute
+ {
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs
new file mode 100644
index 0000000..99225ef
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs
@@ -0,0 +1,29 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string DisableRequestTimeoutAttributeName = "DisableRequestTimeoutAttribute";
+ internal const string DisableRequestTimeoutAttributeHint = $"{DisableRequestTimeoutAttributeFullyQualifiedName}.gs.cs";
+
+ private const string DisableRequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableRequestTimeoutAttributeName}";
+
+ internal static readonly SourceText DisableRequestTimeoutAttributeSourceText = CreateDisableRequestTimeoutAttributeSourceText();
+
+ private static SourceText CreateDisableRequestTimeoutAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Disables the request timeout for the annotated endpoint or class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{DisableRequestTimeoutAttributeName}} : global::System.Attribute
+ {
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs
new file mode 100644
index 0000000..f98194e
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs
@@ -0,0 +1,31 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string DisableValidationAttributeName = "DisableValidationAttribute";
+ internal const string DisableValidationAttributeHint = $"{DisableValidationAttributeFullyQualifiedName}.gs.cs";
+
+ private const string DisableValidationAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableValidationAttributeName}";
+
+ internal static readonly SourceText DisableValidationAttributeSourceText = CreateDisableValidationAttributeSourceText();
+
+ private static SourceText CreateDisableValidationAttributeSourceText() => SourceText.From($$"""
+ #if NET10_0_OR_GREATER
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Disables request validation for the annotated endpoint or class when targeting .NET 10 or later.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{DisableValidationAttributeName}} : global::System.Attribute
+ {
+ }
+ #endif
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs b/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs
new file mode 100644
index 0000000..71fd661
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs
@@ -0,0 +1,55 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string EndpointFilterAttributeName = "EndpointFilterAttribute";
+ internal const string EndpointFilterAttributeHint = $"{EndpointFilterAttributeFullyQualifiedName}.gs.cs";
+
+ private const string EndpointFilterAttributeFullyQualifiedName = $"{AttributesNamespace}.{EndpointFilterAttributeName}";
+
+ internal static readonly SourceText EndpointFilterAttributeSourceText = CreateEndpointFilterAttributeSourceText();
+
+ private static SourceText CreateEndpointFilterAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies an endpoint filter type to apply to the annotated endpoint or class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the CLR type of the endpoint filter.
+ ///
+ public global::System.Type Type { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The CLR type of the endpoint filter.
+ public {{EndpointFilterAttributeName}}(global::System.Type type)
+ {
+ Type = type;
+ }
+ }
+
+ ///
+ /// Specifies an endpoint filter type using a generic argument.
+ ///
+ /// The CLR type of the endpoint filter.
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the CLR type of the endpoint filter.
+ ///
+ public global::System.Type Type => typeof(TFilter);
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs b/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs
index 1b90733..9748a57 100644
--- a/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs
+++ b/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs
@@ -6,19 +6,6 @@ namespace GeneratedEndpoints.Common;
internal static partial class Constants
{
- internal static readonly string FileHeader = $"""
- //-----------------------------------------------------------------------------
- //
- // This code was generated by {nameof(MinimalApiGenerator)} which can be found
- // in the {typeof(MinimalApiGenerator).Namespace} namespace.
- //
- // Changes to this file may cause incorrect behavior
- // and will be lost if the code is regenerated.
- //
- //-----------------------------------------------------------------------------
-
- #nullable enable
- """;
internal static readonly ImmutableArray HttpAttributeDefinitions =
[
@@ -38,632 +25,6 @@ internal static partial class Constants
internal static readonly ImmutableDictionary HttpAttributeDefinitionsByName =
HttpAttributeDefinitions.ToImmutableDictionary(static definition => definition.Name);
- internal static readonly SourceText RequireAuthorizationAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies that authorization is required for the annotated endpoint or class.
- /// Optionally restricts access to the specified authorization policies.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{RequireAuthorizationAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the policy names that the endpoint or class requires.
- ///
- public string[] PolicyNames { get; }
-
- ///
- /// Marks the endpoint or class as requiring authorization.
- ///
- public {{RequireAuthorizationAttributeName}}()
- {
- PolicyNames = [];
- }
-
- ///
- /// Marks the endpoint or class as requiring authorization with one or more policies.
- ///
- public {{RequireAuthorizationAttributeName}}(params string[] policyNames)
- {
- PolicyNames = policyNames ?? [];
- }
- }
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText RequireCorsAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies that the annotated endpoint requires a configured CORS policy.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{RequireCorsAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the optional CORS policy name.
- ///
- public string? PolicyName { get; }
-
- ///
- /// Marks the endpoint or class as requiring the default CORS policy.
- ///
- public {{RequireCorsAttributeName}}()
- {
- }
-
- ///
- /// Marks the endpoint or class as requiring the specified named CORS policy.
- ///
- public {{RequireCorsAttributeName}}(string policyName)
- {
- PolicyName = policyName;
- }
- }
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText RequireRateLimitingAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies that the annotated endpoint requires the provided rate limiting policy.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{RequireRateLimitingAttributeName}} : global::System.Attribute
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The rate limiting policy to apply.
- public {{RequireRateLimitingAttributeName}}(string policyName)
- {
- PolicyName = policyName;
- }
-
- ///
- /// Gets the rate limiting policy name.
- ///
- public string PolicyName { get; }
- }
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText RequireHostAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies the allowed hosts for the annotated endpoint or class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{RequireHostAttributeName}} : global::System.Attribute
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The hosts that are allowed to access the endpoint.
- public {{RequireHostAttributeName}}(params string[] hosts)
- {
- Hosts = hosts ?? [];
- }
-
- ///
- /// Gets the allowed hosts.
- ///
- public string[] Hosts { get; }
- }
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText DisableAntiforgeryAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Disables antiforgery protection for the annotated endpoint or class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{DisableAntiforgeryAttributeName}} : global::System.Attribute
- {
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText ShortCircuitAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Marks the annotated endpoint or class to short-circuit the request pipeline.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{ShortCircuitAttributeName}} : global::System.Attribute
- {
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText DisableRequestTimeoutAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Disables the request timeout for the annotated endpoint or class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{DisableRequestTimeoutAttributeName}} : global::System.Attribute
- {
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText DisableValidationAttributeSourceText = SourceText.From($$"""
- #if NET10_0_OR_GREATER
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Disables request validation for the annotated endpoint or class when targeting .NET 10 or later.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{DisableValidationAttributeName}} : global::System.Attribute
- {
- }
- #endif
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText RequestTimeoutAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Applies the request timeout metadata to the annotated endpoint or class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{RequestTimeoutAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the optional request timeout policy name.
- ///
- public string? PolicyName { get; init; }
-
- ///
- /// Applies the default request timeout behavior.
- ///
- public {{RequestTimeoutAttributeName}}()
- {
- }
-
- ///
- /// Applies the specified request timeout policy.
- ///
- /// The request timeout policy name.
- public {{RequestTimeoutAttributeName}}(string policyName)
- {
- PolicyName = policyName;
- }
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText OrderAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies the order for the annotated endpoint when building conventions.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{OrderAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the order that will be applied to the endpoint.
- ///
- public int Order { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The order value to apply to the endpoint.
- public {{OrderAttributeName}}(int order)
- {
- Order = order;
- }
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText MapGroupAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies the route group for the annotated class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
- internal sealed class {{MapGroupAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the route group pattern.
- ///
- public string Pattern { get; }
-
- ///
- /// Gets or sets the endpoint group name.
- ///
- public string? Name { get; init; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The route group pattern to apply.
- public {{MapGroupAttributeName}}(string pattern)
- {
- Pattern = pattern;
- }
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText SummaryAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies the summary metadata for the annotated endpoint.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
- internal sealed class {{SummaryAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the summary value for the endpoint.
- ///
- public string Summary { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The summary to apply to the endpoint.
- public {{SummaryAttributeName}}(string summary)
- {
- Summary = summary;
- }
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText AcceptsAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies the request type and content types accepted by the annotated endpoint or class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{AcceptsAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the CLR type of the endpoint filter.
- ///
- public global::System.Type Type { get; }
-
- ///
- /// Gets the primary content type accepted by the endpoint.
- ///
- public string ContentType { get; }
-
- ///
- /// Gets the additional content types accepted by the endpoint.
- ///
- public string[] AdditionalContentTypes { get; }
-
- ///
- /// Gets a value indicating whether the request body is optional.
- ///
- public bool IsOptional { get; init; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The CLR type of the request body.
- /// The primary content type accepted by the endpoint.
- /// Additional content types accepted by the endpoint.
- public {{AcceptsAttributeName}}(global::System.Type type, string contentType = "application/json", params string[] additionalContentTypes)
- {
- Type = type;
- ContentType = contentType;
- AdditionalContentTypes = additionalContentTypes;
- }
- }
-
- ///
- /// Specifies the request type using a generic argument and the content types accepted by the annotated endpoint or class.
- ///
- /// The CLR type of the request body.
- [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{AcceptsAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the CLR type of the endpoint filter.
- ///
- public global::System.Type Type => typeof(TRequest);
-
- ///
- /// Gets the primary content type accepted by the endpoint.
- ///
- public string ContentType { get; }
-
- ///
- /// Gets the additional content types accepted by the endpoint.
- ///
- public string[] AdditionalContentTypes { get; }
-
- ///
- /// Gets a value indicating whether the request body is optional.
- ///
- public bool IsOptional { get; init; }
-
- ///
- /// Initializes a new instance of the generic Accepts attribute class.
- ///
- /// The primary content type accepted by the endpoint.
- /// Additional content types accepted by the endpoint.
- public {{AcceptsAttributeName}}(string contentType = "application/json", params string[] additionalContentTypes)
- {
- ContentType = contentType;
- AdditionalContentTypes = additionalContentTypes;
- }
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText EndpointFilterAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies an endpoint filter type to apply to the annotated endpoint or class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the CLR type of the endpoint filter.
- ///
- public global::System.Type Type { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The CLR type of the endpoint filter.
- public {{EndpointFilterAttributeName}}(global::System.Type type)
- {
- Type = type;
- }
- }
-
- ///
- /// Specifies an endpoint filter type using a generic argument.
- ///
- /// The CLR type of the endpoint filter.
- [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{EndpointFilterAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the CLR type of the endpoint filter.
- ///
- public global::System.Type Type => typeof(TFilter);
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText ProducesResponseAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies a response type, status code, and content types produced by the annotated endpoint or class.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the response type produced by the endpoint.
- ///
- public global::System.Type Type { get; }
-
- ///
- /// Gets the HTTP status code returned by the endpoint.
- ///
- public int StatusCode { get; }
-
- ///
- /// Gets the primary content type produced by the endpoint.
- ///
- public string? ContentType { get; }
-
- ///
- /// Gets the additional content types produced by the endpoint.
- ///
- public string[] AdditionalContentTypes { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The CLR type of the response body.
- /// The HTTP status code returned by the endpoint.
- /// The primary content type produced by the endpoint.
- /// Additional content types produced by the endpoint.
- public {{ProducesResponseAttributeName}}(global::System.Type type, int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes)
- {
- Type = type;
- StatusCode = statusCode;
- ContentType = contentType;
- AdditionalContentTypes = additionalContentTypes ?? [];
- }
- }
-
- ///
- /// Specifies a response type using a generic argument along with status code and content types produced by the annotated endpoint or class.
- ///
- /// The CLR type of the response body.
- [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the response type produced by the endpoint.
- ///
- public global::System.Type Type => typeof(TResponse);
-
- ///
- /// Gets the HTTP status code returned by the endpoint.
- ///
- public int StatusCode { get; }
-
- ///
- /// Gets the primary content type produced by the endpoint.
- ///
- public string? ContentType { get; }
-
- ///
- /// Gets the additional content types produced by the endpoint.
- ///
- public string[] AdditionalContentTypes { get; }
-
- ///
- /// Initializes a new instance of the generic Produces attribute class.
- ///
- /// The HTTP status code returned by the endpoint.
- /// The primary content type produced by the endpoint.
- /// Additional content types produced by the endpoint.
- public {{ProducesResponseAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes)
- {
- StatusCode = statusCode;
- ContentType = contentType;
- AdditionalContentTypes = additionalContentTypes ?? [];
- }
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText ProducesProblemAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies that the endpoint produces a problem details payload.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{ProducesProblemAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the HTTP status code returned by the endpoint.
- ///
- public int StatusCode { get; }
-
- ///
- /// Gets the primary content type produced by the endpoint.
- ///
- public string? ContentType { get; }
-
- ///
- /// Gets the additional content types produced by the endpoint.
- ///
- public string[] AdditionalContentTypes { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The HTTP status code returned by the endpoint.
- /// The primary content type produced by the endpoint.
- /// Additional content types produced by the endpoint.
- public {{ProducesProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status500InternalServerError, string? contentType = null, params string[] additionalContentTypes)
- {
- StatusCode = statusCode;
- ContentType = contentType;
- AdditionalContentTypes = additionalContentTypes ?? [];
- }
- }
-
- """, Encoding.UTF8
- );
-
- internal static readonly SourceText ProducesValidationProblemAttributeSourceText = SourceText.From($$"""
- {{FileHeader}}
-
- namespace {{AttributesNamespace}};
-
- ///
- /// Specifies that the endpoint produces a validation problem details payload.
- ///
- [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
- internal sealed class {{ProducesValidationProblemAttributeName}} : global::System.Attribute
- {
- ///
- /// Gets the HTTP status code returned by the endpoint.
- ///
- public int StatusCode { get; }
-
- ///
- /// Gets the primary content type produced by the endpoint.
- ///
- public string? ContentType { get; }
-
- ///
- /// Gets the additional content types produced by the endpoint.
- ///
- public string[] AdditionalContentTypes { get; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The HTTP status code returned by the endpoint.
- /// The primary content type produced by the endpoint.
- /// Additional content types produced by the endpoint.
- public {{ProducesValidationProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status400BadRequest, string? contentType = null, params string[] additionalContentTypes)
- {
- StatusCode = statusCode;
- ContentType = contentType;
- AdditionalContentTypes = additionalContentTypes ?? [];
- }
- }
-
- """, Encoding.UTF8
- );
-
private static HttpAttributeDefinition CreateHttpAttributeDefinition(string attributeName, string verb, bool allowOptionalPattern = false)
{
var fullyQualifiedName = $"{AttributesNamespace}.{attributeName}";
diff --git a/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs b/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs
new file mode 100644
index 0000000..4b61e0b
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs
@@ -0,0 +1,47 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string MapGroupAttributeName = "MapGroupAttribute";
+ internal const string MapGroupAttributeHint = $"{MapGroupAttributeFullyQualifiedName}.gs.cs";
+
+ private const string MapGroupAttributeFullyQualifiedName = $"{AttributesNamespace}.{MapGroupAttributeName}";
+
+ internal static readonly SourceText MapGroupAttributeSourceText = CreateMapGroupAttributeSourceText();
+
+ private static SourceText CreateMapGroupAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies the route group for the annotated class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{MapGroupAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the route group pattern.
+ ///
+ public string Pattern { get; }
+
+ ///
+ /// Gets or sets the endpoint group name.
+ ///
+ public string? Name { get; init; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The route group pattern to apply.
+ public {{MapGroupAttributeName}}(string pattern)
+ {
+ Pattern = pattern;
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs b/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs
new file mode 100644
index 0000000..a52e2fe
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs
@@ -0,0 +1,42 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string OrderAttributeName = "OrderAttribute";
+ internal const string OrderAttributeHint = $"{OrderAttributeFullyQualifiedName}.gs.cs";
+
+ private const string OrderAttributeFullyQualifiedName = $"{AttributesNamespace}.{OrderAttributeName}";
+
+ internal static readonly SourceText OrderAttributeSourceText = CreateOrderAttributeSourceText();
+
+ private static SourceText CreateOrderAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies the order for the annotated endpoint when building conventions.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{OrderAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the order that will be applied to the endpoint.
+ ///
+ public int Order { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The order value to apply to the endpoint.
+ public {{OrderAttributeName}}(int order)
+ {
+ Order = order;
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs
new file mode 100644
index 0000000..80a6d5f
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs
@@ -0,0 +1,56 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string ProducesProblemAttributeName = "ProducesProblemAttribute";
+ internal const string ProducesProblemAttributeHint = $"{ProducesProblemAttributeFullyQualifiedName}.gs.cs";
+
+ private const string ProducesProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesProblemAttributeName}";
+
+ internal static readonly SourceText ProducesProblemAttributeSourceText = CreateProducesProblemAttributeSourceText();
+
+ private static SourceText CreateProducesProblemAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies that the endpoint produces a problem details payload.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{ProducesProblemAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the HTTP status code returned by the endpoint.
+ ///
+ public int StatusCode { get; }
+
+ ///
+ /// Gets the primary content type produced by the endpoint.
+ ///
+ public string? ContentType { get; }
+
+ ///
+ /// Gets the additional content types produced by the endpoint.
+ ///
+ public string[] AdditionalContentTypes { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The HTTP status code returned by the endpoint.
+ /// The primary content type produced by the endpoint.
+ /// Additional content types produced by the endpoint.
+ public {{ProducesProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status500InternalServerError, string? contentType = null, params string[] additionalContentTypes)
+ {
+ StatusCode = statusCode;
+ ContentType = contentType;
+ AdditionalContentTypes = additionalContentTypes ?? [];
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs
new file mode 100644
index 0000000..a7bee4b
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs
@@ -0,0 +1,104 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string ProducesResponseAttributeName = "ProducesResponseAttribute";
+ internal const string ProducesResponseAttributeHint = $"{ProducesResponseAttributeFullyQualifiedName}.gs.cs";
+
+ private const string ProducesResponseAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesResponseAttributeName}";
+
+ internal static readonly SourceText ProducesResponseAttributeSourceText = CreateProducesResponseAttributeSourceText();
+
+ private static SourceText CreateProducesResponseAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies a response type, status code, and content types produced by the annotated endpoint or class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the response type produced by the endpoint.
+ ///
+ public global::System.Type Type { get; }
+
+ ///
+ /// Gets the HTTP status code returned by the endpoint.
+ ///
+ public int StatusCode { get; }
+
+ ///
+ /// Gets the primary content type produced by the endpoint.
+ ///
+ public string? ContentType { get; }
+
+ ///
+ /// Gets the additional content types produced by the endpoint.
+ ///
+ public string[] AdditionalContentTypes { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The CLR type of the response body.
+ /// The HTTP status code returned by the endpoint.
+ /// The primary content type produced by the endpoint.
+ /// Additional content types produced by the endpoint.
+ public {{ProducesResponseAttributeName}}(global::System.Type type, int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes)
+ {
+ Type = type;
+ StatusCode = statusCode;
+ ContentType = contentType;
+ AdditionalContentTypes = additionalContentTypes ?? [];
+ }
+ }
+
+ ///
+ /// Specifies a response type using a generic argument along with status code and content types produced by the annotated endpoint or class.
+ ///
+ /// The CLR type of the response body.
+ [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{ProducesResponseAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the response type produced by the endpoint.
+ ///
+ public global::System.Type Type => typeof(TResponse);
+
+ ///
+ /// Gets the HTTP status code returned by the endpoint.
+ ///
+ public int StatusCode { get; }
+
+ ///
+ /// Gets the primary content type produced by the endpoint.
+ ///
+ public string? ContentType { get; }
+
+ ///
+ /// Gets the additional content types produced by the endpoint.
+ ///
+ public string[] AdditionalContentTypes { get; }
+
+ ///
+ /// Initializes a new instance of the generic Produces attribute class.
+ ///
+ /// The HTTP status code returned by the endpoint.
+ /// The primary content type produced by the endpoint.
+ /// Additional content types produced by the endpoint.
+ public {{ProducesResponseAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status200OK, string? contentType = null, params string[] additionalContentTypes)
+ {
+ StatusCode = statusCode;
+ ContentType = contentType;
+ AdditionalContentTypes = additionalContentTypes ?? [];
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs
new file mode 100644
index 0000000..784e5c0
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs
@@ -0,0 +1,56 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string ProducesValidationProblemAttributeName = "ProducesValidationProblemAttribute";
+ internal const string ProducesValidationProblemAttributeHint = $"{ProducesValidationProblemAttributeFullyQualifiedName}.gs.cs";
+
+ private const string ProducesValidationProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesValidationProblemAttributeName}";
+
+ internal static readonly SourceText ProducesValidationProblemAttributeSourceText = CreateProducesValidationProblemAttributeSourceText();
+
+ private static SourceText CreateProducesValidationProblemAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies that the endpoint produces a validation problem details payload.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
+ internal sealed class {{ProducesValidationProblemAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the HTTP status code returned by the endpoint.
+ ///
+ public int StatusCode { get; }
+
+ ///
+ /// Gets the primary content type produced by the endpoint.
+ ///
+ public string? ContentType { get; }
+
+ ///
+ /// Gets the additional content types produced by the endpoint.
+ ///
+ public string[] AdditionalContentTypes { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The HTTP status code returned by the endpoint.
+ /// The primary content type produced by the endpoint.
+ /// Additional content types produced by the endpoint.
+ public {{ProducesValidationProblemAttributeName}}(int statusCode = global::Microsoft.AspNetCore.Http.StatusCodes.Status400BadRequest, string? contentType = null, params string[] additionalContentTypes)
+ {
+ StatusCode = statusCode;
+ ContentType = contentType;
+ AdditionalContentTypes = additionalContentTypes ?? [];
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs
new file mode 100644
index 0000000..7c7831e
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs
@@ -0,0 +1,49 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string RequestTimeoutAttributeName = "RequestTimeoutAttribute";
+ internal const string RequestTimeoutAttributeHint = $"{RequestTimeoutAttributeFullyQualifiedName}.gs.cs";
+
+ private const string RequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequestTimeoutAttributeName}";
+
+ internal static readonly SourceText RequestTimeoutAttributeSourceText = CreateRequestTimeoutAttributeSourceText();
+
+ private static SourceText CreateRequestTimeoutAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Applies the request timeout metadata to the annotated endpoint or class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{RequestTimeoutAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the optional request timeout policy name.
+ ///
+ public string? PolicyName { get; init; }
+
+ ///
+ /// Applies the default request timeout behavior.
+ ///
+ public {{RequestTimeoutAttributeName}}()
+ {
+ }
+
+ ///
+ /// Applies the specified request timeout policy.
+ ///
+ /// The request timeout policy name.
+ public {{RequestTimeoutAttributeName}}(string policyName)
+ {
+ PolicyName = policyName;
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs
new file mode 100644
index 0000000..bc54ccc
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs
@@ -0,0 +1,49 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string RequireAuthorizationAttributeName = "RequireAuthorizationAttribute";
+ internal const string RequireAuthorizationAttributeHint = $"{RequireAuthorizationAttributeFullyQualifiedName}.gs.cs";
+
+ private const string RequireAuthorizationAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireAuthorizationAttributeName}";
+
+ internal static readonly SourceText RequireAuthorizationAttributeSourceText = CreateRequireAuthorizationAttributeSourceText();
+
+ private static SourceText CreateRequireAuthorizationAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies that authorization is required for the annotated endpoint or class.
+ /// Optionally restricts access to the specified authorization policies.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{RequireAuthorizationAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the policy names that the endpoint or class requires.
+ ///
+ public string[] PolicyNames { get; }
+
+ ///
+ /// Marks the endpoint or class as requiring authorization.
+ ///
+ public {{RequireAuthorizationAttributeName}}()
+ {
+ PolicyNames = [];
+ }
+
+ ///
+ /// Marks the endpoint or class as requiring authorization with one or more policies.
+ ///
+ public {{RequireAuthorizationAttributeName}}(params string[] policyNames)
+ {
+ PolicyNames = policyNames ?? [];
+ }
+ }
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs
new file mode 100644
index 0000000..52fd3e8
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs
@@ -0,0 +1,47 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string RequireCorsAttributeName = "RequireCorsAttribute";
+ internal const string RequireCorsAttributeHint = $"{RequireCorsAttributeFullyQualifiedName}.gs.cs";
+
+ private const string RequireCorsAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireCorsAttributeName}";
+
+ internal static readonly SourceText RequireCorsAttributeSourceText = CreateRequireCorsAttributeSourceText();
+
+ private static SourceText CreateRequireCorsAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies that the annotated endpoint requires a configured CORS policy.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{RequireCorsAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the optional CORS policy name.
+ ///
+ public string? PolicyName { get; }
+
+ ///
+ /// Marks the endpoint or class as requiring the default CORS policy.
+ ///
+ public {{RequireCorsAttributeName}}()
+ {
+ }
+
+ ///
+ /// Marks the endpoint or class as requiring the specified named CORS policy.
+ ///
+ public {{RequireCorsAttributeName}}(string policyName)
+ {
+ PolicyName = policyName;
+ }
+ }
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs
new file mode 100644
index 0000000..7a040a9
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs
@@ -0,0 +1,41 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string RequireHostAttributeName = "RequireHostAttribute";
+ internal const string RequireHostAttributeHint = $"{RequireHostAttributeFullyQualifiedName}.gs.cs";
+
+ private const string RequireHostAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireHostAttributeName}";
+
+ internal static readonly SourceText RequireHostAttributeSourceText = CreateRequireHostAttributeSourceText();
+
+ private static SourceText CreateRequireHostAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies the allowed hosts for the annotated endpoint or class.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{RequireHostAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The hosts that are allowed to access the endpoint.
+ public {{RequireHostAttributeName}}(params string[] hosts)
+ {
+ Hosts = hosts ?? [];
+ }
+
+ ///
+ /// Gets the allowed hosts.
+ ///
+ public string[] Hosts { get; }
+ }
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs
new file mode 100644
index 0000000..fa4f88d
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs
@@ -0,0 +1,41 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string RequireRateLimitingAttributeName = "RequireRateLimitingAttribute";
+ internal const string RequireRateLimitingAttributeHint = $"{RequireRateLimitingAttributeFullyQualifiedName}.gs.cs";
+
+ private const string RequireRateLimitingAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireRateLimitingAttributeName}";
+
+ internal static readonly SourceText RequireRateLimitingAttributeSourceText = CreateRequireRateLimitingAttributeSourceText();
+
+ private static SourceText CreateRequireRateLimitingAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies that the annotated endpoint requires the provided rate limiting policy.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{RequireRateLimitingAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rate limiting policy to apply.
+ public {{RequireRateLimitingAttributeName}}(string policyName)
+ {
+ PolicyName = policyName;
+ }
+
+ ///
+ /// Gets the rate limiting policy name.
+ ///
+ public string PolicyName { get; }
+ }
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs
new file mode 100644
index 0000000..37e2b5c
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs
@@ -0,0 +1,29 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string ShortCircuitAttributeName = "ShortCircuitAttribute";
+ internal const string ShortCircuitAttributeHint = $"{ShortCircuitAttributeFullyQualifiedName}.gs.cs";
+
+ private const string ShortCircuitAttributeFullyQualifiedName = $"{AttributesNamespace}.{ShortCircuitAttributeName}";
+
+ internal static readonly SourceText ShortCircuitAttributeSourceText = CreateShortCircuitAttributeSourceText();
+
+ private static SourceText CreateShortCircuitAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Marks the annotated endpoint or class to short-circuit the request pipeline.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{ShortCircuitAttributeName}} : global::System.Attribute
+ {
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs b/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs
new file mode 100644
index 0000000..ab45af4
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs
@@ -0,0 +1,42 @@
+using System.Text;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ internal const string SummaryAttributeName = "SummaryAttribute";
+ internal const string SummaryAttributeHint = $"{SummaryAttributeFullyQualifiedName}.gs.cs";
+
+ private const string SummaryAttributeFullyQualifiedName = $"{AttributesNamespace}.{SummaryAttributeName}";
+
+ internal static readonly SourceText SummaryAttributeSourceText = CreateSummaryAttributeSourceText();
+
+ private static SourceText CreateSummaryAttributeSourceText() => SourceText.From($$"""
+ {{FileHeader}}
+
+ namespace {{AttributesNamespace}};
+
+ ///
+ /// Specifies the summary metadata for the annotated endpoint.
+ ///
+ [global::System.AttributeUsage(global::System.AttributeTargets.Class | global::System.AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ internal sealed class {{SummaryAttributeName}} : global::System.Attribute
+ {
+ ///
+ /// Gets the summary value for the endpoint.
+ ///
+ public string Summary { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The summary to apply to the endpoint.
+ public {{SummaryAttributeName}}(string summary)
+ {
+ Summary = summary;
+ }
+ }
+
+ """, Encoding.UTF8);
+}
diff --git a/src/GeneratedEndpoints/Common/Constants.cs b/src/GeneratedEndpoints/Common/Constants.cs
index d7cb149..b170d78 100644
--- a/src/GeneratedEndpoints/Common/Constants.cs
+++ b/src/GeneratedEndpoints/Common/Constants.cs
@@ -2,62 +2,24 @@ namespace GeneratedEndpoints.Common;
internal static partial class Constants
{
+ internal const string FileHeader =
+ "//-----------------------------------------------------------------------------\n" +
+ "// \n" +
+ "// This code was generated by MinimalApiGenerator which can be found\n" +
+ "// in the GeneratedEndpoints namespace.\n" +
+ "//\n" +
+ "// Changes to this file may cause incorrect behavior\n" +
+ "// and will be lost if the code is regenerated.\n" +
+ "// \n" +
+ "//-----------------------------------------------------------------------------\n" +
+ "\n" +
+ "#nullable enable";
+
internal const string FallbackHttpMethod = "__FALLBACK__";
internal const string NameAttributeNamedParameter = "Name";
internal const string IsOptionalAttributeNamedParameter = "IsOptional";
- internal const string RequireAuthorizationAttributeName = "RequireAuthorizationAttribute";
- internal const string RequireAuthorizationAttributeHint = $"{RequireAuthorizationAttributeFullyQualifiedName}.gs.cs";
-
- internal const string RequireCorsAttributeName = "RequireCorsAttribute";
- internal const string RequireCorsAttributeHint = $"{RequireCorsAttributeFullyQualifiedName}.gs.cs";
-
- internal const string RequireRateLimitingAttributeName = "RequireRateLimitingAttribute";
- internal const string RequireRateLimitingAttributeHint = $"{RequireRateLimitingAttributeFullyQualifiedName}.gs.cs";
-
- internal const string RequireHostAttributeName = "RequireHostAttribute";
- internal const string RequireHostAttributeHint = $"{RequireHostAttributeFullyQualifiedName}.gs.cs";
-
- internal const string DisableAntiforgeryAttributeName = "DisableAntiforgeryAttribute";
- internal const string DisableAntiforgeryAttributeHint = $"{DisableAntiforgeryAttributeFullyQualifiedName}.gs.cs";
-
- internal const string ShortCircuitAttributeName = "ShortCircuitAttribute";
- internal const string ShortCircuitAttributeHint = $"{ShortCircuitAttributeFullyQualifiedName}.gs.cs";
-
- internal const string DisableRequestTimeoutAttributeName = "DisableRequestTimeoutAttribute";
- internal const string DisableRequestTimeoutAttributeHint = $"{DisableRequestTimeoutAttributeFullyQualifiedName}.gs.cs";
-
- internal const string DisableValidationAttributeName = "DisableValidationAttribute";
- internal const string DisableValidationAttributeHint = $"{DisableValidationAttributeFullyQualifiedName}.gs.cs";
-
- internal const string RequestTimeoutAttributeName = "RequestTimeoutAttribute";
- internal const string RequestTimeoutAttributeHint = $"{RequestTimeoutAttributeFullyQualifiedName}.gs.cs";
-
- internal const string OrderAttributeName = "OrderAttribute";
- internal const string OrderAttributeHint = $"{OrderAttributeFullyQualifiedName}.gs.cs";
-
- internal const string MapGroupAttributeName = "MapGroupAttribute";
- internal const string MapGroupAttributeHint = $"{MapGroupAttributeFullyQualifiedName}.gs.cs";
-
- internal const string SummaryAttributeName = "SummaryAttribute";
- internal const string SummaryAttributeHint = $"{SummaryAttributeFullyQualifiedName}.gs.cs";
-
- internal const string EndpointFilterAttributeName = "EndpointFilterAttribute";
- internal const string EndpointFilterAttributeHint = $"{EndpointFilterAttributeFullyQualifiedName}.gs.cs";
-
- internal const string AcceptsAttributeName = "AcceptsAttribute";
- internal const string AcceptsAttributeHint = $"{AcceptsAttributeFullyQualifiedName}.gs.cs";
-
- internal const string ProducesResponseAttributeName = "ProducesResponseAttribute";
- internal const string ProducesResponseAttributeHint = $"{ProducesResponseAttributeFullyQualifiedName}.gs.cs";
-
- internal const string ProducesProblemAttributeName = "ProducesProblemAttribute";
- internal const string ProducesProblemAttributeHint = $"{ProducesProblemAttributeFullyQualifiedName}.gs.cs";
-
- internal const string ProducesValidationProblemAttributeName = "ProducesValidationProblemAttribute";
- internal const string ProducesValidationProblemAttributeHint = $"{ProducesValidationProblemAttributeFullyQualifiedName}.gs.cs";
-
internal const string RoutingNamespace = $"{BaseNamespace}.Routing";
internal const string AddEndpointHandlersClassName = "EndpointServicesExtensions";
@@ -74,24 +36,7 @@ internal static partial class Constants
internal const string GlobalPrefix = "global::";
private const string BaseNamespace = "Microsoft.AspNetCore.Generated";
- private const string AttributesNamespace = $"{BaseNamespace}.Attributes";
- private const string RequireAuthorizationAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireAuthorizationAttributeName}";
- private const string RequireCorsAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireCorsAttributeName}";
- private const string RequireRateLimitingAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireRateLimitingAttributeName}";
- private const string RequireHostAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireHostAttributeName}";
- private const string DisableAntiforgeryAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableAntiforgeryAttributeName}";
- private const string ShortCircuitAttributeFullyQualifiedName = $"{AttributesNamespace}.{ShortCircuitAttributeName}";
- private const string DisableRequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableRequestTimeoutAttributeName}";
- private const string DisableValidationAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableValidationAttributeName}";
- private const string RequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequestTimeoutAttributeName}";
- private const string OrderAttributeFullyQualifiedName = $"{AttributesNamespace}.{OrderAttributeName}";
- private const string MapGroupAttributeFullyQualifiedName = $"{AttributesNamespace}.{MapGroupAttributeName}";
- private const string SummaryAttributeFullyQualifiedName = $"{AttributesNamespace}.{SummaryAttributeName}";
- private const string EndpointFilterAttributeFullyQualifiedName = $"{AttributesNamespace}.{EndpointFilterAttributeName}";
- private const string AcceptsAttributeFullyQualifiedName = $"{AttributesNamespace}.{AcceptsAttributeName}";
- private const string ProducesResponseAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesResponseAttributeName}";
- private const string ProducesProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesProblemAttributeName}";
- private const string ProducesValidationProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesValidationProblemAttributeName}";
+ internal const string AttributesNamespace = $"{BaseNamespace}.Attributes";
private const string AddEndpointHandlersMethodFullyQualifiedName = $"{RoutingNamespace}.{AddEndpointHandlersMethodName}";
private const string UseEndpointHandlersMethodFullyQualifiedName = $"{RoutingNamespace}.{UseEndpointHandlersMethodName}";
}
diff --git a/src/GeneratedEndpoints/Common/DeclarationExtensions.cs b/src/GeneratedEndpoints/Common/DeclarationExtensions.cs
index aa020a6..5b65c18 100644
--- a/src/GeneratedEndpoints/Common/DeclarationExtensions.cs
+++ b/src/GeneratedEndpoints/Common/DeclarationExtensions.cs
@@ -1,5 +1,3 @@
-using System.Text;
-
namespace GeneratedEndpoints.Common;
/// Provides extension methods for working with declarations.
@@ -10,7 +8,7 @@ internal static class DeclarationExtensions
/// The namespace represented by the declarations.
public static string ToNamespace(this EquatableImmutableArray declarations)
{
- var builder = new StringBuilder();
+ var builder = StringBuilderPool.Get();
for (var index = 0; index < declarations.Count; index++)
{
@@ -23,7 +21,7 @@ public static string ToNamespace(this EquatableImmutableArray decla
builder.Append(declaration.Name);
}
- return builder.ToString();
+ return StringBuilderPool.ToStringAndReturn(builder);
}
/// Converts a list of declarations to their fully qualified name.
@@ -31,7 +29,7 @@ public static string ToNamespace(this EquatableImmutableArray decla
/// The fully qualified name represented by the declarations.
public static string ToFullyQualifiedName(this EquatableImmutableArray declarations)
{
- var builder = new StringBuilder();
+ var builder = StringBuilderPool.Get();
for (var index = 0; index < declarations.Count; index++)
{
@@ -48,7 +46,7 @@ public static string ToFullyQualifiedName(this EquatableImmutableArray Where(this EquatableImmutableArray declarations, Func predicate)
diff --git a/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs b/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs
index f8dccd5..a03f476 100644
--- a/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs
+++ b/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs
@@ -170,12 +170,7 @@ public static EndpointConfiguration Create(ISymbol symbol)
RequestTimeoutPolicyName = requestTimeoutPolicyName,
Order = order,
Group = groupIdentifier is not null && groupPattern is not null
- ? new EndpointGroup
- {
- Identifier = groupIdentifier,
- Pattern = groupPattern,
- Name = groupName,
- }
+ ? new EndpointGroup { Identifier = groupIdentifier, Pattern = groupPattern, Name = groupName }
: null,
};
}
@@ -207,8 +202,10 @@ public static EndpointConfiguration Create(ISymbol symbol)
private static void TryAddAcceptsMetadata(AttributeData attribute, INamedTypeSymbol attributeClass, ref List? accepts)
{
+ var isGenericAttribute = attributeClass is { IsGenericType: true, TypeArguments.Length: 1 };
+
string? requestType;
- if (attributeClass is { IsGenericType: true, TypeArguments.Length: 1 })
+ if (isGenericAttribute)
requestType = attributeClass.TypeArguments[0]
.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
else
@@ -218,8 +215,11 @@ private static void TryAddAcceptsMetadata(AttributeData attribute, INamedTypeSym
if (requestType is null)
return;
- var contentType = attribute.GetConstructorStringValue() ?? ApplicationJsonContentType;
- var additionalContentTypes = attribute.GetConstructorStringArray(1);
+ var contentTypeIndex = isGenericAttribute ? 0 : 1;
+ var additionalContentTypesIndex = isGenericAttribute ? 1 : 2;
+
+ var contentType = attribute.GetConstructorStringValue(contentTypeIndex) ?? ApplicationJsonContentType;
+ var additionalContentTypes = attribute.GetConstructorStringArray(additionalContentTypesIndex);
var isOptional = attribute.GetNamedBoolValue(IsOptionalAttributeNamedParameter);
var acceptMetadata = new AcceptsMetadata(requestType, contentType, additionalContentTypes, isOptional);
@@ -235,7 +235,7 @@ private static void TryAddProducesMetadata(AttributeData attribute, INamedTypeSy
{
var responseType = attributeClass.TypeArguments[0]
.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
- var statusCode = attribute.GetConstructorIntValue(0) ?? 200;
+ var statusCode = attribute.GetConstructorIntValue() ?? 200;
var contentType = attribute.GetConstructorStringValue(1);
var additionalContentTypes = attribute.GetConstructorStringArray(2);
producesMetadata = new ProducesMetadata(responseType, statusCode, contentType, additionalContentTypes);
diff --git a/src/GeneratedEndpoints/Common/RequestHandlerClass.cs b/src/GeneratedEndpoints/Common/RequestHandlerClass.cs
index e3c8336..17ac27a 100644
--- a/src/GeneratedEndpoints/Common/RequestHandlerClass.cs
+++ b/src/GeneratedEndpoints/Common/RequestHandlerClass.cs
@@ -4,6 +4,7 @@ namespace GeneratedEndpoints.Common;
{
public required string Name { get; init; }
public required bool IsStatic { get; init; }
+ public required bool IsAbstract { get; init; }
public required bool HasConfigureMethod { get; init; }
public required bool ConfigureMethodAcceptsServiceProvider { get; init; }
public required EndpointConfiguration Configuration { get; init; }
diff --git a/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs b/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs
index 806a254..08e6d93 100644
--- a/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs
+++ b/src/GeneratedEndpoints/Common/RequestHandlerClassHelper.cs
@@ -15,6 +15,7 @@ internal static class RequestHandlerClassHelper
var name = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var isStatic = classSymbol.IsStatic;
+ var isAbstract = classSymbol.IsAbstract;
var configureMethodDetails = GetConfigureMethodDetails(classSymbol, cancellationToken);
var classConfiguration = EndpointConfigurationFactory.Create(classSymbol);
@@ -22,6 +23,7 @@ internal static class RequestHandlerClassHelper
{
Name = name,
IsStatic = isStatic,
+ IsAbstract = isAbstract,
HasConfigureMethod = configureMethodDetails.HasConfigureMethod,
ConfigureMethodAcceptsServiceProvider = configureMethodDetails.ConfigureMethodAcceptsServiceProvider,
Configuration = classConfiguration,
diff --git a/src/GeneratedEndpoints/GeneratedEndpoints.csproj b/src/GeneratedEndpoints/GeneratedEndpoints.csproj
index efddba0..a5c4bae 100644
--- a/src/GeneratedEndpoints/GeneratedEndpoints.csproj
+++ b/src/GeneratedEndpoints/GeneratedEndpoints.csproj
@@ -27,7 +27,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -36,9 +36,9 @@
GeneratedEndpoints
- 10.0.2
- 10.0.2.0
- 10.0.2.0
+ 10.0.3
+ 10.0.3.0
+ 10.0.3.0
en-US
false
diff --git a/src/GeneratedEndpoints/MinimalApiGenerator.cs b/src/GeneratedEndpoints/MinimalApiGenerator.cs
index 873acfd..b6051b7 100644
--- a/src/GeneratedEndpoints/MinimalApiGenerator.cs
+++ b/src/GeneratedEndpoints/MinimalApiGenerator.cs
@@ -105,6 +105,9 @@ private static bool RequestHandlerFilter(SyntaxNode syntaxNode, CancellationToke
var requestHandlerMethod = RequestHandlerMethodHelper.Create(methodSymbol, cancellationToken);
+ if (requestHandlerClass.Value.IsAbstract && !requestHandlerMethod.IsStatic)
+ return null;
+
var (httpMethod, pattern, name) = GetRequestHandlerAttribute(methodSymbol, attribute, cancellationToken);
var requestHandler = new RequestHandler
diff --git a/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs b/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs
index 035ce8d..5611197 100644
--- a/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs
+++ b/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs
@@ -29,6 +29,20 @@ public static string BuildFallbackSource(bool includeDefault, bool includeCustom
return builder.ToString();
}
+ public static string BuildAbstractEndpointsSource()
+ {
+ return """
+ internal abstract class AbstractEndpoints
+ {
+ [MapGet("/abstract/static")]
+ public static Ok Static() => TypedResults.Ok();
+
+ [MapPost("/abstract/instance")]
+ public Ok Instance() => TypedResults.Ok();
+ }
+ """;
+ }
+
public static string BuildAuthorizationMatrixSource(
bool classAllowAnonymous,
bool methodAllowAnonymous,
@@ -370,7 +384,7 @@ public static string BuildContractsAndBindingSource(
if (includeAccepts)
{
var secondContentType = string.IsNullOrWhiteSpace(acceptsContentType2) ? "" : $", \"{acceptsContentType2}\"";
- builder.AppendLine($" [Accepts(\"{acceptsContentType1 ?? "application/json"}\"{secondContentType})]");
+ builder.AppendLine($" [Accepts(typeof(RequestRecord), \"{acceptsContentType1 ?? "application/json"}\"{secondContentType})]");
}
if (includeGenericAccepts)
diff --git a/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj b/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj
index 1bd4374..8d28c01 100644
--- a/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj
+++ b/tests/GeneratedEndpoints.Tests/GeneratedEndpoints.Tests.csproj
@@ -14,7 +14,7 @@
-
+
all
diff --git a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt
index c9e81bb..33f7099 100644
--- a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt
+++ b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_0BE3EC6390D4_MapEndpointHandlers.verified.txt
@@ -26,6 +26,7 @@ internal static class EndpointRouteBuilderExtensions
.WithName("Handle")
.WithDisplayName("Contract endpoint")
.WithTags("Contracts", "Bindings")
+ .Accepts("application/custom")
.Produces(200, "application/problem+json")
.ProducesProblem(500, "application/problem+json")
.AllowAnonymous();
diff --git a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt
index d535349..ee24747 100644
--- a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt
+++ b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_2F85DF1A025B_MapEndpointHandlers.verified.txt
@@ -26,6 +26,7 @@ internal static class EndpointRouteBuilderExtensions
.WithName("Handle")
.WithDisplayName("Contract endpoint")
.ExcludeFromDescription()
+ .Accepts("application/xml")
.Produces(200, "application/json")
.ProducesProblem(500, "application/json")
.RequireAuthorization("ContractsPolicy");
diff --git a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt
index 74ba401..0930bfa 100644
--- a/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt
+++ b/tests/GeneratedEndpoints.Tests/GeneratedSourceTests.ContractsAndBindingMatrix_9F5FE6E1F139_MapEndpointHandlers.verified.txt
@@ -28,6 +28,7 @@ internal static class EndpointRouteBuilderExtensions
.WithSummary("Gets detailed content.")
.WithDescription("Shows binding and contract combinations.")
.WithTags("Contracts", "Bindings")
+ .Accepts("application/xml", "text/xml")
.Accepts("application/xml")
.Produces(200, "application/json", "text/json")
.Produces(200, "application/json")
diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_AddEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_AddEndpointHandlers.verified.txt
new file mode 100644
index 0000000..e27630b
--- /dev/null
+++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_AddEndpointHandlers.verified.txt
@@ -0,0 +1,23 @@
+//-----------------------------------------------------------------------------
+//
+// This code was generated by MinimalApiGenerator which can be found
+// in the GeneratedEndpoints namespace.
+//
+// Changes to this file may cause incorrect behavior
+// and will be lost if the code is regenerated.
+//
+//-----------------------------------------------------------------------------
+
+#nullable enable
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace Microsoft.AspNetCore.Generated.Routing;
+
+internal static class EndpointServicesExtensions
+{
+ internal static void AddEndpointHandlers(this IServiceCollection services)
+ {
+ }
+}
diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_MapEndpointHandlers.verified.txt
new file mode 100644
index 0000000..eacb7f5
--- /dev/null
+++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_MapEndpointHandlers.verified.txt
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------------
+//
+// This code was generated by MinimalApiGenerator which can be found
+// in the GeneratedEndpoints namespace.
+//
+// Changes to this file may cause incorrect behavior
+// and will be lost if the code is regenerated.
+//
+//-----------------------------------------------------------------------------
+
+#nullable enable
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Generated.Routing;
+
+internal static class EndpointRouteBuilderExtensions
+{
+ internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder)
+ {
+ builder.MapGet("/abstract/static", global::GeneratedEndpointsTests.AbstractEndpoints.Static)
+ .WithName("Static");
+
+ return builder;
+ }
+}
diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt
index d8a37e9..be1b5fd 100644
--- a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt
+++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsAttribute_MapEndpointHandlers.verified.txt
@@ -23,7 +23,8 @@ internal static class EndpointRouteBuilderExtensions
internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder)
{
builder.MapGet("/contracts/{id:int}", global::GeneratedEndpointsTests.ContractEndpoints.Handle)
- .WithName("Handle");
+ .WithName("Handle")
+ .Accepts("application/custom");
return builder;
}
diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt
index d8a37e9..104e71d 100644
--- a/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt
+++ b/tests/GeneratedEndpoints.Tests/IndividualTests.AcceptsMultipleContentTypes_MapEndpointHandlers.verified.txt
@@ -23,7 +23,8 @@ internal static class EndpointRouteBuilderExtensions
internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder)
{
builder.MapGet("/contracts/{id:int}", global::GeneratedEndpointsTests.ContractEndpoints.Handle)
- .WithName("Handle");
+ .WithName("Handle")
+ .Accepts("application/json", "text/json");
return builder;
}
diff --git a/tests/GeneratedEndpoints.Tests/IndividualTests.cs b/tests/GeneratedEndpoints.Tests/IndividualTests.cs
index bd0d6c3..4cf7e63 100644
--- a/tests/GeneratedEndpoints.Tests/IndividualTests.cs
+++ b/tests/GeneratedEndpoints.Tests/IndividualTests.cs
@@ -172,6 +172,13 @@ public async Task OrderMetadata()
await VerifyIndividualAsync(source, nameof(OrderMetadata));
}
+ [Fact]
+ public async Task AbstractClassSkipsInstanceHandlers()
+ {
+ var source = AbstractEndpoints();
+ await VerifyIndividualAsync(source, nameof(AbstractClassSkipsInstanceHandlers));
+ }
+
[Fact]
public async Task ClassMapGroup()
{
@@ -472,6 +479,11 @@ private static string FallbackScenario(bool includeDefault = false, bool include
return SourceFactory.BuildFallbackSource(includeDefault, includeCustom, customRoute);
}
+ private static string AbstractEndpoints()
+ {
+ return SourceFactory.BuildAbstractEndpointsSource();
+ }
+
private static string AuthorizationScenario(
bool classAllowAnonymous = false,
bool methodAllowAnonymous = false,