Skip to content

Commit 7775891

Browse files
Return unsuccessful when trying to read form and request does not have body. Fixes #53630 (#53680)
1 parent 17a8493 commit 7775891

File tree

3 files changed

+115
-35
lines changed

3 files changed

+115
-35
lines changed

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

+53-35
Original file line numberDiff line numberDiff line change
@@ -1475,44 +1475,48 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
14751475
object? formValue = null;
14761476
var feature = httpContext.Features.Get<IHttpRequestBodyDetectionFeature>();
14771477

1478-
if (feature?.CanHaveBody == true)
1478+
if (feature?.CanHaveBody == false)
14791479
{
1480-
if (httpContext.Features.Get<IAntiforgeryValidationFeature>() is { IsValid: false } antiforgeryValidationFeature)
1481-
{
1482-
Log.InvalidAntiforgeryToken(httpContext, parameterTypeName, parameterName, antiforgeryValidationFeature.Error!, throwOnBadRequest);
1483-
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
1484-
return (null, false);
1485-
}
1480+
Log.UnexpectedRequestWithoutBody(httpContext, parameterTypeName, parameterName, throwOnBadRequest);
1481+
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
1482+
return (null, false);
1483+
}
14861484

1487-
if (!httpContext.Request.HasFormContentType)
1488-
{
1489-
Log.UnexpectedNonFormContentType(httpContext, httpContext.Request.ContentType, throwOnBadRequest);
1490-
httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
1491-
return (null, false);
1492-
}
1485+
if (httpContext.Features.Get<IAntiforgeryValidationFeature>() is { IsValid: false } antiforgeryValidationFeature)
1486+
{
1487+
Log.InvalidAntiforgeryToken(httpContext, parameterTypeName, parameterName, antiforgeryValidationFeature.Error!, throwOnBadRequest);
1488+
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
1489+
return (null, false);
1490+
}
14931491

1494-
try
1495-
{
1496-
formValue = await httpContext.Request.ReadFormAsync();
1497-
}
1498-
catch (BadHttpRequestException ex)
1499-
{
1500-
Log.RequestBodyIOException(httpContext, ex);
1501-
httpContext.Response.StatusCode = ex.StatusCode;
1502-
return (null, false);
1503-
}
1504-
catch (IOException ex)
1505-
{
1506-
Log.RequestBodyIOException(httpContext, ex);
1507-
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
1508-
return (null, false);
1509-
}
1510-
catch (InvalidDataException ex)
1511-
{
1512-
Log.InvalidFormRequestBody(httpContext, parameterTypeName, parameterName, ex, throwOnBadRequest);
1513-
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
1514-
return (null, false);
1515-
}
1492+
if (!httpContext.Request.HasFormContentType)
1493+
{
1494+
Log.UnexpectedNonFormContentType(httpContext, httpContext.Request.ContentType, throwOnBadRequest);
1495+
httpContext.Response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
1496+
return (null, false);
1497+
}
1498+
1499+
try
1500+
{
1501+
formValue = await httpContext.Request.ReadFormAsync();
1502+
}
1503+
catch (BadHttpRequestException ex)
1504+
{
1505+
Log.RequestBodyIOException(httpContext, ex);
1506+
httpContext.Response.StatusCode = ex.StatusCode;
1507+
return (null, false);
1508+
}
1509+
catch (IOException ex)
1510+
{
1511+
Log.RequestBodyIOException(httpContext, ex);
1512+
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
1513+
return (null, false);
1514+
}
1515+
catch (InvalidDataException ex)
1516+
{
1517+
Log.InvalidFormRequestBody(httpContext, parameterTypeName, parameterName, ex, throwOnBadRequest);
1518+
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
1519+
return (null, false);
15161520
}
15171521

15181522
return (formValue, true);
@@ -2739,6 +2743,20 @@ public static void FormDataMappingFailed(HttpContext httpContext, string paramet
27392743
[LoggerMessage(RequestDelegateCreationLogging.FormDataMappingFailedEventId, LogLevel.Debug, RequestDelegateCreationLogging.FormDataMappingFailedLogMessage, EventName = RequestDelegateCreationLogging.FormDataMappingFailedEventName)]
27402744
private static partial void FormDataMappingFailed(ILogger logger, string parameterType, string parameterName, Exception exception);
27412745

2746+
public static void UnexpectedRequestWithoutBody(HttpContext httpContext, string parameterTypeName, string parameterName, bool shouldThrow)
2747+
{
2748+
if (shouldThrow)
2749+
{
2750+
var message = string.Format(CultureInfo.InvariantCulture, RequestDelegateCreationLogging.UnexpectedRequestWithoutBodyExceptionMessage, parameterTypeName, parameterName);
2751+
throw new BadHttpRequestException(message);
2752+
}
2753+
2754+
UnexpectedRequestWithoutBody(GetLogger(httpContext), parameterTypeName, parameterName);
2755+
}
2756+
2757+
[LoggerMessage(RequestDelegateCreationLogging.UnexpectedRequestWithoutBodyEventId, LogLevel.Debug, RequestDelegateCreationLogging.UnexpectedRequestWithoutBodyLogMessage, EventName = RequestDelegateCreationLogging.UnexpectedRequestWithoutBodyEventName)]
2758+
private static partial void UnexpectedRequestWithoutBody(ILogger logger, string parameterType, string parameterName);
2759+
27422760
private static ILogger GetLogger(HttpContext httpContext)
27432761
{
27442762
var loggerFactory = httpContext.RequestServices.GetRequiredService<ILoggerFactory>();

src/Http/Routing/test/FunctionalTests/MinimalFormTests.cs

+57
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,63 @@ public async Task MapPost_WithForm_AndRequestLimits_ValidToken_Works()
699699
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
700700
}
701701

702+
[Fact]
703+
public async Task MapPost_WithFormFile_MissingBody_ReturnsBadRequest()
704+
{
705+
using var host = new HostBuilder()
706+
.ConfigureWebHost(webHostBuilder =>
707+
{
708+
webHostBuilder
709+
.Configure(app =>
710+
{
711+
app.UseRouting();
712+
app.UseEndpoints(b => b.MapPost("/", (IFormFile formFile) => "ok").DisableAntiforgery());
713+
})
714+
.UseTestServer();
715+
})
716+
.ConfigureServices(services => services.AddRouting())
717+
.Build();
718+
719+
using var server = host.GetTestServer();
720+
721+
await host.StartAsync();
722+
var client = server.CreateClient();
723+
724+
var response = await client.PostAsync("/", null);
725+
726+
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
727+
}
728+
729+
[Fact]
730+
public async Task MapPost_WithFormFile_MissingContentType_ReturnsUnsupportedMediaType()
731+
{
732+
using var host = new HostBuilder()
733+
.ConfigureWebHost(webHostBuilder =>
734+
{
735+
webHostBuilder
736+
.Configure(app =>
737+
{
738+
app.UseRouting();
739+
app.UseEndpoints(b => b.MapPost("/", (IFormFile formFile) => "ok").DisableAntiforgery());
740+
})
741+
.UseTestServer();
742+
})
743+
.ConfigureServices(services => services.AddRouting())
744+
.Build();
745+
746+
using var server = host.GetTestServer();
747+
748+
await host.StartAsync();
749+
var client = server.CreateClient();
750+
751+
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Post, "/")
752+
{
753+
Content = new ByteArrayContent([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
754+
});
755+
756+
Assert.Equal(HttpStatusCode.UnsupportedMediaType, response.StatusCode);
757+
}
758+
702759
class Todo
703760
{
704761
public string Name { get; set; }

src/Shared/RequestDelegateCreationMessages.cs

+5
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,9 @@ internal static class RequestDelegateCreationLogging
5353
public const string FormDataMappingFailedEventName = "FormDataMappingFailed";
5454
public const string FormDataMappingFailedLogMessage = @"Failed to bind parameter ""{ParameterType} {ParameterName}"" from the request body as form.";
5555
public const string FormDataMappingFailedExceptionMessage = @"Failed to bind parameter ""{0} {1}"" from the request body as form.";
56+
57+
public const int UnexpectedRequestWithoutBodyEventId = 11;
58+
public const string UnexpectedRequestWithoutBodyEventName = "UnexpectedRequestWithoutBody";
59+
public const string UnexpectedRequestWithoutBodyLogMessage = @"Unexpected request without body, failed to bind parameter ""{ParameterType} {ParameterName}"" from the request body as form.";
60+
public const string UnexpectedRequestWithoutBodyExceptionMessage = @"Unexpected request without body, failed to bind parameter ""{0} {1}"" from the request body as form.";
5661
}

0 commit comments

Comments
 (0)