|
|
@@ -768,48 +768,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.Equal(new StringValues(new[] { "7", "8", "9" }), httpContext.Items["form"]!);
|
|
|
}
|
|
|
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateLogsStringValuesFromExplicitQueryStringSourceForUnpresentedValuesFailuresAsDebugAndSets400Response()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Form = new FormCollection(null);
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create((HttpContext context,
|
|
|
- [FromHeader(Name = "foo")] StringValues headerValues,
|
|
|
- [FromQuery(Name = "bar")] StringValues queryValues,
|
|
|
- [FromForm(Name = "form")] StringValues formValues) =>
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- });
|
|
|
-
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- var logs = TestSink.Writes.ToArray();
|
|
|
-
|
|
|
- Assert.Equal(3, logs.Length);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
|
|
|
- Assert.Equal(@"Required parameter ""StringValues headerValues"" was not provided from header.", logs[0].Message);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[1].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
|
|
|
- Assert.Equal(@"Required parameter ""StringValues queryValues"" was not provided from query string.", logs[1].Message);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[2].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[2].LogLevel);
|
|
|
- Assert.Equal(@"Required parameter ""StringValues formValues"" was not provided from form.", logs[2].Message);
|
|
|
- }
|
|
|
-
|
|
|
[Fact]
|
|
|
public async Task RequestDelegateHandlesNullableStringValuesFromExplicitQueryStringSourceForUnpresentedValues()
|
|
|
{
|
|
|
@@ -932,111 +890,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.Equal("Encountered a parameter of type 'System.Runtime.CompilerServices.Closure' without a name. Parameters must have a name.", ex.Message);
|
|
|
}
|
|
|
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateLogsTryParsableFailuresAsDebugAndSets400Response()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromRoute] int tryParsable, [FromRoute] int tryParsable2)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.RouteValues["tryParsable"] = "invalid!";
|
|
|
- httpContext.Request.RouteValues["tryParsable2"] = "invalid again!";
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction);
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- var logs = TestSink.Writes.ToArray();
|
|
|
-
|
|
|
- Assert.Equal(2, logs.Length);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(3, "ParameterBindingFailed"), logs[0].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
|
|
|
- Assert.Equal(@"Failed to bind parameter ""int tryParsable"" from ""invalid!"".", logs[0].Message);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(3, "ParameterBindingFailed"), logs[1].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
|
|
|
- Assert.Equal(@"Failed to bind parameter ""int tryParsable2"" from ""invalid again!"".", logs[1].Message);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateThrowsForTryParsableFailuresIfThrowOnBadRequest()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromRoute] int tryParsable, [FromRoute] int tryParsable2)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.RouteValues["tryParsable"] = "invalid!";
|
|
|
- httpContext.Request.RouteValues["tryParsable2"] = "invalid again!";
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = true });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
-
|
|
|
- // The httpContext should be untouched.
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- // We don't log bad requests when we throw.
|
|
|
- Assert.Empty(TestSink.Writes);
|
|
|
-
|
|
|
- Assert.Equal(@"Failed to bind parameter ""int tryParsable"" from ""invalid!"".", badHttpRequestException.Message);
|
|
|
- Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateThrowsForTryParsableFailuresIfThrowOnBadRequestWithArrays()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromQuery] int[] values)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Query = new QueryCollection(new Dictionary<string, StringValues>()
|
|
|
- {
|
|
|
- ["values"] = new(new[] { "1", "NAN", "3" })
|
|
|
- });
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = true, DisableInferBodyFromParameters = true });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
-
|
|
|
- // The httpContext should be untouched.
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- // We don't log bad requests when we throw.
|
|
|
- Assert.Empty(TestSink.Writes);
|
|
|
-
|
|
|
- Assert.Equal(@"Failed to bind parameter ""int[] values"" from ""NAN"".", badHttpRequestException.Message);
|
|
|
- Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
- }
|
|
|
-
|
|
|
[Fact]
|
|
|
public async Task RequestDelegateThrowsForTryParsableFailuresIfThrowOnBadRequestWithNonOptionalArrays()
|
|
|
{
|
|
|
@@ -1072,130 +925,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
}
|
|
|
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateLogsBindAsyncFailuresAndSets400Response()
|
|
|
- {
|
|
|
- // Not supplying any headers will cause the HttpContext BindAsync overload to return null.
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create((MyBindAsyncRecord myBindAsyncRecord1, MyBindAsyncRecord myBindAsyncRecord2) =>
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- });
|
|
|
-
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
-
|
|
|
- var logs = TestSink.Writes.ToArray();
|
|
|
-
|
|
|
- Assert.Equal(2, logs.Length);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
|
|
|
- Assert.Equal(@"Required parameter ""MyBindAsyncRecord myBindAsyncRecord1"" was not provided from MyBindAsyncRecord.BindAsync(HttpContext, ParameterInfo).", logs[0].Message);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[1].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
|
|
|
- Assert.Equal(@"Required parameter ""MyBindAsyncRecord myBindAsyncRecord2"" was not provided from MyBindAsyncRecord.BindAsync(HttpContext, ParameterInfo).", logs[1].Message);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateThrowsForBindAsyncFailuresIfThrowOnBadRequest()
|
|
|
- {
|
|
|
- // Not supplying any headers will cause the HttpContext BindAsync overload to return null.
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create((MyBindAsyncRecord myBindAsyncRecord1, MyBindAsyncRecord myBindAsyncRecord2) =>
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }, new() { ThrowOnBadRequest = true });
|
|
|
-
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
- var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
-
|
|
|
- // The httpContext should be untouched.
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- // We don't log bad requests when we throw.
|
|
|
- Assert.Empty(TestSink.Writes);
|
|
|
-
|
|
|
- Assert.Equal(@"Required parameter ""MyBindAsyncRecord myBindAsyncRecord1"" was not provided from MyBindAsyncRecord.BindAsync(HttpContext, ParameterInfo).", badHttpRequestException.Message);
|
|
|
- Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateLogsSingleArgBindAsyncFailuresAndSets400Response()
|
|
|
- {
|
|
|
- // Not supplying any headers will cause the HttpContext BindAsync overload to return null.
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create((MySimpleBindAsyncRecord mySimpleBindAsyncRecord1,
|
|
|
- MySimpleBindAsyncRecord mySimpleBindAsyncRecord2) =>
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- });
|
|
|
-
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
-
|
|
|
- var logs = TestSink.Writes.ToArray();
|
|
|
-
|
|
|
- Assert.Equal(2, logs.Length);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[0].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[0].LogLevel);
|
|
|
- Assert.Equal(@"Required parameter ""MySimpleBindAsyncRecord mySimpleBindAsyncRecord1"" was not provided from MySimpleBindAsyncRecord.BindAsync(HttpContext).", logs[0].Message);
|
|
|
-
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), logs[1].EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logs[1].LogLevel);
|
|
|
- Assert.Equal(@"Required parameter ""MySimpleBindAsyncRecord mySimpleBindAsyncRecord2"" was not provided from MySimpleBindAsyncRecord.BindAsync(HttpContext).", logs[1].Message);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateThrowsForSingleArgBindAsyncFailuresIfThrowOnBadRequest()
|
|
|
- {
|
|
|
- // Not supplying any headers will cause the HttpContext BindAsync overload to return null.
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create((MySimpleBindAsyncRecord mySimpleBindAsyncRecord1,
|
|
|
- MySimpleBindAsyncRecord mySimpleBindAsyncRecord2) =>
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }, new() { ThrowOnBadRequest = true });
|
|
|
-
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
- var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
-
|
|
|
- // The httpContext should be untouched.
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- // We don't log bad requests when we throw.
|
|
|
- Assert.Empty(TestSink.Writes);
|
|
|
-
|
|
|
- Assert.Equal(@"Required parameter ""MySimpleBindAsyncRecord mySimpleBindAsyncRecord1"" was not provided from MySimpleBindAsyncRecord.BindAsync(HttpContext).", badHttpRequestException.Message);
|
|
|
- Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
- }
|
|
|
-
|
|
|
[Fact]
|
|
|
public async Task BindAsyncWithBodyArgument()
|
|
|
{
|
|
|
@@ -1537,200 +1266,23 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- [Theory]
|
|
|
- [InlineData(true)]
|
|
|
- [InlineData(false)]
|
|
|
- public async Task RequestDelegateLogsIOExceptionsAsDebugDoesNotAbortAndNeverThrows(bool throwOnBadRequests)
|
|
|
+ [Fact]
|
|
|
+ public void BuildRequestDelegateThrowsInvalidOperationExceptionGivenFromBodyOnMultipleParameters()
|
|
|
{
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromBody] Todo todo)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var ioException = new IOException();
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/json";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "1";
|
|
|
- httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(ioException);
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = throwOnBadRequests });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
+ void TestAttributedInvalidAction([FromBody] int value1, [FromBody] int value2) { }
|
|
|
+ void TestInferredInvalidAction(Todo value1, Todo value2) { }
|
|
|
+ void TestBothInvalidAction(Todo value1, [FromBody] int value2) { }
|
|
|
|
|
|
- var logMessage = Assert.Single(TestSink.Writes);
|
|
|
- Assert.Equal(new EventId(1, "RequestBodyIOException"), logMessage.EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
|
|
|
- Assert.Equal("Reading the request body failed with an IOException.", logMessage.Message);
|
|
|
- Assert.Same(ioException, logMessage.Exception);
|
|
|
+ Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestAttributedInvalidAction));
|
|
|
+ Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestInferredInvalidAction));
|
|
|
+ Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestBothInvalidAction));
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
- public async Task RequestDelegateLogsJsonExceptionsAsDebugAndSets400Response()
|
|
|
+ public void BuildRequestDelegateThrowsInvalidOperationExceptionForInvalidTryParse()
|
|
|
{
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromBody] Todo todo)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var jsonException = new JsonException();
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/json";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "1";
|
|
|
- httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(jsonException);
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction);
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- var logMessage = Assert.Single(TestSink.Writes);
|
|
|
- Assert.Equal(new EventId(2, "InvalidJsonRequestBody"), logMessage.EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
|
|
|
- Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", logMessage.Message);
|
|
|
- Assert.Same(jsonException, logMessage.Exception);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateThrowsForJsonExceptionsIfThrowOnBadRequest()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromBody] Todo todo)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var jsonException = new JsonException();
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/json";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "1";
|
|
|
- httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(jsonException);
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = true });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
-
|
|
|
- // The httpContext should be untouched.
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- // We don't log bad requests when we throw.
|
|
|
- Assert.Empty(TestSink.Writes);
|
|
|
-
|
|
|
- Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", badHttpRequestException.Message);
|
|
|
- Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
- Assert.Same(jsonException, badHttpRequestException.InnerException);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateLogsMalformedJsonAsDebugAndSets400Response()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromBody] Todo todo)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/json";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "1";
|
|
|
- httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{"));
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction);
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- var logMessage = Assert.Single(TestSink.Writes);
|
|
|
- Assert.Equal(new EventId(2, "InvalidJsonRequestBody"), logMessage.EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
|
|
|
- Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", logMessage.Message);
|
|
|
- Assert.IsType<JsonException>(logMessage.Exception);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateThrowsForMalformedJsonIfThrowOnBadRequest()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction([FromBody] Todo todo)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/json";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "1";
|
|
|
- httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{"));
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = true });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
-
|
|
|
- // The httpContext should be untouched.
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- // We don't log bad requests when we throw.
|
|
|
- Assert.Empty(TestSink.Writes);
|
|
|
-
|
|
|
- Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", badHttpRequestException.Message);
|
|
|
- Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
- Assert.IsType<JsonException>(badHttpRequestException.InnerException);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void BuildRequestDelegateThrowsInvalidOperationExceptionGivenFromBodyOnMultipleParameters()
|
|
|
- {
|
|
|
- void TestAttributedInvalidAction([FromBody] int value1, [FromBody] int value2) { }
|
|
|
- void TestInferredInvalidAction(Todo value1, Todo value2) { }
|
|
|
- void TestBothInvalidAction(Todo value1, [FromBody] int value2) { }
|
|
|
-
|
|
|
- Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestAttributedInvalidAction));
|
|
|
- Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestInferredInvalidAction));
|
|
|
- Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestBothInvalidAction));
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public void BuildRequestDelegateThrowsInvalidOperationExceptionForInvalidTryParse()
|
|
|
- {
|
|
|
- void TestTryParseStruct(BadTryParseStruct value1) { }
|
|
|
- void TestTryParseClass(BadTryParseClass value1) { }
|
|
|
+ void TestTryParseStruct(BadTryParseStruct value1) { }
|
|
|
+ void TestTryParseClass(BadTryParseClass value1) { }
|
|
|
|
|
|
Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestTryParseStruct));
|
|
|
Assert.Throws<InvalidOperationException>(() => RequestDelegateFactory.Create(TestTryParseClass));
|
|
|
@@ -2848,77 +2400,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.Equal("null", responseBody);
|
|
|
}
|
|
|
|
|
|
- public static IEnumerable<object?[]> QueryParamOptionalityData
|
|
|
- {
|
|
|
- get
|
|
|
- {
|
|
|
- string requiredQueryParam(string name) => $"Hello {name}!";
|
|
|
- string defaultValueQueryParam(string name = "DefaultName") => $"Hello {name}!";
|
|
|
- string nullableQueryParam(string? name) => $"Hello {name}!";
|
|
|
- string requiredParseableQueryParam(int age) => $"Age: {age}";
|
|
|
- string defaultValueParseableQueryParam(int age = 12) => $"Age: {age}";
|
|
|
- string nullableQueryParseableParam(int? age) => $"Age: {age}";
|
|
|
-
|
|
|
- return new List<object?[]>
|
|
|
- {
|
|
|
- new object?[] { (Func<string, string>)requiredQueryParam, "name", null, true, null},
|
|
|
- new object?[] { (Func<string, string>)requiredQueryParam, "name", "TestName", false, "Hello TestName!" },
|
|
|
- new object?[] { (Func<string, string>)defaultValueQueryParam, "name", null, false, "Hello DefaultName!" },
|
|
|
- new object?[] { (Func<string, string>)defaultValueQueryParam, "name", "TestName", false, "Hello TestName!" },
|
|
|
- new object?[] { (Func<string?, string>)nullableQueryParam, "name", null, false, "Hello !" },
|
|
|
- new object?[] { (Func<string?, string>)nullableQueryParam, "name", "TestName", false, "Hello TestName!"},
|
|
|
-
|
|
|
- new object?[] { (Func<int, string>)requiredParseableQueryParam, "age", null, true, null},
|
|
|
- new object?[] { (Func<int, string>)requiredParseableQueryParam, "age", "42", false, "Age: 42" },
|
|
|
- new object?[] { (Func<int, string>)defaultValueParseableQueryParam, "age", null, false, "Age: 12" },
|
|
|
- new object?[] { (Func<int, string>)defaultValueParseableQueryParam, "age", "42", false, "Age: 42" },
|
|
|
- new object?[] { (Func<int?, string>)nullableQueryParseableParam, "age", null, false, "Age: " },
|
|
|
- new object?[] { (Func<int?, string>)nullableQueryParseableParam, "age", "42", false, "Age: 42"},
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [MemberData(nameof(QueryParamOptionalityData))]
|
|
|
- public async Task RequestDelegateHandlesQueryParamOptionality(Delegate @delegate, string paramName, string? queryParam, bool isInvalid, string? expectedResponse)
|
|
|
- {
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- var responseBodyStream = new MemoryStream();
|
|
|
- httpContext.Response.Body = responseBodyStream;
|
|
|
-
|
|
|
- if (queryParam is not null)
|
|
|
- {
|
|
|
- httpContext.Request.Query = new QueryCollection(new Dictionary<string, StringValues>
|
|
|
- {
|
|
|
- [paramName] = queryParam
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(@delegate);
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- var logs = TestSink.Writes.ToArray();
|
|
|
-
|
|
|
- if (isInvalid)
|
|
|
- {
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- var log = Assert.Single(logs);
|
|
|
- Assert.Equal(LogLevel.Debug, log.LogLevel);
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), log.EventId);
|
|
|
- var expectedType = paramName == "age" ? "int age" : $"string name";
|
|
|
- Assert.Equal($@"Required parameter ""{expectedType}"" was not provided from route or query string.", log.Message);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- var decodedResponseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray());
|
|
|
- Assert.Equal(expectedResponse, decodedResponseBody);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
public static IEnumerable<object?[]> RouteParamOptionalityData
|
|
|
{
|
|
|
get
|
|
|
@@ -2991,77 +2472,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public static IEnumerable<object?[]> BodyParamOptionalityData
|
|
|
- {
|
|
|
- get
|
|
|
- {
|
|
|
- string requiredBodyParam(Todo todo) => $"Todo: {todo.Name}";
|
|
|
- string defaultValueBodyParam(Todo? todo = null) => $"Todo: {todo?.Name}";
|
|
|
- string nullableBodyParam(Todo? todo) => $"Todo: {todo?.Name}";
|
|
|
-
|
|
|
- return new List<object?[]>
|
|
|
- {
|
|
|
- new object?[] { (Func<Todo, string>)requiredBodyParam, false, true, null },
|
|
|
- new object?[] { (Func<Todo, string>)requiredBodyParam, true, false, "Todo: Default Todo"},
|
|
|
- new object?[] { (Func<Todo, string>)defaultValueBodyParam, false, false, "Todo: "},
|
|
|
- new object?[] { (Func<Todo, string>)defaultValueBodyParam, true, false, "Todo: Default Todo"},
|
|
|
- new object?[] { (Func<Todo?, string>)nullableBodyParam, false, false, "Todo: " },
|
|
|
- new object?[] { (Func<Todo?, string>)nullableBodyParam, true, false, "Todo: Default Todo" },
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [MemberData(nameof(BodyParamOptionalityData))]
|
|
|
- public async Task RequestDelegateHandlesBodyParamOptionality(Delegate @delegate, bool hasBody, bool isInvalid, string? expectedResponse)
|
|
|
- {
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- var responseBodyStream = new MemoryStream();
|
|
|
- httpContext.Response.Body = responseBodyStream;
|
|
|
-
|
|
|
- if (hasBody)
|
|
|
- {
|
|
|
- var todo = new Todo() { Name = "Default Todo" };
|
|
|
- var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(todo);
|
|
|
- var stream = new MemoryStream(requestBodyBytes);
|
|
|
- httpContext.Request.Body = stream;
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/json";
|
|
|
- httpContext.Request.ContentLength = stream.Length;
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
- }
|
|
|
-
|
|
|
- var jsonOptions = new JsonOptions();
|
|
|
- jsonOptions.SerializerOptions.Converters.Add(new TodoJsonConverter());
|
|
|
-
|
|
|
- var serviceCollection = new ServiceCollection();
|
|
|
- serviceCollection.AddSingleton(LoggerFactory);
|
|
|
- serviceCollection.AddSingleton(Options.Create(jsonOptions));
|
|
|
- httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(@delegate);
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var request = requestDelegate(httpContext);
|
|
|
-
|
|
|
- if (isInvalid)
|
|
|
- {
|
|
|
- var logs = TestSink.Writes.ToArray();
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- var log = Assert.Single(logs);
|
|
|
- Assert.Equal(LogLevel.Debug, log.LogLevel);
|
|
|
- Assert.Equal(new EventId(5, "ImplicitBodyNotProvided"), log.EventId);
|
|
|
- Assert.Equal(@"Implicit body inferred for parameter ""todo"" but no body was provided. Did you mean to use a Service instead?", log.Message);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await request;
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- var decodedResponseBody = Encoding.UTF8.GetString(responseBodyStream.ToArray());
|
|
|
- Assert.Equal(expectedResponse, decodedResponseBody);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
public static IEnumerable<object?[]> BindAsyncParamOptionalityData
|
|
|
{
|
|
|
get
|
|
|
@@ -3228,54 +2638,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- public static IEnumerable<object?[]> AllowEmptyData
|
|
|
- {
|
|
|
- get
|
|
|
- {
|
|
|
- string disallowEmptyAndNonOptional([FromBody(AllowEmpty = false)] Todo todo) => $"{todo}";
|
|
|
- string allowEmptyAndNonOptional([FromBody(AllowEmpty = true)] Todo todo) => $"{todo}";
|
|
|
- string allowEmptyAndOptional([FromBody(AllowEmpty = true)] Todo? todo = null) => $"{todo}";
|
|
|
- string disallowEmptyAndOptional([FromBody(AllowEmpty = false)] Todo? todo = null) => $"{todo}";
|
|
|
-
|
|
|
- return new List<object?[]>
|
|
|
- {
|
|
|
- new object?[] { (Func<Todo, string>)disallowEmptyAndNonOptional, false },
|
|
|
- new object?[] { (Func<Todo, string>)allowEmptyAndNonOptional, true },
|
|
|
- new object?[] { (Func<Todo, string>)allowEmptyAndOptional, true },
|
|
|
- new object?[] { (Func<Todo, string>)disallowEmptyAndOptional, true }
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [MemberData(nameof(AllowEmptyData))]
|
|
|
- public async Task AllowEmptyOverridesOptionality(Delegate @delegate, bool allowsEmptyRequest)
|
|
|
- {
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(@delegate);
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- var logs = TestSink.Writes.ToArray();
|
|
|
-
|
|
|
- if (!allowsEmptyRequest)
|
|
|
- {
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- var log = Assert.Single(logs);
|
|
|
- Assert.Equal(LogLevel.Debug, log.LogLevel);
|
|
|
- Assert.Equal(new EventId(4, "RequiredParameterNotProvided"), log.EventId);
|
|
|
- Assert.Equal(@"Required parameter ""Todo todo"" was not provided from body.", log.Message);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
#nullable disable
|
|
|
|
|
|
[Theory]
|
|
|
@@ -3395,93 +2757,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.Equal(@"""Hello Tester. This is from an extension method.""", decodedResponseBody);
|
|
|
}
|
|
|
|
|
|
- [Theory]
|
|
|
- [InlineData(true)]
|
|
|
- [InlineData(false)]
|
|
|
- public async Task RequestDelegateRejectsNonJsonContent(bool shouldThrow)
|
|
|
- {
|
|
|
- var httpContext = new DefaultHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/xml";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "1";
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var serviceCollection = new ServiceCollection();
|
|
|
- serviceCollection.AddSingleton(LoggerFactory);
|
|
|
- httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create((HttpContext context, Todo todo) =>
|
|
|
- {
|
|
|
- }, new RequestDelegateFactoryOptions() { ThrowOnBadRequest = shouldThrow });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var request = requestDelegate(httpContext);
|
|
|
-
|
|
|
- if (shouldThrow)
|
|
|
- {
|
|
|
- var ex = await Assert.ThrowsAsync<BadHttpRequestException>(() => request);
|
|
|
- Assert.Equal("Expected a supported JSON media type but got \"application/xml\".", ex.Message);
|
|
|
- Assert.Equal(StatusCodes.Status415UnsupportedMediaType, ex.StatusCode);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await request;
|
|
|
-
|
|
|
- Assert.Equal(415, httpContext.Response.StatusCode);
|
|
|
- var logMessage = Assert.Single(TestSink.Writes);
|
|
|
- Assert.Equal(new EventId(6, "UnexpectedContentType"), logMessage.EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
|
|
|
- Assert.Equal("Expected a supported JSON media type but got \"application/xml\".", logMessage.Message);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- [Theory]
|
|
|
- [InlineData(true)]
|
|
|
- [InlineData(false)]
|
|
|
- public async Task RequestDelegateWithBindAndImplicitBodyRejectsNonJsonContent(bool shouldThrow)
|
|
|
- {
|
|
|
- Todo originalTodo = new()
|
|
|
- {
|
|
|
- Name = "Write more tests!"
|
|
|
- };
|
|
|
-
|
|
|
- var httpContext = new DefaultHttpContext();
|
|
|
-
|
|
|
- var requestBodyBytes = JsonSerializer.SerializeToUtf8Bytes(originalTodo);
|
|
|
- var stream = new MemoryStream(requestBodyBytes);
|
|
|
- httpContext.Request.Body = stream;
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/xml";
|
|
|
- httpContext.Request.Headers["Content-Length"] = stream.Length.ToString(CultureInfo.InvariantCulture);
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var serviceCollection = new ServiceCollection();
|
|
|
- serviceCollection.AddSingleton(LoggerFactory);
|
|
|
- httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create((HttpContext context, JsonTodo customTodo, Todo todo) =>
|
|
|
- {
|
|
|
- }, new RequestDelegateFactoryOptions() { ThrowOnBadRequest = shouldThrow });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var request = requestDelegate(httpContext);
|
|
|
-
|
|
|
- if (shouldThrow)
|
|
|
- {
|
|
|
- var ex = await Assert.ThrowsAsync<BadHttpRequestException>(() => request);
|
|
|
- Assert.Equal("Expected a supported JSON media type but got \"application/xml\".", ex.Message);
|
|
|
- Assert.Equal(StatusCodes.Status415UnsupportedMediaType, ex.StatusCode);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await request;
|
|
|
-
|
|
|
- Assert.Equal(415, httpContext.Response.StatusCode);
|
|
|
- var logMessage = Assert.Single(TestSink.Writes);
|
|
|
- Assert.Equal(new EventId(6, "UnexpectedContentType"), logMessage.EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
|
|
|
- Assert.Equal("Expected a supported JSON media type but got \"application/xml\".", logMessage.Message);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
public static IEnumerable<object?[]> UriDelegates
|
|
|
{
|
|
|
get
|
|
|
@@ -3522,41 +2797,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.Equal(expectedResponse, decodedResponseBody);
|
|
|
}
|
|
|
|
|
|
- [Theory]
|
|
|
- [InlineData(true)]
|
|
|
- [InlineData(false)]
|
|
|
- public async Task RequestDelegateLogsIOExceptionsForFormAsDebugDoesNotAbortAndNeverThrows(bool throwOnBadRequests)
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction(IFormFile file)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var ioException = new IOException();
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "1";
|
|
|
- httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(ioException);
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = throwOnBadRequests });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
-
|
|
|
- var logMessage = Assert.Single(TestSink.Writes);
|
|
|
- Assert.Equal(new EventId(1, "RequestBodyIOException"), logMessage.EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
|
|
|
- Assert.Equal("Reading the request body failed with an IOException.", logMessage.Message);
|
|
|
- Assert.Same(ioException, logMessage.Exception);
|
|
|
- }
|
|
|
-
|
|
|
[Fact]
|
|
|
public async Task RequestDelegateThrowsBadHttpRequestExceptionWhenReadingOversizeFormResultsIn413BadRequest()
|
|
|
{
|
|
|
@@ -3623,75 +2863,6 @@ public partial class RequestDelegateFactoryTests : LoggedTest
|
|
|
Assert.Equal(413, httpContext.Response.StatusCode);
|
|
|
}
|
|
|
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateLogsMalformedFormAsDebugAndSets400Response()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction(IFormFile file)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "2049";
|
|
|
- httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(new string('x', 2049)));
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction);
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- await requestDelegate(httpContext);
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(400, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- var logMessage = Assert.Single(TestSink.Writes);
|
|
|
- Assert.Equal(new EventId(8, "InvalidFormRequestBody"), logMessage.EventId);
|
|
|
- Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
|
|
|
- Assert.Equal(@"Failed to read parameter ""IFormFile file"" from the request body as form.", logMessage.Message);
|
|
|
- Assert.IsType<InvalidDataException>(logMessage.Exception);
|
|
|
- }
|
|
|
-
|
|
|
- [Fact]
|
|
|
- public async Task RequestDelegateThrowsForMalformedFormIfThrowOnBadRequest()
|
|
|
- {
|
|
|
- var invoked = false;
|
|
|
-
|
|
|
- void TestAction(IFormFile file)
|
|
|
- {
|
|
|
- invoked = true;
|
|
|
- }
|
|
|
-
|
|
|
- var httpContext = CreateHttpContext();
|
|
|
- httpContext.Request.Headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
|
- httpContext.Request.Headers["Content-Length"] = "2049";
|
|
|
- httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(new string('x', 2049)));
|
|
|
- httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
|
|
|
-
|
|
|
- var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = true });
|
|
|
- var requestDelegate = factoryResult.RequestDelegate;
|
|
|
-
|
|
|
- var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
|
|
|
-
|
|
|
- Assert.False(invoked);
|
|
|
-
|
|
|
- // The httpContext should be untouched.
|
|
|
- Assert.False(httpContext.RequestAborted.IsCancellationRequested);
|
|
|
- Assert.Equal(200, httpContext.Response.StatusCode);
|
|
|
- Assert.False(httpContext.Response.HasStarted);
|
|
|
-
|
|
|
- // We don't log bad requests when we throw.
|
|
|
- Assert.Empty(TestSink.Writes);
|
|
|
-
|
|
|
- Assert.Equal(@"Failed to read parameter ""IFormFile file"" from the request body as form.", badHttpRequestException.Message);
|
|
|
- Assert.Equal(400, badHttpRequestException.StatusCode);
|
|
|
- Assert.IsType<InvalidDataException>(badHttpRequestException.InnerException);
|
|
|
- }
|
|
|
-
|
|
|
[Fact]
|
|
|
public void BuildRequestDelegateThrowsInvalidOperationExceptionBodyAndFormParameters()
|
|
|
{
|