Handle exceptions for invalid operation types
Related to https://github.com/aspnet/Mvc/issues/5463
This commit is contained in:
parent
04c0056c6a
commit
81931e75d4
|
|
@ -150,14 +150,14 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
|||
if (!visitor.TryVisit(ref target, out adapter, out errorMessage))
|
||||
{
|
||||
var error = CreatePathNotFoundError(objectToApplyTo, path, operation, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!adapter.TryAdd(target, parsedPath.LastSegment, ContractResolver, value, out errorMessage))
|
||||
{
|
||||
var error = CreateOperationFailedError(objectToApplyTo, path, operation, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -259,14 +259,14 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
|||
if (!visitor.TryVisit(ref target, out adapter, out errorMessage))
|
||||
{
|
||||
var error = CreatePathNotFoundError(objectToApplyTo, path, operationToReport, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!adapter.TryRemove(target, parsedPath.LastSegment, ContractResolver, out errorMessage))
|
||||
{
|
||||
var error = CreateOperationFailedError(objectToApplyTo, path, operationToReport, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -312,14 +312,14 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
|||
if (!visitor.TryVisit(ref target, out adapter, out errorMessage))
|
||||
{
|
||||
var error = CreatePathNotFoundError(objectToApplyTo, operation.path, operation, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!adapter.TryReplace(target, parsedPath.LastSegment, ContractResolver, operation.value, out errorMessage))
|
||||
{
|
||||
var error = CreateOperationFailedError(objectToApplyTo, operation.path, operation, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -401,29 +401,25 @@ namespace Microsoft.AspNetCore.JsonPatch.Adapters
|
|||
if (!visitor.TryVisit(ref target, out adapter, out errorMessage))
|
||||
{
|
||||
var error = CreatePathNotFoundError(objectToGetValueFrom, fromLocation, operation, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!adapter.TryGet(target, parsedPath.LastSegment, ContractResolver, out propertyValue, out errorMessage))
|
||||
{
|
||||
var error = CreateOperationFailedError(objectToGetValueFrom, fromLocation, operation, errorMessage);
|
||||
ReportError(error);
|
||||
ErrorReporter(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ReportError(JsonPatchError error)
|
||||
private Action<JsonPatchError> ErrorReporter
|
||||
{
|
||||
if (LogErrorAction != null)
|
||||
get
|
||||
{
|
||||
LogErrorAction(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new JsonPatchException(error);
|
||||
return LogErrorAction ?? Internal.ErrorReporter.Default;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.JsonPatch.Exceptions;
|
||||
|
||||
namespace Microsoft.AspNetCore.JsonPatch.Internal
|
||||
{
|
||||
internal static class ErrorReporter
|
||||
{
|
||||
public static readonly Action<JsonPatchError> Default = (error) =>
|
||||
{
|
||||
throw new JsonPatchException(error);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.JsonPatch.Adapters;
|
||||
using Microsoft.AspNetCore.JsonPatch.Converters;
|
||||
using Microsoft.AspNetCore.JsonPatch.Exceptions;
|
||||
using Microsoft.AspNetCore.JsonPatch.Internal;
|
||||
using Microsoft.AspNetCore.JsonPatch.Operations;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -170,7 +171,22 @@ namespace Microsoft.AspNetCore.JsonPatch
|
|||
throw new ArgumentNullException(nameof(objectToApplyTo));
|
||||
}
|
||||
|
||||
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction));
|
||||
var adapter = new ObjectAdapter(ContractResolver, logErrorAction);
|
||||
foreach (var op in Operations)
|
||||
{
|
||||
try
|
||||
{
|
||||
op.Apply(objectToApplyTo, adapter);
|
||||
}
|
||||
catch (JsonPatchException jsonPatchException)
|
||||
{
|
||||
var errorReporter = logErrorAction ?? ErrorReporter.Default;
|
||||
errorReporter(new JsonPatchError(objectToApplyTo, op, jsonPatchException.Message));
|
||||
|
||||
// As per JSON Patch spec if an operation results in error, further operations should not be executed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.JsonPatch.Adapters;
|
||||
using Microsoft.AspNetCore.JsonPatch.Converters;
|
||||
using Microsoft.AspNetCore.JsonPatch.Exceptions;
|
||||
using Microsoft.AspNetCore.JsonPatch.Internal;
|
||||
using Microsoft.AspNetCore.JsonPatch.Operations;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -648,7 +649,22 @@ namespace Microsoft.AspNetCore.JsonPatch
|
|||
throw new ArgumentNullException(nameof(objectToApplyTo));
|
||||
}
|
||||
|
||||
ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction));
|
||||
var adapter = new ObjectAdapter(ContractResolver, logErrorAction);
|
||||
foreach (var op in Operations)
|
||||
{
|
||||
try
|
||||
{
|
||||
op.Apply(objectToApplyTo, adapter);
|
||||
}
|
||||
catch (JsonPatchException jsonPatchException)
|
||||
{
|
||||
var errorReporter = logErrorAction ?? ErrorReporter.Default;
|
||||
errorReporter(new JsonPatchError(objectToApplyTo, op, jsonPatchException.Message));
|
||||
|
||||
// As per JSON Patch spec if an operation results in error, further operations should not be executed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.JsonPatch.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.JsonPatch.Operations
|
||||
|
|
@ -13,7 +14,14 @@ namespace Microsoft.AspNetCore.JsonPatch.Operations
|
|||
{
|
||||
get
|
||||
{
|
||||
return (OperationType)Enum.Parse(typeof(OperationType), op, true);
|
||||
OperationType result;
|
||||
if (!Enum.TryParse(op, ignoreCase: true, result: out result))
|
||||
{
|
||||
throw new JsonPatchException(
|
||||
Resources.FormatInvalidJsonPatchOperation(op),
|
||||
innerException: null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.JsonPatch.Adapters;
|
||||
using Microsoft.AspNetCore.JsonPatch.Exceptions;
|
||||
|
||||
namespace Microsoft.AspNetCore.JsonPatch.Operations
|
||||
{
|
||||
|
|
@ -73,7 +74,7 @@ namespace Microsoft.AspNetCore.JsonPatch.Operations
|
|||
adapter.Copy(this, objectToApplyTo);
|
||||
break;
|
||||
case OperationType.Test:
|
||||
throw new NotSupportedException(Resources.TestOperationNotSupported);
|
||||
throw new JsonPatchException(new JsonPatchError(objectToApplyTo, this, Resources.TestOperationNotSupported));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,22 @@ namespace Microsoft.AspNetCore.JsonPatch
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidJsonPatchDocument"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid JsonPatch operation '{0}'.
|
||||
/// </summary>
|
||||
internal static string InvalidJsonPatchOperation
|
||||
{
|
||||
get { return GetString("InvalidJsonPatchOperation"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid JsonPatch operation '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatInvalidJsonPatchOperation(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidJsonPatchOperation"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The provided string '{0}' is an invalid path.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -138,6 +138,9 @@
|
|||
<data name="InvalidJsonPatchDocument" xml:space="preserve">
|
||||
<value>The type '{0}' was malformed and could not be parsed.</value>
|
||||
</data>
|
||||
<data name="InvalidJsonPatchOperation" xml:space="preserve">
|
||||
<value>Invalid JsonPatch operation '{0}'.</value>
|
||||
</data>
|
||||
<data name="InvalidValueForPath" xml:space="preserve">
|
||||
<value>The provided string '{0}' is an invalid path.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.JsonPatch.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.JsonPatch.Test
|
||||
{
|
||||
public class JsonPatchDocumentTest
|
||||
{
|
||||
[Fact]
|
||||
public void TestOperation_ThrowsException_CallsIntoLogErrorAction()
|
||||
{
|
||||
// Arrange
|
||||
var serialized = "[{\"value\":\"John\",\"path\":\"/Name\",\"op\":\"test\"}]";
|
||||
var jsonPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument<Customer>>(serialized);
|
||||
var model = new Customer();
|
||||
var expectedErrorMessage = "The test operation is not supported.";
|
||||
string actualErrorMessage = null;
|
||||
|
||||
// Act
|
||||
jsonPatchDocument.ApplyTo(model, (jsonPatchError) =>
|
||||
{
|
||||
actualErrorMessage = jsonPatchError.ErrorMessage;
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedErrorMessage, actualErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestOperation_NoLogErrorAction_ThrowsJsonPatchException()
|
||||
{
|
||||
// Arrange
|
||||
var serialized = "[{\"value\":\"John\",\"path\":\"/Name\",\"op\":\"test\"}]";
|
||||
var jsonPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument<Customer>>(serialized);
|
||||
var model = new Customer();
|
||||
var expectedErrorMessage = "The test operation is not supported.";
|
||||
|
||||
// Act
|
||||
var jsonPatchException = Assert.Throws<JsonPatchException>(() => jsonPatchDocument.ApplyTo(model));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedErrorMessage, jsonPatchException.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidOperation_ThrowsException_CallsIntoLogErrorAction()
|
||||
{
|
||||
// Arrange
|
||||
var operationName = "foo";
|
||||
var serialized = "[{\"value\":\"John\",\"path\":\"/Name\",\"op\":\"" + operationName + "\"}]";
|
||||
var jsonPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument<Customer>>(serialized);
|
||||
var model = new Customer();
|
||||
var expectedErrorMessage = $"Invalid JsonPatch operation '{operationName}'.";
|
||||
string actualErrorMessage = null;
|
||||
|
||||
// Act
|
||||
jsonPatchDocument.ApplyTo(model, (jsonPatchError) =>
|
||||
{
|
||||
actualErrorMessage = jsonPatchError.ErrorMessage;
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedErrorMessage, actualErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvalidOperation_NoLogErrorAction_ThrowsJsonPatchException()
|
||||
{
|
||||
// Arrange
|
||||
var operationName = "foo";
|
||||
var serialized = "[{\"value\":\"John\",\"path\":\"/Name\",\"op\":\"" + operationName + "\"}]";
|
||||
var jsonPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument<Customer>>(serialized);
|
||||
var model = new Customer();
|
||||
var expectedErrorMessage = $"Invalid JsonPatch operation '{operationName}'.";
|
||||
|
||||
// Act
|
||||
var jsonPatchException = Assert.Throws<JsonPatchException>(() => jsonPatchDocument.ApplyTo(model));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedErrorMessage, jsonPatchException.Message);
|
||||
}
|
||||
|
||||
private class Customer
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue