Skip to content

Commit 0bbbb7f

Browse files
committed
Don't catch InvalidDataExceptions when reading JSON
1 parent 3177ab3 commit 0bbbb7f

File tree

2 files changed

+46
-48
lines changed

2 files changed

+46
-48
lines changed

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

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
586586
Log.RequestBodyIOException(httpContext, ex);
587587
return;
588588
}
589-
catch (Exception ex) when (ex is InvalidDataException || ex is JsonException)
589+
catch (JsonException ex)
590590
{
591591
Log.InvalidJsonRequestBody(httpContext, parameterTypeName, parameterName, ex, factoryContext.ThrowOnBadRequest);
592592
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
@@ -624,7 +624,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
624624
Log.RequestBodyIOException(httpContext, ex);
625625
return;
626626
}
627-
catch (Exception ex) when (ex is InvalidDataException || ex is JsonException)
627+
catch (JsonException ex)
628628
{
629629

630630
Log.InvalidJsonRequestBody(httpContext, parameterTypeName, parameterName, ex, factoryContext.ThrowOnBadRequest);
@@ -1271,16 +1271,6 @@ public static void RequiredParameterNotProvided(HttpContext httpContext, string
12711271
[LoggerMessage(4, LogLevel.Debug, RequiredParameterNotProvidedLogMessage, EventName = "RequiredParameterNotProvided")]
12721272
private static partial void RequiredParameterNotProvided(ILogger logger, string parameterType, string parameterName, string source);
12731273

1274-
public static void UnexpectedContentType(HttpContext httpContext, string? contentType, bool shouldThrow)
1275-
{
1276-
if (shouldThrow)
1277-
{
1278-
var message = string.Format(CultureInfo.InvariantCulture, UnexpectedContentTypeExceptionMessage, contentType);
1279-
throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType);
1280-
}
1281-
1282-
UnexpectedContentType(GetLogger(httpContext), contentType ?? "(none)");
1283-
}
12841274
public static void ImplicitBodyNotProvided(HttpContext httpContext, string parameterName, bool shouldThrow)
12851275
{
12861276
if (shouldThrow)
@@ -1295,8 +1285,16 @@ public static void ImplicitBodyNotProvided(HttpContext httpContext, string param
12951285
[LoggerMessage(5, LogLevel.Debug, ImplicitBodyNotProvidedLogMessage, EventName = "ImplicitBodyNotProvided")]
12961286
private static partial void ImplicitBodyNotProvided(ILogger logger, string parameterName);
12971287

1298-
public static void UnexpectedContentType(HttpContext httpContext, string? contentType)
1299-
=> UnexpectedContentType(GetLogger(httpContext), contentType ?? "(none)");
1288+
public static void UnexpectedContentType(HttpContext httpContext, string? contentType, bool shouldThrow)
1289+
{
1290+
if (shouldThrow)
1291+
{
1292+
var message = string.Format(CultureInfo.InvariantCulture, UnexpectedContentTypeExceptionMessage, contentType);
1293+
throw new BadHttpRequestException(message, StatusCodes.Status415UnsupportedMediaType);
1294+
}
1295+
1296+
UnexpectedContentType(GetLogger(httpContext), contentType ?? "(none)");
1297+
}
13001298

13011299
[LoggerMessage(6, LogLevel.Debug, UnexpectedContentTypeLogMessage, EventName = "UnexpectedContentType")]
13021300
private static partial void UnexpectedContentType(ILogger logger, string contentType);

src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ void TestAction([FromRoute] int tryParsable, [FromRoute] int tryParsable2)
904904
}
905905

906906
[Fact]
907-
public async Task RequestDelegateLogsTryParsableFailuresAsDebugAndThrowsIfThrowOnBadRequest()
907+
public async Task RequestDelegateThrowsForTryParsableFailuresIfThrowOnBadRequest()
908908
{
909909
var invoked = false;
910910

@@ -969,7 +969,7 @@ public async Task RequestDelegateLogsBindAsyncFailuresAndSets400Response()
969969
}
970970

971971
[Fact]
972-
public async Task RequestDelegateLogsBindAsyncFailuresAndThrowsIfThrowOnBadRequest()
972+
public async Task RequestDelegateThrowsForBindAsyncFailuresIfThrowOnBadRequest()
973973
{
974974
// Not supplying any headers will cause the HttpContext BindAsync overload to return null.
975975
var httpContext = CreateHttpContext();
@@ -1031,7 +1031,7 @@ public async Task RequestDelegateLogsSingleArgBindAsyncFailuresAndSets400Respons
10311031
}
10321032

10331033
[Fact]
1034-
public async Task RequestDelegateLogsSingleArgBindAsyncFailuresAndThrowsIfThrowOnBadRequest()
1034+
public async Task RequestDelegateThrowsForSingleArgBindAsyncFailuresIfThrowOnBadRequest()
10351035
{
10361036
// Not supplying any headers will cause the HttpContext BindAsync overload to return null.
10371037
var httpContext = CreateHttpContext();
@@ -1421,7 +1421,7 @@ void TestAction([FromBody(AllowEmpty = true)] BodyStruct bodyStruct)
14211421
[Theory]
14221422
[InlineData(true)]
14231423
[InlineData(false)]
1424-
public async Task RequestDelegateLogsFromBodyIOExceptionsAsDebugDoesNotAbortAndNeverThrows(bool throwOnBadRequests)
1424+
public async Task RequestDelegateLogsIOExceptionsAsDebugDoesNotAbortAndNeverThrows(bool throwOnBadRequests)
14251425
{
14261426
var invoked = false;
14271427

@@ -1454,7 +1454,7 @@ void TestAction([FromBody] Todo todo)
14541454
}
14551455

14561456
[Fact]
1457-
public async Task RequestDelegateLogsFromBodyInvalidDataExceptionsAsDebugAndSets400Response()
1457+
public async Task RequestDelegateLogsJsonExceptionsAsDebugAndSets400Response()
14581458
{
14591459
var invoked = false;
14601460

@@ -1463,12 +1463,12 @@ void TestAction([FromBody] Todo todo)
14631463
invoked = true;
14641464
}
14651465

1466-
var invalidDataException = new InvalidDataException();
1466+
var jsonException = new JsonException();
14671467

14681468
var httpContext = CreateHttpContext();
14691469
httpContext.Request.Headers["Content-Type"] = "application/json";
14701470
httpContext.Request.Headers["Content-Length"] = "1";
1471-
httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(invalidDataException);
1471+
httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(jsonException);
14721472
httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
14731473

14741474
var factoryResult = RequestDelegateFactory.Create(TestAction);
@@ -1485,11 +1485,11 @@ void TestAction([FromBody] Todo todo)
14851485
Assert.Equal(new EventId(2, "InvalidJsonRequestBody"), logMessage.EventId);
14861486
Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
14871487
Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", logMessage.Message);
1488-
Assert.Same(invalidDataException, logMessage.Exception);
1488+
Assert.Same(jsonException, logMessage.Exception);
14891489
}
14901490

14911491
[Fact]
1492-
public async Task RequestDelegateLogsFromBodyJsonExceptionAsDebugAndSets400Response()
1492+
public async Task RequestDelegateThrowsForJsonExceptionsIfThrowOnBadRequest()
14931493
{
14941494
var invoked = false;
14951495

@@ -1498,31 +1498,36 @@ void TestAction([FromBody] Todo todo)
14981498
invoked = true;
14991499
}
15001500

1501+
var jsonException = new JsonException();
1502+
15011503
var httpContext = CreateHttpContext();
15021504
httpContext.Request.Headers["Content-Type"] = "application/json";
15031505
httpContext.Request.Headers["Content-Length"] = "1";
1504-
httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{"));
1506+
httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(jsonException);
15051507
httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
15061508

1507-
var factoryResult = RequestDelegateFactory.Create(TestAction);
1509+
var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = true });
15081510
var requestDelegate = factoryResult.RequestDelegate;
15091511

1510-
await requestDelegate(httpContext);
1512+
var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
15111513

15121514
Assert.False(invoked);
1515+
1516+
// The httpContext should be untouched.
15131517
Assert.False(httpContext.RequestAborted.IsCancellationRequested);
1514-
Assert.Equal(400, httpContext.Response.StatusCode);
1518+
Assert.Equal(200, httpContext.Response.StatusCode);
15151519
Assert.False(httpContext.Response.HasStarted);
15161520

1517-
var logMessage = Assert.Single(TestSink.Writes);
1518-
Assert.Equal(new EventId(2, "InvalidJsonRequestBody"), logMessage.EventId);
1519-
Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
1520-
Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", logMessage.Message);
1521-
Assert.IsType<JsonException>(logMessage.Exception);
1521+
// We don't log bad requests when we throw.
1522+
Assert.Empty(TestSink.Writes);
1523+
1524+
Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", badHttpRequestException.Message);
1525+
Assert.Equal(400, badHttpRequestException.StatusCode);
1526+
Assert.Same(jsonException, badHttpRequestException.InnerException);
15221527
}
15231528

