Skip to content

Commit cf3710e

Browse files
committed
add array tests and fixed emitter for nullable types
1 parent 3f5cb0e commit cf3710e

File tree

2 files changed

+100
-9
lines changed

2 files changed

+100
-9
lines changed

src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Emitters/EndpointParameterEmitter.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ internal static void EmitFormParameterPreparation(this EndpointParameter endpoin
8989

9090
internal static void EmitParsingBlock(this EndpointParameter endpointParameter, CodeWriter codeWriter)
9191
{
92+
// parsable array
9293
if (endpointParameter.IsArray && endpointParameter.IsParsable)
9394
{
9495
var createArray = $"new {endpointParameter.ElementType.ToDisplayString(EmitterConstants.DisplayFormat)}[{endpointParameter.EmitTempArgument()}.Length]";
@@ -105,7 +106,6 @@ internal static void EmitParsingBlock(this EndpointParameter endpointParameter,
105106
codeWriter.StartBlock();
106107
codeWriter.WriteLine("if (!string.IsNullOrEmpty(element))");
107108
codeWriter.StartBlock();
108-
codeWriter.WriteLine("wasParamCheckFailure = true;");
109109
EmitLogOrThrowException(endpointParameter, codeWriter, "element");
110110
codeWriter.EndBlock();
111111
codeWriter.EndBlock();
@@ -128,22 +128,24 @@ internal static void EmitParsingBlock(this EndpointParameter endpointParameter,
128128
}
129129
codeWriter.EndBlock();
130130
}
131-
else if (endpointParameter.IsArray && !endpointParameter.IsParsable)
131+
// array fallback
132+
else if (endpointParameter.IsArray)
132133
{
133134
codeWriter.WriteLine($"{endpointParameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitTempArgument()}!;");
134135
}
135-
else if (!endpointParameter.IsArray && endpointParameter.IsParsable)
136+
// parsable single
137+
else if (endpointParameter.IsParsable)
136138
{
137139
var temp_argument = endpointParameter.EmitTempArgument();
138140
var output_argument = endpointParameter.EmitParsedTempArgument();
139141

140142
//endpointParameter.ParsingBlockEmitter(codeWriter, endpointParameter.EmitTempArgument(), endpointParameter.EmitParsedTempArgument());
141-
if (endpointParameter.IsOptional)
143+
if (endpointParameter.IsOptional || endpointParameter.Type.NullableAnnotation == NullableAnnotation.Annotated)
142144
{
143145
var parameterType = endpointParameter.Type.UnwrapTypeSymbol(unwrapArray: true, unwrapNullable: true);
144146
var temp_argument_parsed_non_nullable = $"{temp_argument}_parsed_non_nullable";
145147

146-
codeWriter.WriteLine($"""{parameterType.ToDisplayString(EmitterConstants.DisplayFormat)} {output_argument} = default;""");
148+
codeWriter.WriteLine($"""{endpointParameter.Type.ToDisplayString(EmitterConstants.DisplayFormat)} {output_argument} = default;""");
147149
codeWriter.WriteLine($"""if ({endpointParameter.PreferredTryParseInvocation(temp_argument, temp_argument_parsed_non_nullable)})""");
148150
codeWriter.StartBlock();
149151
codeWriter.WriteLine($"""{output_argument} = {temp_argument_parsed_non_nullable};""");
@@ -153,25 +155,25 @@ internal static void EmitParsingBlock(this EndpointParameter endpointParameter,
153155
codeWriter.WriteLine($"""{output_argument} = {endpointParameter.DefaultValue};""");
154156
codeWriter.EndBlock();
155157
codeWriter.WriteLine("else");
156-
codeWriter.EndBlock();
157-
codeWriter.WriteLine("wasParamCheckFailure = true;");
158158
codeWriter.StartBlock();
159+
codeWriter.WriteLine("wasParamCheckFailure = true;");
160+
codeWriter.EndBlock();
159161
}
160162
else
161163
{
162164
codeWriter.WriteLine($$"""if (!{{endpointParameter.PreferredTryParseInvocation(temp_argument, output_argument)}})""");
163165
codeWriter.StartBlock();
164166
codeWriter.WriteLine($"if (!string.IsNullOrEmpty({temp_argument}))");
165167
codeWriter.StartBlock();
166-
codeWriter.WriteLine("wasParamCheckFailure = true;");
167168
EmitLogOrThrowException(endpointParameter, codeWriter, temp_argument);
168169
codeWriter.EndBlock();
169170
codeWriter.EndBlock();
170171
}
171172

172173
codeWriter.WriteLine($"{endpointParameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitParsedTempArgument()}!;");
173174
}
174-
else // Not parsable, not an array.
175+
// Not parsable, not an array.
176+
else
175177
{
176178
codeWriter.WriteLine($"{endpointParameter.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {endpointParameter.EmitHandlerArgument()} = {endpointParameter.EmitTempArgument()}!;");
177179
}
@@ -180,11 +182,13 @@ static void EmitLogOrThrowException(EndpointParameter parameter, CodeWriter writ
180182
{
181183
if (parameter.IsArray && parameter.ElementType.NullableAnnotation == NullableAnnotation.Annotated)
182184
{
185+
writer.WriteLine("wasParamCheckFailure = true;");
183186
writer.WriteLine($@"logOrThrowExceptionHelper.RequiredParameterNotProvided({SymbolDisplay.FormatLiteral(parameter.Type.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat), true)}, {SymbolDisplay.FormatLiteral(parameter.SymbolName, true)}, {SymbolDisplay.FormatLiteral(parameter.ToMessageString(), true)});");
184187
}
185188
else
186189
{
187190
writer.WriteLine($@"logOrThrowExceptionHelper.ParameterBindingFailed({SymbolDisplay.FormatLiteral(parameter.Type.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat), true)}, {SymbolDisplay.FormatLiteral(parameter.SymbolName, true)}, {inputArgument});");
191+
writer.WriteLine("wasParamCheckFailure = true;");
188192
}
189193
}
190194
}

src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateCreationTests.Arrays.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Text.Json;
1111
using Microsoft.AspNetCore.Http.Features;
1212
using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel;
13+
using Microsoft.Extensions.DependencyInjection;
1314
using Microsoft.Extensions.Primitives;
1415
using Microsoft.VisualStudio.TestPlatform.Common;
1516
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
@@ -325,6 +326,92 @@ public async Task MapAction_ImplicitQuery_NullableStringArrayParam()
325326
await VerifyAgainstBaselineUsingFile(compilation);
326327
}
327328

329+
[Fact]
330+
public async Task MapAction_ImplicitQuery_OptionalIntArrayParam()
331+
{
332+
var (results, compilation) = await RunGeneratorAsync("""
333+
app.MapGet("/hello", (int[]? p = null) => p?.Length ?? 0);
334+
""");
335+
336+
var endpoint = GetEndpointFromCompilation(compilation);
337+
338+
VerifyStaticEndpointModel(results, endpointModel =>
339+
{
340+
Assert.Equal("MapGet", endpointModel.HttpMethod);
341+
});
342+
343+
var httpContext = CreateHttpContext();
344+
httpContext.Request.QueryString = new QueryString("?p=0&p=1&p=2");
345+
346+
await endpoint.RequestDelegate(httpContext);
347+
await VerifyResponseBodyAsync(httpContext, "3");
348+
//await VerifyAgainstBaselineUsingFile(compilation);
349+
}
350+
351+
[Fact]
352+
public async Task MapAction_ImplicitQuery_OptionalIntArrayParam_EmptyValues()
353+
{
354+
var (results, compilation) = await RunGeneratorAsync("""
355+
app.MapGet("/hello", (int[]? p = null) => p?.Length ?? 0);
356+
""");
357+
358+
var endpoint = GetEndpointFromCompilation(compilation);
359+
360+
VerifyStaticEndpointModel(results, endpointModel =>
361+
{
362+
Assert.Equal("MapGet", endpointModel.HttpMethod);
363+
});
364+
365+
var httpContext = CreateHttpContext();
366+
367+
await endpoint.RequestDelegate(httpContext);
368+
await VerifyResponseBodyAsync(httpContext, "0");
369+
//await VerifyAgainstBaselineUsingFile(compilation);
370+
}
371+
372+
[Fact]
373+
public async Task MapAction_ExplicitQuery_OptionalIntArrayParam()
374+
{
375+
var (results, compilation) = await RunGeneratorAsync("""
376+
app.MapGet("/hello", ([FromQuery] int[]? p = null) => p?.Length ?? 0);
377+
""");
378+
379+
var endpoint = GetEndpointFromCompilation(compilation);
380+
381+
VerifyStaticEndpointModel(results, endpointModel =>
382+
{
383+
Assert.Equal("MapGet", endpointModel.HttpMethod);
384+
});
385+
386+
var httpContext = CreateHttpContext();
387+
httpContext.Request.QueryString = new QueryString("?p=0&p=1&p=2");
388+
389+
await endpoint.RequestDelegate(httpContext);
390+
await VerifyResponseBodyAsync(httpContext, "3");
391+
//await VerifyAgainstBaselineUsingFile(compilation);
392+
}
393+
394+
[Fact]
395+
public async Task MapAction_ExplicitQuery_OptionalIntArrayParam_EmptyValues()
396+
{
397+
var (results, compilation) = await RunGeneratorAsync("""
398+
app.MapGet("/hello", ([FromQuery] int[]? p = null) => p?.Length ?? 0);
399+
""");
400+
401+
var endpoint = GetEndpointFromCompilation(compilation);
402+
403+
VerifyStaticEndpointModel(results, endpointModel =>
404+
{
405+
Assert.Equal("MapGet", endpointModel.HttpMethod);
406+
});
407+
408+
var httpContext = CreateHttpContext();
409+
410+
await endpoint.RequestDelegate(httpContext);
411+
await VerifyResponseBodyAsync(httpContext, "0");
412+
//await VerifyAgainstBaselineUsingFile(compilation);
413+
}
414+
328415
[Fact]
329416
public async Task MapPost_WithArrayQueryString_ShouldFail()
330417
{

0 commit comments

Comments
 (0)