Skip to content

Commit 77e5784

Browse files
authored
Remove form support from minimal APIs (#31646)
* Remove form support from minimal APIs * Add * MapActionSample -> MinimalSample * Remove MapActionSample.csproj from Mvc.slnf
1 parent 00b551e commit 77e5784

13 files changed

+46
-267
lines changed

AspNetCore.sln

+15-15
Original file line numberDiff line numberDiff line change
@@ -1600,8 +1600,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWinFormsApp", "src\Co
16001600
EndProject
16011601
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Http.Abstractions.Microbenchmarks", "src\Http\Http.Abstractions\perf\Microbenchmarks\Microsoft.AspNetCore.Http.Abstractions.Microbenchmarks.csproj", "{3F752B48-2936-4FCA-B0DC-4AB0F788F897}"
16021602
EndProject
1603-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapActionSample", "src\Http\samples\MapActionSample\MapActionSample.csproj", "{A661D867-708A-494E-8B6B-6558804F9A3F}"
1604-
EndProject
16051603
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{F0849E7E-61DB-4849-9368-9E7BC125DCB0}"
16061604
EndProject
16071605
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinFormsTestApp", "src\Components\WebView\Platforms\WindowsForms\testassets\WinFormsTestApp\WinFormsTestApp.csproj", "{99EE7769-3C81-477B-B947-0A5CBCD5B27D}"
@@ -1626,6 +1624,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SpaSer
16261624
EndProject
16271625
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.SpaServices.Extensions.Tests", "src\Middleware\Spa\SpaServices.Extensions\test\Microsoft.AspNetCore.SpaServices.Extensions.Tests.csproj", "{AAB50C64-39AA-4AED-8E9C-50D68E7751AD}"
16281626
EndProject
1627+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalSample", "src\Http\samples\MinimalSample\MinimalSample.csproj", "{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}"
1628+
EndProject
16291629
Global
16301630
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16311631
Debug|Any CPU = Debug|Any CPU
@@ -7613,18 +7613,6 @@ Global
76137613
{3F752B48-2936-4FCA-B0DC-4AB0F788F897}.Release|x64.Build.0 = Release|Any CPU
76147614
{3F752B48-2936-4FCA-B0DC-4AB0F788F897}.Release|x86.ActiveCfg = Release|Any CPU
76157615
{3F752B48-2936-4FCA-B0DC-4AB0F788F897}.Release|x86.Build.0 = Release|Any CPU
7616-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
7617-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
7618-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x64.ActiveCfg = Debug|Any CPU
7619-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x64.Build.0 = Debug|Any CPU
7620-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x86.ActiveCfg = Debug|Any CPU
7621-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x86.Build.0 = Debug|Any CPU
7622-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
7623-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Release|Any CPU.Build.0 = Release|Any CPU
7624-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x64.ActiveCfg = Release|Any CPU
7625-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x64.Build.0 = Release|Any CPU
7626-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x86.ActiveCfg = Release|Any CPU
7627-
{A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x86.Build.0 = Release|Any CPU
76287616
{99EE7769-3C81-477B-B947-0A5CBCD5B27D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
76297617
{99EE7769-3C81-477B-B947-0A5CBCD5B27D}.Debug|Any CPU.Build.0 = Debug|Any CPU
76307618
{99EE7769-3C81-477B-B947-0A5CBCD5B27D}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -7709,6 +7697,18 @@ Global
77097697
{AAB50C64-39AA-4AED-8E9C-50D68E7751AD}.Release|x64.Build.0 = Release|Any CPU
77107698
{AAB50C64-39AA-4AED-8E9C-50D68E7751AD}.Release|x86.ActiveCfg = Release|Any CPU
77117699
{AAB50C64-39AA-4AED-8E9C-50D68E7751AD}.Release|x86.Build.0 = Release|Any CPU
7700+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
7701+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|Any CPU.Build.0 = Debug|Any CPU
7702+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x64.ActiveCfg = Debug|Any CPU
7703+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x64.Build.0 = Debug|Any CPU
7704+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x86.ActiveCfg = Debug|Any CPU
7705+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x86.Build.0 = Debug|Any CPU
7706+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|Any CPU.ActiveCfg = Release|Any CPU
7707+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|Any CPU.Build.0 = Release|Any CPU
7708+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x64.ActiveCfg = Release|Any CPU
7709+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x64.Build.0 = Release|Any CPU
7710+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x86.ActiveCfg = Release|Any CPU
7711+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x86.Build.0 = Release|Any CPU
77127712
EndGlobalSection
77137713
GlobalSection(SolutionProperties) = preSolution
77147714
HideSolutionNode = FALSE
@@ -8501,7 +8501,6 @@ Global
85018501
{3BA297F8-1CA1-492D-AE64-A60B825D8501} = {D4E9A2C5-0838-42DF-BC80-C829C4C9137E}
85028502
{CC740832-D268-47A3-9058-B9054F8397E2} = {D3B76F4E-A980-45BF-AEA1-EA3175B0B5A1}
85038503
{3F752B48-2936-4FCA-B0DC-4AB0F788F897} = {DCBBDB52-4A49-4141-8F4D-81C0FFFB7BD5}
8504-
{A661D867-708A-494E-8B6B-6558804F9A3F} = {EB5E294B-9ED5-43BF-AFA9-1CD2327F3DC1}
85058504
{F0849E7E-61DB-4849-9368-9E7BC125DCB0} = {D4E9A2C5-0838-42DF-BC80-C829C4C9137E}
85068505
{99EE7769-3C81-477B-B947-0A5CBCD5B27D} = {F0849E7E-61DB-4849-9368-9E7BC125DCB0}
85078506
{94D0D6F3-8632-41DE-908B-47A787D570FF} = {5241CF68-66A0-4724-9BAA-36DB959A5B11}
@@ -8514,6 +8513,7 @@ Global
85148513
{7F99E967-3DC1-4198-9D55-47CD9471D0B6} = {0A064174-8E5C-4F97-B941-A4E302661DF2}
85158514
{DF4637DA-5F07-4903-8461-4E2DAB235F3C} = {7F99E967-3DC1-4198-9D55-47CD9471D0B6}
85168515
{AAB50C64-39AA-4AED-8E9C-50D68E7751AD} = {7F99E967-3DC1-4198-9D55-47CD9471D0B6}
8516+
{9647D8B7-4616-4E05-B258-BAD5CAEEDD38} = {EB5E294B-9ED5-43BF-AFA9-1CD2327F3DC1}
85178517
EndGlobalSection
85188518
GlobalSection(ExtensibilityGlobals) = postSolution
85198519
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

src/Http/Http.Abstractions/src/Metadata/IFromFormMetadata.cs

-16
This file was deleted.

src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ Microsoft.AspNetCore.Http.IResult
88
Microsoft.AspNetCore.Http.IResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task!
99
Microsoft.AspNetCore.Http.Metadata.IFromBodyMetadata
1010
Microsoft.AspNetCore.Http.Metadata.IFromBodyMetadata.AllowEmpty.get -> bool
11-
Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata
12-
Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string?
1311
Microsoft.AspNetCore.Http.Metadata.IFromHeaderMetadata
1412
Microsoft.AspNetCore.Http.Metadata.IFromHeaderMetadata.Name.get -> string?
1513
Microsoft.AspNetCore.Http.Metadata.IFromQueryMetadata

src/Http/Http.Extensions/src/RequestDelegateFactory.cs

+25-98
Original file line numberDiff line numberDiff line change
@@ -203,48 +203,20 @@ private static Expression CreateArgument(ParameterInfo parameter, FactoryContext
203203
}
204204
else if (parameterCustomAttributes.OfType<IFromBodyMetadata>().FirstOrDefault() is { } bodyAttribute)
205205
{
206-
if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson)
206+
if (factoryContext.JsonRequestBodyType is not null)
207207
{
208208
throw new InvalidOperationException("Action cannot have more than one FromBody attribute.");
209209
}
210210

211-
if (factoryContext.RequestBodyMode is RequestBodyMode.AsForm)
212-
{
213-
ThrowCannotReadBodyDirectlyAndAsForm();
214-
}
215-
216-
factoryContext.RequestBodyMode = RequestBodyMode.AsJson;
217211
factoryContext.JsonRequestBodyType = parameter.ParameterType;
218212
factoryContext.AllowEmptyRequestBody = bodyAttribute.AllowEmpty;
219213

220214
return Expression.Convert(BodyValueExpr, parameter.ParameterType);
221215
}
222-
else if (parameterCustomAttributes.OfType<IFromFormMetadata>().FirstOrDefault() is { } formAttribute)
223-
{
224-
if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson)
225-
{
226-
ThrowCannotReadBodyDirectlyAndAsForm();
227-
}
228-
229-
factoryContext.RequestBodyMode = RequestBodyMode.AsForm;
230-
231-
return BindParameterFromProperty(parameter, FormExpr, formAttribute.Name ?? parameter.Name, factoryContext);
232-
}
233216
else if (parameter.CustomAttributes.Any(a => typeof(IFromServiceMetadata).IsAssignableFrom(a.AttributeType)))
234217
{
235218
return Expression.Call(GetRequiredServiceMethod.MakeGenericMethod(parameter.ParameterType), RequestServicesExpr);
236219
}
237-
else if (parameter.ParameterType == typeof(IFormCollection))
238-
{
239-
if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson)
240-
{
241-
ThrowCannotReadBodyDirectlyAndAsForm();
242-
}
243-
244-
factoryContext.RequestBodyMode = RequestBodyMode.AsForm;
245-
246-
return Expression.Property(HttpRequestExpr, nameof(HttpRequest.Form));
247-
}
248220
else if (parameter.ParameterType == typeof(HttpContext))
249221
{
250222
return HttpContextExpr;
@@ -446,67 +418,41 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
446418

447419
private static Func<object?, HttpContext, Task> HandleRequestBodyAndCompileRequestDelegate(Expression responseWritingMethodCall, FactoryContext factoryContext)
448420
{
449-
if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson)
421+
if (factoryContext.JsonRequestBodyType is null)
450422
{
451-
// We need to generate the code for reading from the body before calling into the delegate
452-
var invoker = Expression.Lambda<Func<object?, HttpContext, object?, Task>>(
453-
responseWritingMethodCall, TargetExpr, HttpContextExpr, BodyValueExpr).Compile();
454-
455-
var bodyType = factoryContext.JsonRequestBodyType!;
456-
object? defaultBodyValue = null;
457-
458-
if (factoryContext.AllowEmptyRequestBody && bodyType.IsValueType)
459-
{
460-
defaultBodyValue = Activator.CreateInstance(bodyType);
461-
}
423+
return Expression.Lambda<Func<object?, HttpContext, Task>>(
424+
responseWritingMethodCall, TargetExpr, HttpContextExpr).Compile();
425+
}
462426

463-
return async (target, httpContext) =>
464-
{
465-
object? bodyValue;
427+
// We need to generate the code for reading from the body before calling into the delegate
428+
var invoker = Expression.Lambda<Func<object?, HttpContext, object?, Task>>(
429+
responseWritingMethodCall, TargetExpr, HttpContextExpr, BodyValueExpr).Compile();
466430

467-
if (factoryContext.AllowEmptyRequestBody && httpContext.Request.ContentLength == 0)
468-
{
469-
bodyValue = defaultBodyValue;
470-
}
471-
else
472-
{
473-
try
474-
{
475-
bodyValue = await httpContext.Request.ReadFromJsonAsync(bodyType);
476-
}
477-
catch (IOException ex)
478-
{
479-
Log.RequestBodyIOException(httpContext, ex);
480-
return;
481-
}
482-
catch (InvalidDataException ex)
483-
{
484-
Log.RequestBodyInvalidDataException(httpContext, ex);
485-
httpContext.Response.StatusCode = 400;
486-
return;
487-
}
488-
}
431+
var bodyType = factoryContext.JsonRequestBodyType!;
432+
object? defaultBodyValue = null;
489433

490-
await invoker(target, httpContext, bodyValue);
491-
};
434+
if (factoryContext.AllowEmptyRequestBody && bodyType.IsValueType)
435+
{
436+
defaultBodyValue = Activator.CreateInstance(bodyType);
492437
}
493-
else if (factoryContext.RequestBodyMode is RequestBodyMode.AsForm)
438+
439+
return async (target, httpContext) =>
494440
{
495-
var invoker = Expression.Lambda<Func<object?, HttpContext, Task>>(
496-
responseWritingMethodCall, TargetExpr, HttpContextExpr).Compile();
441+
object? bodyValue;
497442

498-
return async (target, httpContext) =>
443+
if (factoryContext.AllowEmptyRequestBody && httpContext.Request.ContentLength == 0)
444+
{
445+
bodyValue = defaultBodyValue;
446+
}
447+
else
499448
{
500-
// Generating async code would just be insane so if the method needs the form populate it here
501-
// so the within the method it's cached
502449
try
503450
{
504-
await httpContext.Request.ReadFormAsync();
451+
bodyValue = await httpContext.Request.ReadFromJsonAsync(bodyType);
505452
}
506453
catch (IOException ex)
507454
{
508455
Log.RequestBodyIOException(httpContext, ex);
509-
httpContext.Abort();
510456
return;
511457
}
512458
catch (InvalidDataException ex)
@@ -515,15 +461,10 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
515461
httpContext.Response.StatusCode = 400;
516462
return;
517463
}
464+
}
518465

519-
await invoker(target, httpContext);
520-
};
521-
}
522-
else
523-
{
524-
return Expression.Lambda<Func<object?, HttpContext, Task>>(
525-
responseWritingMethodCall, TargetExpr, HttpContextExpr).Compile();
526-
}
466+
await invoker(target, httpContext, bodyValue);
467+
};
527468
}
528469

529470
private static MethodInfo GetEnumTryParseMethod()
@@ -793,22 +734,8 @@ private static async Task ExecuteTaskResult<T>(Task<T> task, HttpContext httpCon
793734
await (await task).ExecuteAsync(httpContext);
794735
}
795736

796-
[StackTraceHidden]
797-
private static void ThrowCannotReadBodyDirectlyAndAsForm()
798-
{
799-
throw new InvalidOperationException("Action cannot mix FromBody and FromForm on the same method.");
800-
}
801-
802-
private enum RequestBodyMode
803-
{
804-
None,
805-
AsJson,
806-
AsForm,
807-
}
808-
809737
private class FactoryContext
810738
{
811-
public RequestBodyMode RequestBodyMode { get; set; }
812739
public Type? JsonRequestBodyType { get; set; }
813740
public bool AllowEmptyRequestBody { get; set; }
814741

0 commit comments

Comments
 (0)