diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index c51e7aae5a36..9b0c6d21bdf8 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -1986,6 +1986,15 @@ private static Expression CreateDefaultValueExpression(object? defaultValue, Typ { if (defaultValue is null) { + // For non-nullable value types (e.g., Guid, DateTime, TimeSpan), reflection reports + // DefaultValue == null when the parameter default is `= default`. Using + // Expression.Constant(null, valueType) would throw an ArgumentException because null + // is not valid for a non-nullable value type. Use Expression.Default instead. + if (parameterType.IsValueType && Nullable.GetUnderlyingType(parameterType) is null) + { + return Expression.Default(parameterType); + } + return Expression.Constant(null, parameterType); } diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs index 41dde7279e17..414dee57e5b1 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.QueryParameters.cs @@ -139,6 +139,52 @@ public async Task MapAction_ExplicitParsableParameter_StringReturn() await VerifyResponseBodyAsync(httpContext, "10"); } + [Theory] + [InlineData(null, "Id: 00000000-0000-0000-0000-000000000000")] + [InlineData("a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b", "Id: a0e1f2b3-c4d5-4e6f-7a8b-9c0d1e2f3a4b")] + public async Task CanSetGuidParamAsOptionalWithDefaultValue(string queryValue, string expectedResponse) + { + var (_, compilation) = await RunGeneratorAsync(""" +app.MapGet("/", ([FromQuery] Guid id = default) => $"Id: {id}"); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + var httpContext = CreateHttpContext(); + if (queryValue is not null) + { + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["id"] = queryValue + }); + } + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, expectedResponse); + } + + [Theory] + [InlineData(null, "Date: 0001-01-01T00:00:00.0000000")] + [InlineData("2024-01-15", "Date: 2024-01-15T00:00:00.0000000")] + public async Task CanSetDateTimeParamAsOptionalWithDefaultValue(string queryValue, string expectedResponse) + { + var (_, compilation) = await RunGeneratorAsync(""" +app.MapGet("/", ([FromQuery] DateTime date = default) => $"Date: {date:O}"); +"""); + var endpoint = GetEndpointFromCompilation(compilation); + + var httpContext = CreateHttpContext(); + if (queryValue is not null) + { + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["date"] = queryValue + }); + } + + await endpoint.RequestDelegate(httpContext); + await VerifyResponseBodyAsync(httpContext, expectedResponse); + } + public static object[][] MapAction_ExplicitQueryParam_NameTest_Data { get