From 09f2d362808547c0c07346ed53e783caee4d6cc1 Mon Sep 17 00:00:00 2001
From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com>
Date: Fri, 28 Nov 2025 11:14:08 -0500
Subject: [PATCH 1/6] Refactor constants into attribute-specific files (#76)
---
.../Common/Constants.AcceptsAttribute.cs | 100 +++
.../Constants.AttributesInitialization.cs | 31 +
.../Constants.DisableAntiforgeryAttribute.cs | 29 +
...onstants.DisableRequestTimeoutAttribute.cs | 29 +
.../Constants.DisableValidationAttribute.cs | 31 +
.../Constants.EndpointFilterAttribute.cs | 55 ++
.../Common/Constants.GeneratedSources.cs | 626 ------------------
.../Common/Constants.MapGroupAttribute.cs | 47 ++
.../Common/Constants.OrderAttribute.cs | 42 ++
.../Constants.ProducesProblemAttribute.cs | 56 ++
.../Constants.ProducesResponseAttribute.cs | 104 +++
...ants.ProducesValidationProblemAttribute.cs | 56 ++
.../Constants.RequestTimeoutAttribute.cs | 49 ++
...Constants.RequireAuthorizationAttribute.cs | 49 ++
.../Common/Constants.RequireCorsAttribute.cs | 47 ++
.../Common/Constants.RequireHostAttribute.cs | 41 ++
.../Constants.RequireRateLimitingAttribute.cs | 41 ++
.../Common/Constants.ShortCircuitAttribute.cs | 29 +
.../Common/Constants.SummaryAttribute.cs | 42 ++
src/GeneratedEndpoints/Common/Constants.cs | 70 +-
20 files changed, 879 insertions(+), 695 deletions(-)
create mode 100644 src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.AttributesInitialization.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs
create mode 100644 src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs
diff --git a/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs b/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs
new file mode 100644
index 0000000..c07e4d7
--- /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;
+
+ 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.AttributesInitialization.cs b/src/GeneratedEndpoints/Common/Constants.AttributesInitialization.cs
new file mode 100644
index 0000000..41461ee
--- /dev/null
+++ b/src/GeneratedEndpoints/Common/Constants.AttributesInitialization.cs
@@ -0,0 +1,31 @@
+#pragma warning disable S3963
+#pragma warning disable CA1810
+
+namespace GeneratedEndpoints.Common;
+
+internal static partial class Constants
+{
+ static Constants()
+ {
+ RequireAuthorizationAttributeSourceText = CreateRequireAuthorizationAttributeSourceText();
+ RequireCorsAttributeSourceText = CreateRequireCorsAttributeSourceText();
+ RequireRateLimitingAttributeSourceText = CreateRequireRateLimitingAttributeSourceText();
+ RequireHostAttributeSourceText = CreateRequireHostAttributeSourceText();
+ DisableAntiforgeryAttributeSourceText = CreateDisableAntiforgeryAttributeSourceText();
+ ShortCircuitAttributeSourceText = CreateShortCircuitAttributeSourceText();
+ DisableRequestTimeoutAttributeSourceText = CreateDisableRequestTimeoutAttributeSourceText();
+ DisableValidationAttributeSourceText = CreateDisableValidationAttributeSourceText();
+ RequestTimeoutAttributeSourceText = CreateRequestTimeoutAttributeSourceText();
+ OrderAttributeSourceText = CreateOrderAttributeSourceText();
+ MapGroupAttributeSourceText = CreateMapGroupAttributeSourceText();
+ SummaryAttributeSourceText = CreateSummaryAttributeSourceText();
+ EndpointFilterAttributeSourceText = CreateEndpointFilterAttributeSourceText();
+ AcceptsAttributeSourceText = CreateAcceptsAttributeSourceText();
+ ProducesResponseAttributeSourceText = CreateProducesResponseAttributeSourceText();
+ ProducesProblemAttributeSourceText = CreateProducesProblemAttributeSourceText();
+ ProducesValidationProblemAttributeSourceText = CreateProducesValidationProblemAttributeSourceText();
+ }
+}
+
+#pragma warning restore CA1810
+#pragma warning restore S3963
diff --git a/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs
new file mode 100644
index 0000000..4a10f94
--- /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;
+
+ 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..1ab9d08
--- /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;
+
+ 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..8ac0c6c
--- /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;
+
+ 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..97bb1c3
--- /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;
+
+ 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..81ac968 100644
--- a/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs
+++ b/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs
@@ -38,632 +38,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..cc29b68
--- /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;
+
+ 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..64e06d5
--- /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;
+
+ 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..269541f
--- /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;
+
+ 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..191f29f
--- /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;
+
+ 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..b1f1708
--- /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;
+
+ 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..c77fbaf
--- /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;
+
+ 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..7d35f79
--- /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;
+
+ 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..766ee22
--- /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;
+
+ 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..d8966e0
--- /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;
+
+ 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..ad74b5d
--- /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;
+
+ 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..dc5104d
--- /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;
+
+ 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..ae0c537
--- /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;
+
+ 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..1c393b6 100644
--- a/src/GeneratedEndpoints/Common/Constants.cs
+++ b/src/GeneratedEndpoints/Common/Constants.cs
@@ -7,57 +7,6 @@ internal static partial class Constants
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 +23,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}";
}
From a719d1299bbee03643f59551710299cd4a403cba Mon Sep 17 00:00:00 2001
From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com>
Date: Fri, 28 Nov 2025 11:40:30 -0500
Subject: [PATCH 2/6] Inline attribute initialization (#77)
---
.../Common/Constants.AcceptsAttribute.cs | 2 +-
.../Constants.AttributesInitialization.cs | 31 -------------------
.../Constants.DisableAntiforgeryAttribute.cs | 2 +-
...onstants.DisableRequestTimeoutAttribute.cs | 2 +-
.../Constants.DisableValidationAttribute.cs | 2 +-
.../Constants.EndpointFilterAttribute.cs | 2 +-
.../Common/Constants.GeneratedSources.cs | 13 --------
.../Common/Constants.MapGroupAttribute.cs | 2 +-
.../Common/Constants.OrderAttribute.cs | 2 +-
.../Constants.ProducesProblemAttribute.cs | 2 +-
.../Constants.ProducesResponseAttribute.cs | 2 +-
...ants.ProducesValidationProblemAttribute.cs | 2 +-
.../Constants.RequestTimeoutAttribute.cs | 2 +-
...Constants.RequireAuthorizationAttribute.cs | 2 +-
.../Common/Constants.RequireCorsAttribute.cs | 2 +-
.../Common/Constants.RequireHostAttribute.cs | 2 +-
.../Constants.RequireRateLimitingAttribute.cs | 2 +-
.../Common/Constants.ShortCircuitAttribute.cs | 2 +-
.../Common/Constants.SummaryAttribute.cs | 2 +-
src/GeneratedEndpoints/Common/Constants.cs | 13 ++++++++
20 files changed, 30 insertions(+), 61 deletions(-)
delete mode 100644 src/GeneratedEndpoints/Common/Constants.AttributesInitialization.cs
diff --git a/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs b/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs
index c07e4d7..25c413a 100644
--- a/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.AcceptsAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string AcceptsAttributeFullyQualifiedName = $"{AttributesNamespace}.{AcceptsAttributeName}";
- internal static readonly SourceText AcceptsAttributeSourceText;
+ internal static readonly SourceText AcceptsAttributeSourceText = CreateAcceptsAttributeSourceText();
private static SourceText CreateAcceptsAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.AttributesInitialization.cs b/src/GeneratedEndpoints/Common/Constants.AttributesInitialization.cs
deleted file mode 100644
index 41461ee..0000000
--- a/src/GeneratedEndpoints/Common/Constants.AttributesInitialization.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma warning disable S3963
-#pragma warning disable CA1810
-
-namespace GeneratedEndpoints.Common;
-
-internal static partial class Constants
-{
- static Constants()
- {
- RequireAuthorizationAttributeSourceText = CreateRequireAuthorizationAttributeSourceText();
- RequireCorsAttributeSourceText = CreateRequireCorsAttributeSourceText();
- RequireRateLimitingAttributeSourceText = CreateRequireRateLimitingAttributeSourceText();
- RequireHostAttributeSourceText = CreateRequireHostAttributeSourceText();
- DisableAntiforgeryAttributeSourceText = CreateDisableAntiforgeryAttributeSourceText();
- ShortCircuitAttributeSourceText = CreateShortCircuitAttributeSourceText();
- DisableRequestTimeoutAttributeSourceText = CreateDisableRequestTimeoutAttributeSourceText();
- DisableValidationAttributeSourceText = CreateDisableValidationAttributeSourceText();
- RequestTimeoutAttributeSourceText = CreateRequestTimeoutAttributeSourceText();
- OrderAttributeSourceText = CreateOrderAttributeSourceText();
- MapGroupAttributeSourceText = CreateMapGroupAttributeSourceText();
- SummaryAttributeSourceText = CreateSummaryAttributeSourceText();
- EndpointFilterAttributeSourceText = CreateEndpointFilterAttributeSourceText();
- AcceptsAttributeSourceText = CreateAcceptsAttributeSourceText();
- ProducesResponseAttributeSourceText = CreateProducesResponseAttributeSourceText();
- ProducesProblemAttributeSourceText = CreateProducesProblemAttributeSourceText();
- ProducesValidationProblemAttributeSourceText = CreateProducesValidationProblemAttributeSourceText();
- }
-}
-
-#pragma warning restore CA1810
-#pragma warning restore S3963
diff --git a/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs
index 4a10f94..7973b79 100644
--- a/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.DisableAntiforgeryAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string DisableAntiforgeryAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableAntiforgeryAttributeName}";
- internal static readonly SourceText DisableAntiforgeryAttributeSourceText;
+ internal static readonly SourceText DisableAntiforgeryAttributeSourceText = CreateDisableAntiforgeryAttributeSourceText();
private static SourceText CreateDisableAntiforgeryAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs
index 1ab9d08..99225ef 100644
--- a/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.DisableRequestTimeoutAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string DisableRequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableRequestTimeoutAttributeName}";
- internal static readonly SourceText DisableRequestTimeoutAttributeSourceText;
+ internal static readonly SourceText DisableRequestTimeoutAttributeSourceText = CreateDisableRequestTimeoutAttributeSourceText();
private static SourceText CreateDisableRequestTimeoutAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs b/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs
index 8ac0c6c..f98194e 100644
--- a/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.DisableValidationAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string DisableValidationAttributeFullyQualifiedName = $"{AttributesNamespace}.{DisableValidationAttributeName}";
- internal static readonly SourceText DisableValidationAttributeSourceText;
+ internal static readonly SourceText DisableValidationAttributeSourceText = CreateDisableValidationAttributeSourceText();
private static SourceText CreateDisableValidationAttributeSourceText() => SourceText.From($$"""
#if NET10_0_OR_GREATER
diff --git a/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs b/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs
index 97bb1c3..71fd661 100644
--- a/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.EndpointFilterAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string EndpointFilterAttributeFullyQualifiedName = $"{AttributesNamespace}.{EndpointFilterAttributeName}";
- internal static readonly SourceText EndpointFilterAttributeSourceText;
+ internal static readonly SourceText EndpointFilterAttributeSourceText = CreateEndpointFilterAttributeSourceText();
private static SourceText CreateEndpointFilterAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs b/src/GeneratedEndpoints/Common/Constants.GeneratedSources.cs
index 81ac968..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 =
[
diff --git a/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs b/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs
index cc29b68..4b61e0b 100644
--- a/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.MapGroupAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string MapGroupAttributeFullyQualifiedName = $"{AttributesNamespace}.{MapGroupAttributeName}";
- internal static readonly SourceText MapGroupAttributeSourceText;
+ internal static readonly SourceText MapGroupAttributeSourceText = CreateMapGroupAttributeSourceText();
private static SourceText CreateMapGroupAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs b/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs
index 64e06d5..a52e2fe 100644
--- a/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.OrderAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string OrderAttributeFullyQualifiedName = $"{AttributesNamespace}.{OrderAttributeName}";
- internal static readonly SourceText OrderAttributeSourceText;
+ internal static readonly SourceText OrderAttributeSourceText = CreateOrderAttributeSourceText();
private static SourceText CreateOrderAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs
index 269541f..80a6d5f 100644
--- a/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.ProducesProblemAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string ProducesProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesProblemAttributeName}";
- internal static readonly SourceText ProducesProblemAttributeSourceText;
+ internal static readonly SourceText ProducesProblemAttributeSourceText = CreateProducesProblemAttributeSourceText();
private static SourceText CreateProducesProblemAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs
index 191f29f..a7bee4b 100644
--- a/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.ProducesResponseAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string ProducesResponseAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesResponseAttributeName}";
- internal static readonly SourceText ProducesResponseAttributeSourceText;
+ internal static readonly SourceText ProducesResponseAttributeSourceText = CreateProducesResponseAttributeSourceText();
private static SourceText CreateProducesResponseAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs
index b1f1708..784e5c0 100644
--- a/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.ProducesValidationProblemAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string ProducesValidationProblemAttributeFullyQualifiedName = $"{AttributesNamespace}.{ProducesValidationProblemAttributeName}";
- internal static readonly SourceText ProducesValidationProblemAttributeSourceText;
+ internal static readonly SourceText ProducesValidationProblemAttributeSourceText = CreateProducesValidationProblemAttributeSourceText();
private static SourceText CreateProducesValidationProblemAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs
index c77fbaf..7c7831e 100644
--- a/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.RequestTimeoutAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string RequestTimeoutAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequestTimeoutAttributeName}";
- internal static readonly SourceText RequestTimeoutAttributeSourceText;
+ internal static readonly SourceText RequestTimeoutAttributeSourceText = CreateRequestTimeoutAttributeSourceText();
private static SourceText CreateRequestTimeoutAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs
index 7d35f79..bc54ccc 100644
--- a/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.RequireAuthorizationAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string RequireAuthorizationAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireAuthorizationAttributeName}";
- internal static readonly SourceText RequireAuthorizationAttributeSourceText;
+ internal static readonly SourceText RequireAuthorizationAttributeSourceText = CreateRequireAuthorizationAttributeSourceText();
private static SourceText CreateRequireAuthorizationAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs
index 766ee22..52fd3e8 100644
--- a/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.RequireCorsAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string RequireCorsAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireCorsAttributeName}";
- internal static readonly SourceText RequireCorsAttributeSourceText;
+ internal static readonly SourceText RequireCorsAttributeSourceText = CreateRequireCorsAttributeSourceText();
private static SourceText CreateRequireCorsAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs
index d8966e0..7a040a9 100644
--- a/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.RequireHostAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string RequireHostAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireHostAttributeName}";
- internal static readonly SourceText RequireHostAttributeSourceText;
+ internal static readonly SourceText RequireHostAttributeSourceText = CreateRequireHostAttributeSourceText();
private static SourceText CreateRequireHostAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs b/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs
index ad74b5d..fa4f88d 100644
--- a/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.RequireRateLimitingAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string RequireRateLimitingAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireRateLimitingAttributeName}";
- internal static readonly SourceText RequireRateLimitingAttributeSourceText;
+ internal static readonly SourceText RequireRateLimitingAttributeSourceText = CreateRequireRateLimitingAttributeSourceText();
private static SourceText CreateRequireRateLimitingAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs b/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs
index dc5104d..37e2b5c 100644
--- a/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.ShortCircuitAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string ShortCircuitAttributeFullyQualifiedName = $"{AttributesNamespace}.{ShortCircuitAttributeName}";
- internal static readonly SourceText ShortCircuitAttributeSourceText;
+ internal static readonly SourceText ShortCircuitAttributeSourceText = CreateShortCircuitAttributeSourceText();
private static SourceText CreateShortCircuitAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs b/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs
index ae0c537..ab45af4 100644
--- a/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs
+++ b/src/GeneratedEndpoints/Common/Constants.SummaryAttribute.cs
@@ -10,7 +10,7 @@ internal static partial class Constants
private const string SummaryAttributeFullyQualifiedName = $"{AttributesNamespace}.{SummaryAttributeName}";
- internal static readonly SourceText SummaryAttributeSourceText;
+ internal static readonly SourceText SummaryAttributeSourceText = CreateSummaryAttributeSourceText();
private static SourceText CreateSummaryAttributeSourceText() => SourceText.From($$"""
{{FileHeader}}
diff --git a/src/GeneratedEndpoints/Common/Constants.cs b/src/GeneratedEndpoints/Common/Constants.cs
index 1c393b6..b170d78 100644
--- a/src/GeneratedEndpoints/Common/Constants.cs
+++ b/src/GeneratedEndpoints/Common/Constants.cs
@@ -2,6 +2,19 @@ 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";
From 697b8521716f24e012f05fe56436221caf08f5e8 Mon Sep 17 00:00:00 2001
From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com>
Date: Fri, 12 Dec 2025 12:37:49 -0500
Subject: [PATCH 3/6] Fix Accepts attribute handling (#78)
---
.../Common/EndpointConfigurationFactory.cs | 11 ++++++++---
.../GeneratedEndpoints.Tests/Common/SourceFactory.cs | 2 +-
...trix_0BE3EC6390D4_MapEndpointHandlers.verified.txt | 1 +
...trix_2F85DF1A025B_MapEndpointHandlers.verified.txt | 1 +
...trix_9F5FE6E1F139_MapEndpointHandlers.verified.txt | 1 +
....AcceptsAttribute_MapEndpointHandlers.verified.txt | 3 ++-
...tipleContentTypes_MapEndpointHandlers.verified.txt | 3 ++-
7 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs b/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs
index f8dccd5..242772a 100644
--- a/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs
+++ b/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs
@@ -207,8 +207,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 +220,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);
diff --git a/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs b/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs
index 035ce8d..a9f928f 100644
--- a/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs
+++ b/tests/GeneratedEndpoints.Tests/Common/SourceFactory.cs
@@ -370,7 +370,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/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.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;
}
From 1f180a1efc8d9cb90869141f89b3fbe583171e9b Mon Sep 17 00:00:00 2001
From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com>
Date: Fri, 12 Dec 2025 12:45:17 -0500
Subject: [PATCH 4/6] Use pooled builders for declaration names (#79)
---
src/GeneratedEndpoints/Common/DeclarationExtensions.cs | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
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)
From da713d5b37629883b3b5b4232676b0d51f0eee24 Mon Sep 17 00:00:00 2001
From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com>
Date: Fri, 12 Dec 2025 13:01:27 -0500
Subject: [PATCH 5/6] Skip instance endpoint generation for abstract classes
(#80)
---
.../AddEndpointHandlersGenerator.cs | 2 +-
.../Common/RequestHandlerClass.cs | 1 +
.../Common/RequestHandlerClassHelper.cs | 2 ++
src/GeneratedEndpoints/MinimalApiGenerator.cs | 3 ++
.../Common/SourceFactory.cs | 14 +++++++++
...eHandlers_AddEndpointHandlers.verified.txt | 23 ++++++++++++++
...eHandlers_MapEndpointHandlers.verified.txt | 30 +++++++++++++++++++
.../IndividualTests.cs | 12 ++++++++
8 files changed, 86 insertions(+), 1 deletion(-)
create mode 100644 tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_AddEndpointHandlers.verified.txt
create mode 100644 tests/GeneratedEndpoints.Tests/IndividualTests.AbstractClassSkipsInstanceHandlers_MapEndpointHandlers.verified.txt
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/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/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 a9f928f..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,
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.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,
From 8888591be500294047db67f4f7347078557e2efa Mon Sep 17 00:00:00 2001
From: Jean-Sebastien Carle <29762210+jscarle@users.noreply.github.com>
Date: Sun, 21 Dec 2025 21:04:10 -0500
Subject: [PATCH 6/6] Bump version.
---
.../Common/EndpointConfigurationFactory.cs | 9 ++-------
src/GeneratedEndpoints/GeneratedEndpoints.csproj | 8 ++++----
.../GeneratedEndpoints.Tests.csproj | 2 +-
3 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs b/src/GeneratedEndpoints/Common/EndpointConfigurationFactory.cs
index 242772a..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,
};
}
@@ -240,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/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/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