From 77e578438bff3c01e4818a85d1123522e1521c20 Mon Sep 17 00:00:00 2001 From: Stephen Halter <halter73@gmail.com> Date: Fri, 9 Apr 2021 15:31:37 -0700 Subject: [PATCH] Remove form support from minimal APIs (#31646) * Remove form support from minimal APIs * Add * MapActionSample -> MinimalSample * Remove MapActionSample.csproj from Mvc.slnf --- AspNetCore.sln | 30 ++-- .../src/Metadata/IFromFormMetadata.cs | 16 --- .../src/PublicAPI.Unshipped.txt | 2 - .../src/RequestDelegateFactory.cs | 123 ++++------------ .../test/RequestDelegateFactoryTests.cs | 133 +----------------- src/Http/HttpAbstractions.slnf | 2 +- .../MinimalSample.csproj} | 0 .../Program.cs | 4 +- .../Properties/launchSettings.json | 0 .../appsettings.Development.json | 0 .../appsettings.json | 0 src/Mvc/Mvc.Core/src/FromFormAttribute.cs | 2 +- src/Mvc/Mvc.slnf | 1 - 13 files changed, 46 insertions(+), 267 deletions(-) delete mode 100644 src/Http/Http.Abstractions/src/Metadata/IFromFormMetadata.cs rename src/Http/samples/{MapActionSample/MapActionSample.csproj => MinimalSample/MinimalSample.csproj} (100%) rename src/Http/samples/{MapActionSample => MinimalSample}/Program.cs (86%) rename src/Http/samples/{MapActionSample => MinimalSample}/Properties/launchSettings.json (100%) rename src/Http/samples/{MapActionSample => MinimalSample}/appsettings.Development.json (100%) rename src/Http/samples/{MapActionSample => MinimalSample}/appsettings.json (100%) diff --git a/AspNetCore.sln b/AspNetCore.sln index 8160c08a5c5..81801038c95 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1600,8 +1600,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWinFormsApp", "src\Co EndProject 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}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapActionSample", "src\Http\samples\MapActionSample\MapActionSample.csproj", "{A661D867-708A-494E-8B6B-6558804F9A3F}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{F0849E7E-61DB-4849-9368-9E7BC125DCB0}" EndProject 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 EndProject 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}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalSample", "src\Http\samples\MinimalSample\MinimalSample.csproj", "{9647D8B7-4616-4E05-B258-BAD5CAEEDD38}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -7613,18 +7613,6 @@ Global {3F752B48-2936-4FCA-B0DC-4AB0F788F897}.Release|x64.Build.0 = Release|Any CPU {3F752B48-2936-4FCA-B0DC-4AB0F788F897}.Release|x86.ActiveCfg = Release|Any CPU {3F752B48-2936-4FCA-B0DC-4AB0F788F897}.Release|x86.Build.0 = Release|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x64.ActiveCfg = Debug|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x64.Build.0 = Debug|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x86.ActiveCfg = Debug|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Debug|x86.Build.0 = Debug|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Release|Any CPU.Build.0 = Release|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x64.ActiveCfg = Release|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x64.Build.0 = Release|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x86.ActiveCfg = Release|Any CPU - {A661D867-708A-494E-8B6B-6558804F9A3F}.Release|x86.Build.0 = Release|Any CPU {99EE7769-3C81-477B-B947-0A5CBCD5B27D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {99EE7769-3C81-477B-B947-0A5CBCD5B27D}.Debug|Any CPU.Build.0 = Debug|Any CPU {99EE7769-3C81-477B-B947-0A5CBCD5B27D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -7709,6 +7697,18 @@ Global {AAB50C64-39AA-4AED-8E9C-50D68E7751AD}.Release|x64.Build.0 = Release|Any CPU {AAB50C64-39AA-4AED-8E9C-50D68E7751AD}.Release|x86.ActiveCfg = Release|Any CPU {AAB50C64-39AA-4AED-8E9C-50D68E7751AD}.Release|x86.Build.0 = Release|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x64.ActiveCfg = Debug|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x64.Build.0 = Debug|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x86.ActiveCfg = Debug|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Debug|x86.Build.0 = Debug|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|Any CPU.Build.0 = Release|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x64.ActiveCfg = Release|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x64.Build.0 = Release|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x86.ActiveCfg = Release|Any CPU + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -8501,7 +8501,6 @@ Global {3BA297F8-1CA1-492D-AE64-A60B825D8501} = {D4E9A2C5-0838-42DF-BC80-C829C4C9137E} {CC740832-D268-47A3-9058-B9054F8397E2} = {D3B76F4E-A980-45BF-AEA1-EA3175B0B5A1} {3F752B48-2936-4FCA-B0DC-4AB0F788F897} = {DCBBDB52-4A49-4141-8F4D-81C0FFFB7BD5} - {A661D867-708A-494E-8B6B-6558804F9A3F} = {EB5E294B-9ED5-43BF-AFA9-1CD2327F3DC1} {F0849E7E-61DB-4849-9368-9E7BC125DCB0} = {D4E9A2C5-0838-42DF-BC80-C829C4C9137E} {99EE7769-3C81-477B-B947-0A5CBCD5B27D} = {F0849E7E-61DB-4849-9368-9E7BC125DCB0} {94D0D6F3-8632-41DE-908B-47A787D570FF} = {5241CF68-66A0-4724-9BAA-36DB959A5B11} @@ -8514,6 +8513,7 @@ Global {7F99E967-3DC1-4198-9D55-47CD9471D0B6} = {0A064174-8E5C-4F97-B941-A4E302661DF2} {DF4637DA-5F07-4903-8461-4E2DAB235F3C} = {7F99E967-3DC1-4198-9D55-47CD9471D0B6} {AAB50C64-39AA-4AED-8E9C-50D68E7751AD} = {7F99E967-3DC1-4198-9D55-47CD9471D0B6} + {9647D8B7-4616-4E05-B258-BAD5CAEEDD38} = {EB5E294B-9ED5-43BF-AFA9-1CD2327F3DC1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/src/Http/Http.Abstractions/src/Metadata/IFromFormMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IFromFormMetadata.cs deleted file mode 100644 index 054628d5006..00000000000 --- a/src/Http/Http.Abstractions/src/Metadata/IFromFormMetadata.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.AspNetCore.Http.Metadata -{ - /// <summary> - /// Interface marking attributes that specify a parameter should be bound using form-data in the request body. - /// </summary> - public interface IFromFormMetadata - { - /// <summary> - /// The form field name. - /// </summary> - string? Name { get; } - } -} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 28f8d0317d6..4ff4474b6bf 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -8,8 +8,6 @@ Microsoft.AspNetCore.Http.IResult Microsoft.AspNetCore.Http.IResult.ExecuteAsync(Microsoft.AspNetCore.Http.HttpContext! httpContext) -> System.Threading.Tasks.Task! Microsoft.AspNetCore.Http.Metadata.IFromBodyMetadata Microsoft.AspNetCore.Http.Metadata.IFromBodyMetadata.AllowEmpty.get -> bool -Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata -Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string? Microsoft.AspNetCore.Http.Metadata.IFromHeaderMetadata Microsoft.AspNetCore.Http.Metadata.IFromHeaderMetadata.Name.get -> string? Microsoft.AspNetCore.Http.Metadata.IFromQueryMetadata diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index d96cddbdfc6..d21b481b7e6 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -203,48 +203,20 @@ namespace Microsoft.AspNetCore.Http } else if (parameterCustomAttributes.OfType<IFromBodyMetadata>().FirstOrDefault() is { } bodyAttribute) { - if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson) + if (factoryContext.JsonRequestBodyType is not null) { throw new InvalidOperationException("Action cannot have more than one FromBody attribute."); } - if (factoryContext.RequestBodyMode is RequestBodyMode.AsForm) - { - ThrowCannotReadBodyDirectlyAndAsForm(); - } - - factoryContext.RequestBodyMode = RequestBodyMode.AsJson; factoryContext.JsonRequestBodyType = parameter.ParameterType; factoryContext.AllowEmptyRequestBody = bodyAttribute.AllowEmpty; return Expression.Convert(BodyValueExpr, parameter.ParameterType); } - else if (parameterCustomAttributes.OfType<IFromFormMetadata>().FirstOrDefault() is { } formAttribute) - { - if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson) - { - ThrowCannotReadBodyDirectlyAndAsForm(); - } - - factoryContext.RequestBodyMode = RequestBodyMode.AsForm; - - return BindParameterFromProperty(parameter, FormExpr, formAttribute.Name ?? parameter.Name, factoryContext); - } else if (parameter.CustomAttributes.Any(a => typeof(IFromServiceMetadata).IsAssignableFrom(a.AttributeType))) { return Expression.Call(GetRequiredServiceMethod.MakeGenericMethod(parameter.ParameterType), RequestServicesExpr); } - else if (parameter.ParameterType == typeof(IFormCollection)) - { - if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson) - { - ThrowCannotReadBodyDirectlyAndAsForm(); - } - - factoryContext.RequestBodyMode = RequestBodyMode.AsForm; - - return Expression.Property(HttpRequestExpr, nameof(HttpRequest.Form)); - } else if (parameter.ParameterType == typeof(HttpContext)) { return HttpContextExpr; @@ -446,67 +418,41 @@ namespace Microsoft.AspNetCore.Http private static Func<object?, HttpContext, Task> HandleRequestBodyAndCompileRequestDelegate(Expression responseWritingMethodCall, FactoryContext factoryContext) { - if (factoryContext.RequestBodyMode is RequestBodyMode.AsJson) + if (factoryContext.JsonRequestBodyType is null) { - // We need to generate the code for reading from the body before calling into the delegate - var invoker = Expression.Lambda<Func<object?, HttpContext, object?, Task>>( - responseWritingMethodCall, TargetExpr, HttpContextExpr, BodyValueExpr).Compile(); - - var bodyType = factoryContext.JsonRequestBodyType!; - object? defaultBodyValue = null; - - if (factoryContext.AllowEmptyRequestBody && bodyType.IsValueType) - { - defaultBodyValue = Activator.CreateInstance(bodyType); - } + return Expression.Lambda<Func<object?, HttpContext, Task>>( + responseWritingMethodCall, TargetExpr, HttpContextExpr).Compile(); + } - return async (target, httpContext) => - { - object? bodyValue; + // We need to generate the code for reading from the body before calling into the delegate + var invoker = Expression.Lambda<Func<object?, HttpContext, object?, Task>>( + responseWritingMethodCall, TargetExpr, HttpContextExpr, BodyValueExpr).Compile(); - if (factoryContext.AllowEmptyRequestBody && httpContext.Request.ContentLength == 0) - { - bodyValue = defaultBodyValue; - } - else - { - try - { - bodyValue = await httpContext.Request.ReadFromJsonAsync(bodyType); - } - catch (IOException ex) - { - Log.RequestBodyIOException(httpContext, ex); - return; - } - catch (InvalidDataException ex) - { - Log.RequestBodyInvalidDataException(httpContext, ex); - httpContext.Response.StatusCode = 400; - return; - } - } + var bodyType = factoryContext.JsonRequestBodyType!; + object? defaultBodyValue = null; - await invoker(target, httpContext, bodyValue); - }; + if (factoryContext.AllowEmptyRequestBody && bodyType.IsValueType) + { + defaultBodyValue = Activator.CreateInstance(bodyType); } - else if (factoryContext.RequestBodyMode is RequestBodyMode.AsForm) + + return async (target, httpContext) => { - var invoker = Expression.Lambda<Func<object?, HttpContext, Task>>( - responseWritingMethodCall, TargetExpr, HttpContextExpr).Compile(); + object? bodyValue; - return async (target, httpContext) => + if (factoryContext.AllowEmptyRequestBody && httpContext.Request.ContentLength == 0) + { + bodyValue = defaultBodyValue; + } + else { - // Generating async code would just be insane so if the method needs the form populate it here - // so the within the method it's cached try { - await httpContext.Request.ReadFormAsync(); + bodyValue = await httpContext.Request.ReadFromJsonAsync(bodyType); } catch (IOException ex) { Log.RequestBodyIOException(httpContext, ex); - httpContext.Abort(); return; } catch (InvalidDataException ex) @@ -515,15 +461,10 @@ namespace Microsoft.AspNetCore.Http httpContext.Response.StatusCode = 400; return; } + } - await invoker(target, httpContext); - }; - } - else - { - return Expression.Lambda<Func<object?, HttpContext, Task>>( - responseWritingMethodCall, TargetExpr, HttpContextExpr).Compile(); - } + await invoker(target, httpContext, bodyValue); + }; } private static MethodInfo GetEnumTryParseMethod() @@ -793,22 +734,8 @@ namespace Microsoft.AspNetCore.Http await (await task).ExecuteAsync(httpContext); } - [StackTraceHidden] - private static void ThrowCannotReadBodyDirectlyAndAsForm() - { - throw new InvalidOperationException("Action cannot mix FromBody and FromForm on the same method."); - } - - private enum RequestBodyMode - { - None, - AsJson, - AsForm, - } - private class FactoryContext { - public RequestBodyMode RequestBodyMode { get; set; } public Type? JsonRequestBodyType { get; set; } public bool AllowEmptyRequestBody { get; set; } diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs index 80e4532746f..79ece71eddd 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs @@ -400,6 +400,7 @@ namespace Microsoft.AspNetCore.Routing.Internal var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues["tryParsable"] = "42"; + httpContext.Request.Query = new QueryCollection(new Dictionary<string, StringValues> { ["tryParsable"] = "invalid!" @@ -422,14 +423,12 @@ namespace Microsoft.AspNetCore.Routing.Internal void InvalidFromRoute([FromRoute] object notTryParsable) { } void InvalidFromQuery([FromQuery] object notTryParsable) { } void InvalidFromHeader([FromHeader] object notTryParsable) { } - void InvalidFromForm([FromForm] object notTryParsable) { } return new[] { new object[] { (Action<object>)InvalidFromRoute }, new object[] { (Action<object>)InvalidFromQuery }, new object[] { (Action<object>)InvalidFromHeader }, - new object[] { (Action<object>)InvalidFromForm }, }; } } @@ -700,111 +699,6 @@ namespace Microsoft.AspNetCore.Routing.Internal Assert.Same(invalidDataException, logMessage.Exception); } - [Fact] - public async Task RequestDelegatePopulatesFromFormParameterBasedOnParameterName() - { - const string paramName = "value"; - const int originalQueryParam = 42; - - int? deserializedRouteParam = null; - - void TestAction([FromForm] int value) - { - deserializedRouteParam = value; - } - - var form = new FormCollection(new Dictionary<string, StringValues>() - { - [paramName] = originalQueryParam.ToString(NumberFormatInfo.InvariantInfo) - }); - - var httpContext = new DefaultHttpContext(); - httpContext.Request.Form = form; - - var requestDelegate = RequestDelegateFactory.Create((Action<int>)TestAction); - - await requestDelegate(httpContext); - - Assert.Equal(originalQueryParam, deserializedRouteParam); - } - - [Fact] - public async Task RequestDelegateLogsFromFormIOExceptionsAsDebugAndAborts() - { - var invoked = false; - - void TestAction([FromForm] int value) - { - invoked = true; - } - - var ioException = new IOException(); - var serviceCollection = new ServiceCollection(); - serviceCollection.AddSingleton(LoggerFactory); - - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded"; - httpContext.Request.Body = new IOExceptionThrowingRequestBodyStream(ioException); - httpContext.Features.Set<IHttpRequestLifetimeFeature>(new TestHttpRequestLifetimeFeature()); - httpContext.RequestServices = serviceCollection.BuildServiceProvider(); - - var requestDelegate = RequestDelegateFactory.Create((Action<int>)TestAction); - - await requestDelegate(httpContext); - - Assert.False(invoked); - Assert.True(httpContext.RequestAborted.IsCancellationRequested); - - var logMessage = Assert.Single(TestSink.Writes); - Assert.Equal(new EventId(1, "RequestBodyIOException"), logMessage.EventId); - Assert.Equal(LogLevel.Debug, logMessage.LogLevel); - Assert.Same(ioException, logMessage.Exception); - } - - [Fact] - public async Task RequestDelegateLogsFromFormInvalidDataExceptionsAsDebugAndSets400Response() - { - var invoked = false; - - void TestAction([FromForm] int value) - { - invoked = true; - } - - var invalidDataException = new InvalidDataException(); - var serviceCollection = new ServiceCollection(); - serviceCollection.AddSingleton(LoggerFactory); - - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded"; - httpContext.Request.Body = new IOExceptionThrowingRequestBodyStream(invalidDataException); - httpContext.Features.Set<IHttpRequestLifetimeFeature>(new TestHttpRequestLifetimeFeature()); - httpContext.RequestServices = serviceCollection.BuildServiceProvider(); - - var requestDelegate = RequestDelegateFactory.Create((Action<int>)TestAction); - - await requestDelegate(httpContext); - - Assert.False(invoked); - Assert.False(httpContext.RequestAborted.IsCancellationRequested); - Assert.Equal(400, httpContext.Response.StatusCode); - - var logMessage = Assert.Single(TestSink.Writes); - Assert.Equal(new EventId(2, "RequestBodyInvalidDataException"), logMessage.EventId); - Assert.Equal(LogLevel.Debug, logMessage.LogLevel); - Assert.Same(invalidDataException, logMessage.Exception); - } - - [Fact] - public void BuildRequestDelegateThrowsInvalidOperationExceptionGivenBothFromBodyAndFromFormOnDifferentParameters() - { - void TestAction([FromBody] int value1, [FromForm] int value2) { } - void TestActionWithFlippedParams([FromForm] int value1, [FromBody] int value2) { } - - Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create((Action<int, int>)TestAction)); - Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create((Action<int, int>)TestActionWithFlippedParams)); - } - [Fact] public void BuildRequestDelegateThrowsInvalidOperationExceptionGivenFromBodyOnMultipleParameters() { @@ -885,26 +779,6 @@ namespace Microsoft.AspNetCore.Routing.Internal Assert.Same(httpContext, httpContextArgument); } - [Fact] - public async Task RequestDelegatePopulatesIFormCollectionParameterWithoutAttribute() - { - IFormCollection? formCollectionArgument = null; - - void TestAction(IFormCollection httpContext) - { - formCollectionArgument = httpContext; - } - - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded"; - - var requestDelegate = RequestDelegateFactory.Create((Action<IFormCollection>)TestAction); - - await requestDelegate(httpContext); - - Assert.Same(httpContext.Request.Form, formCollectionArgument); - } - [Fact] public async Task RequestDelegatePassHttpContextRequestAbortedAsCancelationToken() { @@ -1180,11 +1054,6 @@ namespace Microsoft.AspNetCore.Routing.Internal public bool AllowEmpty { get; set; } } - private class FromFormAttribute : Attribute, IFromFormMetadata - { - public string? Name { get; set; } - } - private class FromServiceAttribute : Attribute, IFromServiceMetadata { } diff --git a/src/Http/HttpAbstractions.slnf b/src/Http/HttpAbstractions.slnf index 6fad000bf86..2b9c64391b8 100644 --- a/src/Http/HttpAbstractions.slnf +++ b/src/Http/HttpAbstractions.slnf @@ -36,7 +36,7 @@ "src\\Http\\WebUtilities\\perf\\Microbenchmarks\\Microsoft.AspNetCore.WebUtilities.Microbenchmarks.csproj", "src\\Http\\WebUtilities\\src\\Microsoft.AspNetCore.WebUtilities.csproj", "src\\Http\\WebUtilities\\test\\Microsoft.AspNetCore.WebUtilities.Tests.csproj", - "src\\Http\\samples\\MapActionSample\\MapActionSample.csproj", + "src\\Http\\samples\\MinimalSample\\MinimalSample.csproj", "src\\Http\\samples\\SampleApp\\HttpAbstractions.SampleApp.csproj", "src\\Middleware\\CORS\\src\\Microsoft.AspNetCore.Cors.csproj", "src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj", diff --git a/src/Http/samples/MapActionSample/MapActionSample.csproj b/src/Http/samples/MinimalSample/MinimalSample.csproj similarity index 100% rename from src/Http/samples/MapActionSample/MapActionSample.csproj rename to src/Http/samples/MinimalSample/MinimalSample.csproj diff --git a/src/Http/samples/MapActionSample/Program.cs b/src/Http/samples/MinimalSample/Program.cs similarity index 86% rename from src/Http/samples/MapActionSample/Program.cs rename to src/Http/samples/MinimalSample/Program.cs index 1df9c761fcb..40697fdc666 100644 --- a/src/Http/samples/MapActionSample/Program.cs +++ b/src/Http/samples/MinimalSample/Program.cs @@ -22,8 +22,10 @@ using var host = Host.CreateDefaultBuilder(args) object Json() => new { message = "Hello, World!" }; endpoints.MapGet("/json", (Func<object>)Json); - }); + string SayHello(string name) => $"Hello {name}"; + endpoints.MapGet("/hello/{name}", (Func<string, string>)SayHello); + }); }); }) .Build(); diff --git a/src/Http/samples/MapActionSample/Properties/launchSettings.json b/src/Http/samples/MinimalSample/Properties/launchSettings.json similarity index 100% rename from src/Http/samples/MapActionSample/Properties/launchSettings.json rename to src/Http/samples/MinimalSample/Properties/launchSettings.json diff --git a/src/Http/samples/MapActionSample/appsettings.Development.json b/src/Http/samples/MinimalSample/appsettings.Development.json similarity index 100% rename from src/Http/samples/MapActionSample/appsettings.Development.json rename to src/Http/samples/MinimalSample/appsettings.Development.json diff --git a/src/Http/samples/MapActionSample/appsettings.json b/src/Http/samples/MinimalSample/appsettings.json similarity index 100% rename from src/Http/samples/MapActionSample/appsettings.json rename to src/Http/samples/MinimalSample/appsettings.json diff --git a/src/Mvc/Mvc.Core/src/FromFormAttribute.cs b/src/Mvc/Mvc.Core/src/FromFormAttribute.cs index a2237fe1f1f..9be8b34fc53 100644 --- a/src/Mvc/Mvc.Core/src/FromFormAttribute.cs +++ b/src/Mvc/Mvc.Core/src/FromFormAttribute.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc /// Specifies that a parameter or property should be bound using form-data in the request body. /// </summary> [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] - public class FromFormAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider, IFromFormMetadata + public class FromFormAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider { /// <inheritdoc /> public BindingSource BindingSource => BindingSource.Form; diff --git a/src/Mvc/Mvc.slnf b/src/Mvc/Mvc.slnf index a534f723618..39a4d5afaf6 100644 --- a/src/Mvc/Mvc.slnf +++ b/src/Mvc/Mvc.slnf @@ -29,7 +29,6 @@ "src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj", "src\\Http\\Metadata\\src\\Microsoft.AspNetCore.Metadata.csproj", "src\\Http\\Routing.Abstractions\\src\\Microsoft.AspNetCore.Routing.Abstractions.csproj", - "src\\Http\\Routing\\samples\\MapActionSample\\MapActionSample.csproj", "src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj", "src\\Http\\WebUtilities\\src\\Microsoft.AspNetCore.WebUtilities.csproj", "src\\JSInterop\\Microsoft.JSInterop\\src\\Microsoft.JSInterop.csproj", -- GitLab