Просмотр исходного кода

Endpoint filter pipeline now returns EmptyHttpResult in case of 400 BadRequest (#45073)

* Fixes #45040

When an endpoint filter is used together with a failed argument validation we now return _new ValueTask<object>(EmptyHttpResult.Instance)_ instead of _new ValueTask<object>(Task.Completed)_ to prevent Task.Completed from being serialized to the response body.

* Replaced ConstantExpression with NewExpression to not pass the constant through the generated closure parameter

Co-authored-by: Tobias Oskarsson <[email protected]>
Tobias 3 лет назад
Родитель
Сommit
f69643b785

+ 3 - 3
src/Http/Http.Extensions/src/RequestDelegateFactory.cs

@@ -92,7 +92,7 @@ public static partial class RequestDelegateFactory
     private static readonly MemberExpression FormFilesExpr = Expression.Property(FormExpr, typeof(IFormCollection).GetProperty(nameof(IFormCollection.Files))!);
     private static readonly MemberExpression FormFilesExpr = Expression.Property(FormExpr, typeof(IFormCollection).GetProperty(nameof(IFormCollection.Files))!);
     private static readonly MemberExpression StatusCodeExpr = Expression.Property(HttpResponseExpr, typeof(HttpResponse).GetProperty(nameof(HttpResponse.StatusCode))!);
     private static readonly MemberExpression StatusCodeExpr = Expression.Property(HttpResponseExpr, typeof(HttpResponse).GetProperty(nameof(HttpResponse.StatusCode))!);
     private static readonly MemberExpression CompletedTaskExpr = Expression.Property(null, (PropertyInfo)GetMemberInfo<Func<Task>>(() => Task.CompletedTask));
     private static readonly MemberExpression CompletedTaskExpr = Expression.Property(null, (PropertyInfo)GetMemberInfo<Func<Task>>(() => Task.CompletedTask));
-    private static readonly NewExpression CompletedValueTaskExpr = Expression.New(typeof(ValueTask<object>).GetConstructor(new[] { typeof(Task) })!, CompletedTaskExpr);
+    private static readonly NewExpression EmptyHttpResultValueTaskExpr = Expression.New(typeof(ValueTask<object>).GetConstructor(new[] { typeof(EmptyHttpResult) })!, Expression.Property(null, typeof(EmptyHttpResult), nameof(EmptyHttpResult.Instance)));
 
 
     private static readonly ParameterExpression TempSourceStringExpr = ParameterBindingMethodCache.TempSourceStringExpr;
     private static readonly ParameterExpression TempSourceStringExpr = ParameterBindingMethodCache.TempSourceStringExpr;
     private static readonly BinaryExpression TempSourceStringNotNullExpr = Expression.NotEqual(TempSourceStringExpr, Expression.Constant(null));
     private static readonly BinaryExpression TempSourceStringNotNullExpr = Expression.NotEqual(TempSourceStringExpr, Expression.Constant(null));
@@ -432,7 +432,7 @@ public static partial class RequestDelegateFactory
         var filteredInvocation = Expression.Lambda<EndpointFilterDelegate>(
         var filteredInvocation = Expression.Lambda<EndpointFilterDelegate>(
             Expression.Condition(
             Expression.Condition(
                 Expression.GreaterThanOrEqual(FilterContextHttpContextStatusCodeExpr, Expression.Constant(400)),
                 Expression.GreaterThanOrEqual(FilterContextHttpContextStatusCodeExpr, Expression.Constant(400)),
-                CompletedValueTaskExpr,
+                EmptyHttpResultValueTaskExpr,
                 handlerInvocation),
                 handlerInvocation),
             FilterContextExpr).Compile();
             FilterContextExpr).Compile();
         var routeHandlerContext = new EndpointFilterFactoryContext
         var routeHandlerContext = new EndpointFilterFactoryContext
@@ -463,7 +463,7 @@ public static partial class RequestDelegateFactory
     {
     {
         if (returnType == typeof(void))
         if (returnType == typeof(void))
         {
         {
-            return Expression.Block(methodCall, Expression.Constant(new ValueTask<object?>(EmptyHttpResult.Instance)));
+            return Expression.Block(methodCall, EmptyHttpResultValueTaskExpr);
         }
         }
         else if (returnType == typeof(Task))
         else if (returnType == typeof(Task))
         {
         {

+ 3 - 0
src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs

@@ -5671,6 +5671,8 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         };
         };
 
 
         var httpContext = CreateHttpContext();
         var httpContext = CreateHttpContext();
+        var responseBodyStream = new MemoryStream();
+        httpContext.Response.Body = responseBodyStream;
 
 
         // Act
         // Act
         var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
         var factoryResult = RequestDelegateFactory.Create(HelloName, new RequestDelegateFactoryOptions()
@@ -5690,6 +5692,7 @@ public partial class RequestDelegateFactoryTests : LoggedTest
         // Assert
         // Assert
         Assert.False(invoked);
         Assert.False(invoked);
         Assert.Equal(400, httpContext.Response.StatusCode);
         Assert.Equal(400, httpContext.Response.StatusCode);
+        Assert.Equal(0, responseBodyStream.Position);
     }
     }
 
 
     [Fact]
     [Fact]