Skip to content

Commit 5e08f80

Browse files
authored
Backport 39275 to release/6.0 (#41993)
* Backport 39275 to release/6.0 * Cache appcontext switch value * PR feedback
1 parent 8b51ef5 commit 5e08f80

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonInputFormatter.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
2323
/// </summary>
2424
public class NewtonsoftJsonInputFormatter : TextInputFormatter, IInputFormatterExceptionPolicy
2525
{
26+
internal const string EnableSkipHandledError = "Microsoft.AspNetCore.Mvc.NewtonsoftJson.EnableSkipHandledError";
27+
2628
private readonly IArrayPool<char> _charPool;
2729
private readonly ILogger _logger;
2830
private readonly ObjectPoolProvider _objectPoolProvider;
@@ -79,6 +81,8 @@ public NewtonsoftJsonInputFormatter(
7981
_options = options;
8082
_jsonOptions = jsonOptions;
8183

84+
SkipHandledErrorEnabled = AppContext.TryGetSwitch(EnableSkipHandledError, out var enabled) && enabled;
85+
8286
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
8387
SupportedEncodings.Add(UTF16EncodingLittleEndian);
8488

@@ -109,6 +113,9 @@ public virtual InputFormatterExceptionPolicy ExceptionPolicy
109113
/// </remarks>
110114
protected JsonSerializerSettings SerializerSettings { get; }
111115

116+
// internal for testing
117+
internal bool SkipHandledErrorEnabled { get; set; }
118+
112119
/// <inheritdoc />
113120
public override async Task<InputFormatterResult> ReadRequestBodyAsync(
114121
InputFormatterContext context,
@@ -232,6 +239,13 @@ public override async Task<InputFormatterResult> ReadRequestBodyAsync(
232239

233240
void ErrorHandler(object? sender, Newtonsoft.Json.Serialization.ErrorEventArgs eventArgs)
234241
{
242+
// Skipping error, if it's already marked as handled
243+
// This allows user code to implement its own error handling
244+
if (eventArgs.ErrorContext.Handled && SkipHandledErrorEnabled)
245+
{
246+
return;
247+
}
248+
235249
successful = false;
236250

237251
// When ErrorContext.Path does not include ErrorContext.Member, add Member to form full path.

src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonInputFormatterTest.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,47 @@ public async Task ReadAsync_WithReadJsonWithRequestCulture_DeserializesUsingRequ
487487
}
488488
}
489489

490+
[Fact]
491+
public async Task ReadAsync_AllowUserCodeToHandleDeserializationErrors()
492+
{
493+
// Arrange
494+
var serializerSettings = new JsonSerializerSettings
495+
{
496+
Error = (sender, eventArgs) =>
497+
{
498+
eventArgs.ErrorContext.Handled = true;
499+
}
500+
};
501+
var formatter = new NewtonsoftJsonInputFormatter(
502+
GetLogger(),
503+
serializerSettings,
504+
ArrayPool<char>.Shared,
505+
_objectPoolProvider,
506+
new MvcOptions(),
507+
new MvcNewtonsoftJsonOptions())
508+
{
509+
SkipHandledErrorEnabled = true
510+
};
511+
512+
var content = $"{{'id': 'should be integer', 'name': 'test location'}}";
513+
var contentBytes = Encoding.UTF8.GetBytes(content);
514+
var httpContext = new DefaultHttpContext();
515+
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
516+
httpContext.Request.Body = new NonSeekableReadStream(contentBytes, allowSyncReads: false);
517+
httpContext.Request.ContentType = "application/json";
518+
519+
var formatterContext = CreateInputFormatterContext(typeof(Location), httpContext);
520+
521+
// Act
522+
var result = await formatter.ReadAsync(formatterContext);
523+
524+
// Assert
525+
Assert.False(result.HasError);
526+
var location = (Location)result.Model;
527+
Assert.Equal(0, location?.Id);
528+
Assert.Equal("test location", location?.Name);
529+
}
530+
490531
private class TestableJsonInputFormatter : NewtonsoftJsonInputFormatter
491532
{
492533
public TestableJsonInputFormatter(JsonSerializerSettings settings, ObjectPoolProvider objectPoolProvider)

0 commit comments

Comments
 (0)