15241529
[Fact]
1525-
public async Task RequestDelegateLogsFromBodyInvalidDataExceptionsAsDebugAndThrowsIfThrowOnBadRequest()
1530+
public async Task RequestDelegateLogsMalformedJsonAsDebugAndSets400Response()
15261531
{
15271532
var invoked = false;
15281533

@@ -1531,36 +1536,31 @@ void TestAction([FromBody] Todo todo)
15311536
invoked = true;
15321537
}
15331538

1534-
var invalidDataException = new InvalidDataException();
1535-
15361539
var httpContext = CreateHttpContext();
15371540
httpContext.Request.Headers["Content-Type"] = "application/json";
15381541
httpContext.Request.Headers["Content-Length"] = "1";
1539-
httpContext.Request.Body = new ExceptionThrowingRequestBodyStream(invalidDataException);
1542+
httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("{"));
15401543
httpContext.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(true));
15411544

1542-
var factoryResult = RequestDelegateFactory.Create(TestAction, new() { ThrowOnBadRequest = true });
1545+
var factoryResult = RequestDelegateFactory.Create(TestAction);
15431546
var requestDelegate = factoryResult.RequestDelegate;
15441547

1545-
var badHttpRequestException = await Assert.ThrowsAsync<BadHttpRequestException>(() => requestDelegate(httpContext));
1548+
await requestDelegate(httpContext);
15461549

