Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions src/GeneratedEndpoints/MinimalApiGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public sealed class MinimalApiGenerator : IIncrementalGenerator
private const string DescriptionAttributeNamedParameter = "Description";
private const string ResponseTypeAttributeNamedParameter = "ResponseType";
private const string RequestTypeAttributeNamedParameter = "RequestType";
private const string IsOptionalAttributeNamedParameter = "IsOptional";

private const string RequireAuthorizationAttributeName = "RequireAuthorizationAttribute";
private const string RequireAuthorizationAttributeFullyQualifiedName = $"{AttributesNamespace}.{RequireAuthorizationAttributeName}";
Expand Down Expand Up @@ -284,6 +285,11 @@ internal sealed class {{AcceptsAttributeName}} : global::System.Attribute
/// </summary>
public global::System.Type RequestType { get; init; } = default!;

/// <summary>
/// Gets a value indicating whether the request body is optional.
/// </summary>
public bool IsOptional { get; init; }

/// <summary>
/// Gets the primary content type accepted by the endpoint.
/// </summary>
Expand Down Expand Up @@ -318,6 +324,11 @@ internal sealed class {{AcceptsAttributeName}}<TRequest> : global::System.Attrib
/// </summary>
public global::System.Type RequestType => typeof(TRequest);

/// <summary>
/// Gets a value indicating whether the request body is optional.
/// </summary>
public bool IsOptional { get; init; }

/// <summary>
/// Gets the primary content type accepted by the endpoint.
/// </summary>
Expand Down Expand Up @@ -915,6 +926,7 @@ private static void TryAddAcceptsMetadata(
string? requestType = null;
string contentType;
EquatableImmutableArray<string>? additionalContentTypes;
var isOptional = GetNamedBoolValue(attribute, IsOptionalAttributeNamedParameter);

if (attributeClass.IsGenericType && attributeClass.TypeArguments.Length == 1)
{
Expand Down Expand Up @@ -942,7 +954,7 @@ private static void TryAddAcceptsMetadata(
}

var acceptsList = accepts ??= new List<AcceptsMetadata>();
acceptsList.Add(new AcceptsMetadata(requestType, contentType, additionalContentTypes));
acceptsList.Add(new AcceptsMetadata(requestType, contentType, additionalContentTypes, isOptional));
}

private static void TryAddProducesMetadata(
Expand Down Expand Up @@ -1001,6 +1013,17 @@ private static void TryAddProducesMetadata(
return null;
}

private static bool GetNamedBoolValue(AttributeData attribute, string namedParameter, bool defaultValue = false)
{
foreach (var namedArg in attribute.NamedArguments)
{
if (namedArg.Key == namedParameter && namedArg.Value.Value is bool boolValue)
return boolValue;
}

return defaultValue;
}

private static EquatableImmutableArray<string> MergeUnion(EquatableImmutableArray<string>? existing, IEnumerable<string> values)
{
var list = new List<string>();
Expand Down Expand Up @@ -1510,6 +1533,10 @@ private static void GenerateMapRequestHandler(StringBuilder source, RequestHandl
source.Append(accepts.RequestType);
source.Append('>');
source.Append('(');
if (accepts.IsOptional)
{
source.Append("isOptional: true, ");
}
source.Append(StringLiteral(accepts.ContentType));
AppendAdditionalContentTypes(source, accepts.AdditionalContentTypes);
source.Append(')');
Expand Down Expand Up @@ -1660,7 +1687,8 @@ private static StringBuilder GetUseEndpointHandlersStringBuilder(ImmutableArray<
var additionalCost = accepts.AdditionalContentTypes is { Count: > 0 }
? accepts.AdditionalContentTypes.Value.Sum(ct => 6 + ct.Length)
: 0;
cost += 32 + accepts.RequestType.Length + accepts.ContentType.Length + additionalCost;
var optionalCost = accepts.IsOptional ? 20 : 0;
cost += 32 + optionalCost + accepts.RequestType.Length + accepts.ContentType.Length + additionalCost;
}
}

Expand Down Expand Up @@ -1902,7 +1930,11 @@ private readonly record struct RequestHandlerMetadata(
bool ExcludeFromDescription
);

private readonly record struct AcceptsMetadata(string RequestType, string ContentType, EquatableImmutableArray<string>? AdditionalContentTypes);
private readonly record struct AcceptsMetadata(
string RequestType,
string ContentType,
EquatableImmutableArray<string>? AdditionalContentTypes,
bool IsOptional);

private readonly record struct ProducesMetadata(
string ResponseType,
Expand Down
Loading