15471550
Assert.False(invoked);
1548-
1549-
// The httpContext should be untouched.
15501551
Assert.False(httpContext.RequestAborted.IsCancellationRequested);
1551-
Assert.Equal(200, httpContext.Response.StatusCode);
1552+
Assert.Equal(400, httpContext.Response.StatusCode);
15521553
Assert.False(httpContext.Response.HasStarted);
15531554

1554-
// We don't log bad requests when we throw.
1555-
Assert.Empty(TestSink.Writes);
1556-
1557-
Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", badHttpRequestException.Message);
1558-
Assert.Equal(400, badHttpRequestException.StatusCode);
1559-
Assert.Same(invalidDataException, badHttpRequestException.InnerException);
1555+
var logMessage = Assert.Single(TestSink.Writes);
1556+
Assert.Equal(new EventId(2, "InvalidJsonRequestBody"), logMessage.EventId);
1557+
Assert.Equal(LogLevel.Debug, logMessage.LogLevel);
1558+
Assert.Equal(@"Failed to read parameter ""Todo todo"" from the request body as JSON.", logMessage.Message);
1559+
Assert.IsType<JsonException>(logMessage.Exception);
15601560
}
15611561

15621562
[Fact]
1563-
public async Task RequestDelegateLogsFromBodyJsonExceptionsAsDebugAndThrowsIfThrowOnBadRequest()
1563+
public async Task RequestDelegateThrowsForMalformedJsonIfThrowOnBadRequest()
15641564
{
15651565
var invoked = false;
15661566

0 commit comments

Comments
 (0)