Adding errors in ModelState
This commit is contained in:
parent
9f97d25e02
commit
e48565dcd8
17
Mvc.sln
17
Mvc.sln
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.22726.0
|
VisualStudioVersion = 14.0.22806.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -160,6 +160,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.JsonPatch.
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.JsonPatch", "src\Microsoft.AspNet.JsonPatch\Microsoft.AspNet.JsonPatch.xproj", "{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.JsonPatch", "src\Microsoft.AspNet.JsonPatch\Microsoft.AspNet.JsonPatch.xproj", "{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "JsonPatchWebSite", "test\WebSites\JsonPatchWebSite\JsonPatchWebSite.xproj", "{DAB1252D-577C-4912-98BE-1A812BF83F86}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -950,6 +952,18 @@ Global
|
||||||
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|x86.ActiveCfg = Release|Any CPU
|
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|x86.Build.0 = Release|Any CPU
|
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -1028,5 +1042,6 @@ Global
|
||||||
{B42D4844-FFF8-4EC2-88D1-3AE95234D9EB} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{B42D4844-FFF8-4EC2-88D1-3AE95234D9EB} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{81C20848-E063-4E12-AC40-0B55A532C16C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{81C20848-E063-4E12-AC40-0B55A532C16C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
||||||
|
{DAB1252D-577C-4912-98BE-1A812BF83F86} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNet.JsonPatch;
|
using Microsoft.AspNet.JsonPatch;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
|
@ -34,7 +35,12 @@ namespace MvcSample.Web.Controllers
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
patchDoc.ApplyTo(customer);
|
patchDoc.ApplyTo(customer, ModelState);
|
||||||
|
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return HttpBadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
return new ObjectResult(customer);
|
return new ObjectResult(customer);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
"Orders": [{"OrderName": "Order1"},
|
"Orders": [{"OrderName": "Order1"},
|
||||||
{"OrderName": "Order2"}]
|
{"OrderName": "Order2"}]
|
||||||
};
|
};
|
||||||
$('#currentlabel').text(JSON.stringify(currentObj))
|
$('#currentLabel').text(JSON.stringify(currentObj))
|
||||||
|
|
||||||
$('#addorder').on("click", function () {
|
$('#addOrder').on("click", function () {
|
||||||
var obj = [{
|
var obj = [{
|
||||||
"op": "Add",
|
"op": "Add",
|
||||||
"path": "Customer/Orders/2",
|
"path": "Customer/Orders/2",
|
||||||
|
|
@ -22,12 +22,34 @@
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json-patch+json',
|
contentType: 'application/json-patch+json',
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$('#addlabel').text(JSON.stringify(data))
|
$('#addLabel').text(JSON.stringify(data))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#removeorder').on("click", function () {
|
$('#invalidAddOrder').on("click", function () {
|
||||||
|
var obj = [{
|
||||||
|
"op": "Add",
|
||||||
|
"path": "Customer/Orders/5",
|
||||||
|
"value": { "OrderName": "Name5" }
|
||||||
|
}]
|
||||||
|
$.ajax({
|
||||||
|
url: 'jsonpatch',
|
||||||
|
type: 'PATCH',
|
||||||
|
data: JSON.stringify(obj),
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: 'application/json-patch+json',
|
||||||
|
success: function (data) {
|
||||||
|
$('#invalidAddLabel').text(data)
|
||||||
|
},
|
||||||
|
error: function (request, status, error) {
|
||||||
|
$('#invalidAddLabel').text(error)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#removeOrder').on("click", function () {
|
||||||
var obj = [{
|
var obj = [{
|
||||||
"op": "Remove",
|
"op": "Remove",
|
||||||
"path": "Customer/Name"
|
"path": "Customer/Name"
|
||||||
|
|
@ -39,12 +61,12 @@
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json-patch+json',
|
contentType: 'application/json-patch+json',
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$('#removelabel').text(JSON.stringify(data))
|
$('#removeLabel').text(JSON.stringify(data))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#moveorder').on("click", function () {
|
$('#moveOrder').on("click", function () {
|
||||||
var obj = [{
|
var obj = [{
|
||||||
"op": "Move",
|
"op": "Move",
|
||||||
"from": "Customer/Orders/0",
|
"from": "Customer/Orders/0",
|
||||||
|
|
@ -57,12 +79,12 @@
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json-patch+json',
|
contentType: 'application/json-patch+json',
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$('#movelabel').text(JSON.stringify(data))
|
$('#moveLabel').text(JSON.stringify(data))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#replaceorder').on("click", function () {
|
$('#replaceOrder').on("click", function () {
|
||||||
var obj = [{
|
var obj = [{
|
||||||
"op": "Replace",
|
"op": "Replace",
|
||||||
"path": "Customer/Name",
|
"path": "Customer/Name",
|
||||||
|
|
@ -75,7 +97,7 @@
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json-patch+json',
|
contentType: 'application/json-patch+json',
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$('#replacelabel').text(JSON.stringify(data))
|
$('#replaceLabel').text(JSON.stringify(data))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -86,29 +108,35 @@
|
||||||
<div>
|
<div>
|
||||||
<label>Current Customer</label>
|
<label>Current Customer</label>
|
||||||
<br />
|
<br />
|
||||||
<label id="currentlabel" name="currentlabel"></label>
|
<label id="currentLabel" name="currentLabel"></label>
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button id="addorder" type="submit" class="btn btn-primary"> Add </button>
|
<button id="addOrder" type="submit" class="btn btn-primary"> Add </button>
|
||||||
<br />
|
<br />
|
||||||
<label id="addlabel" name="addlabel"></label>
|
<label id="addLabel" name="addLabel"></label>
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button id="removeorder" type="submit" class="btn btn-primary"> Remove </button>
|
<button id="invalidAddOrder" type="submit" class="btn btn-primary"> Invalid Add </button>
|
||||||
<br />
|
<br />
|
||||||
<label id="removelabel" name="removelabel"></label>
|
<label id="invalidAddLabel" name="invalidAddLabel"></label>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button id="moveorder" type="submit" class="btn btn-primary"> Move </button>
|
|
||||||
<br />
|
|
||||||
<label id="movelabel" name="movelabel"></label>
|
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button id="replaceorder" type="submit" class="btn btn-primary"> Replace </button>
|
<button id="removeOrder" type="submit" class="btn btn-primary"> Remove </button>
|
||||||
<br />
|
<br />
|
||||||
<label id="replacelabel" name="replacelabel"></label>
|
<label id="removeLabel" name="removeLabel"></label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="moveOrder" type="submit" class="btn btn-primary"> Move </button>
|
||||||
|
<br />
|
||||||
|
<label id="moveLabel" name="moveLabel"></label>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="replaceOrder" type="submit" class="btn btn-primary"> Replace </button>
|
||||||
|
<br />
|
||||||
|
<label id="replaceLabel" name="replaceLabel"></label>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.JsonPatch.Operations;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.JsonPatch.Adapters
|
namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
{
|
{
|
||||||
public interface IObjectAdapter<T>
|
/// <summary>
|
||||||
where T : class
|
/// Defines the operations that can be performed on a JSON patch document.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public interface IObjectAdapter<T> where T : class
|
||||||
{
|
{
|
||||||
void Add(Microsoft.AspNet.JsonPatch.Operations.Operation<T> operation, T objectToApplyTo);
|
void Add(Operation<T> operation, T objectToApplyTo);
|
||||||
void Copy(Microsoft.AspNet.JsonPatch.Operations.Operation<T> operation, T objectToApplyTo);
|
void Copy(Operation<T> operation, T objectToApplyTo);
|
||||||
void Move(Microsoft.AspNet.JsonPatch.Operations.Operation<T> operation, T objectToApplyTo);
|
void Move(Operation<T> operation, T objectToApplyTo);
|
||||||
void Remove(Microsoft.AspNet.JsonPatch.Operations.Operation<T> operation, T objectToApplyTo);
|
void Remove(Operation<T> operation, T objectToApplyTo);
|
||||||
void Replace(Microsoft.AspNet.JsonPatch.Operations.Operation<T> operation, T objectToApplyTo);
|
void Replace(Operation<T> operation, T objectToApplyTo);
|
||||||
void Test(Microsoft.AspNet.JsonPatch.Operations.Operation<T> operation, T objectToApplyTo);
|
void Test(Operation<T> operation, T objectToApplyTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,20 +7,34 @@ using System.Reflection;
|
||||||
using Microsoft.AspNet.JsonPatch.Exceptions;
|
using Microsoft.AspNet.JsonPatch.Exceptions;
|
||||||
using Microsoft.AspNet.JsonPatch.Helpers;
|
using Microsoft.AspNet.JsonPatch.Helpers;
|
||||||
using Microsoft.AspNet.JsonPatch.Operations;
|
using Microsoft.AspNet.JsonPatch.Operations;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.JsonPatch.Adapters
|
namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
public class ObjectAdapter<T> : IObjectAdapter<T> where T : class
|
public class ObjectAdapter<T> : IObjectAdapter<T> where T : class
|
||||||
{
|
{
|
||||||
public IContractResolver ContractResolver { get; set; }
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ObjectAdapter{T}"/>.
|
||||||
public ObjectAdapter(IContractResolver contractResolver)
|
/// </summary>
|
||||||
|
/// <param name="contractResolver">The <see cref="IContractResolver"/>.</param>
|
||||||
|
/// <param name="logErrorAction">The <see cref="Action"/> for logging <see cref="JsonPatchError{T}"/>.</param>
|
||||||
|
public ObjectAdapter(IContractResolver contractResolver, Action<JsonPatchError<T>> logErrorAction)
|
||||||
{
|
{
|
||||||
ContractResolver = contractResolver;
|
ContractResolver = contractResolver;
|
||||||
|
LogErrorAction = logErrorAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="IContractResolver"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IContractResolver ContractResolver { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Action for logging <see cref="JsonPatchError{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Action<JsonPatchError<T>> LogErrorAction { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The "add" operation performs one of the following functions,
|
/// The "add" operation performs one of the following functions,
|
||||||
/// depending upon what the target location references:
|
/// depending upon what the target location references:
|
||||||
|
|
@ -123,7 +137,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualPathToProperty, ContractResolver);
|
.FindPropertyAndParent(objectToApplyTo, actualPathToProperty, ContractResolver);
|
||||||
|
|
||||||
// does property at path exist?
|
// does property at path exist?
|
||||||
CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path);
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// it exists. If it' an array, add to that array. If it's not, we replace.
|
// it exists. If it' an array, add to that array. If it's not, we replace.
|
||||||
// is the path an array (but not a string (= char[]))? In this case,
|
// is the path an array (but not a string (= char[]))? In this case,
|
||||||
|
|
@ -139,7 +156,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
|
|
||||||
var conversionResult = PropertyHelpers.ConvertToActualType(genericTypeOfArray, value);
|
var conversionResult = PropertyHelpers.ConvertToActualType(genericTypeOfArray, value);
|
||||||
|
|
||||||
CheckIfPropertyCanBeSet(conversionResult, objectToApplyTo, operationToReport, path);
|
if (!CheckIfPropertyCanBeSet(conversionResult, objectToApplyTo, operationToReport, path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// get value (it can be cast, we just checked that)
|
// get value (it can be cast, we just checked that)
|
||||||
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
var array = (IList)patchProperty.Property.ValueProvider.GetValue(patchProperty.Parent);
|
||||||
|
|
@ -157,19 +177,23 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operationToReport,
|
operationToReport,
|
||||||
Resources.FormatInvalidIndexForArrayProperty(operationToReport.op, path),
|
Resources.FormatInvalidIndexForArrayProperty(operationToReport.op, path)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operationToReport,
|
operationToReport,
|
||||||
Resources.FormatInvalidPathForArrayProperty(operationToReport.op, path),
|
Resources.FormatInvalidIndexForArrayProperty(operationToReport.op, path)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -179,7 +203,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
value);
|
value);
|
||||||
|
|
||||||
// Is conversion successful
|
// Is conversion successful
|
||||||
CheckIfPropertyCanBeSet(conversionResultTuple, objectToApplyTo, operationToReport, path);
|
if (!CheckIfPropertyCanBeSet(conversionResultTuple, objectToApplyTo, operationToReport, path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
patchProperty.Property.ValueProvider.SetValue(
|
patchProperty.Property.ValueProvider.SetValue(
|
||||||
patchProperty.Parent,
|
patchProperty.Parent,
|
||||||
|
|
@ -229,7 +256,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualFromProperty, ContractResolver);
|
.FindPropertyAndParent(objectToApplyTo, actualFromProperty, ContractResolver);
|
||||||
|
|
||||||
// does property at from exist?
|
// does property at from exist?
|
||||||
CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from);
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// is the path an array (but not a string (= char[]))? In this case,
|
// is the path an array (but not a string (= char[]))? In this case,
|
||||||
// the path must end with "/position" or "/-", which we already determined before.
|
// the path must end with "/position" or "/-", which we already determined before.
|
||||||
|
|
@ -245,20 +275,24 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
|
|
||||||
if (array.Count <= positionAsInteger)
|
if (array.Count <= positionAsInteger)
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatInvalidIndexForArrayProperty(operation.op, operation.from),
|
Resources.FormatInvalidIndexForArrayProperty(operation.op, operation.from)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueAtFromLocation = array[positionAsInteger];
|
valueAtFromLocation = array[positionAsInteger];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatInvalidPathForArrayProperty(operation.op, operation.from),
|
Resources.FormatInvalidPathForArrayProperty(operation.op, operation.from)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -324,7 +358,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualPathToProperty, ContractResolver);
|
.FindPropertyAndParent(objectToApplyTo, actualPathToProperty, ContractResolver);
|
||||||
|
|
||||||
// does the target location exist?
|
// does the target location exist?
|
||||||
CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path);
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operationToReport, path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// get the property, and remove it - in this case, for DTO's, that means setting
|
// get the property, and remove it - in this case, for DTO's, that means setting
|
||||||
// it to null or its default value; in case of an array, remove at provided index
|
// it to null or its default value; in case of an array, remove at provided index
|
||||||
|
|
@ -352,19 +389,23 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operationToReport,
|
operationToReport,
|
||||||
Resources.FormatInvalidIndexForArrayProperty(operationToReport.op, path),
|
Resources.FormatInvalidIndexForArrayProperty(operationToReport.op, path)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operationToReport,
|
operationToReport,
|
||||||
Resources.FormatInvalidPathForArrayProperty(operationToReport.op, path),
|
Resources.FormatInvalidPathForArrayProperty(operationToReport.op, path)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -451,10 +492,13 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualPathProperty, ContractResolver);
|
.FindPropertyAndParent(objectToApplyTo, actualPathProperty, ContractResolver);
|
||||||
|
|
||||||
// does property at path exist?
|
// does property at path exist?
|
||||||
CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.path);
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// get the property path
|
// get the property path
|
||||||
Type typeOfFinalPropertyAtPathLocation;
|
Type typeOfFinalPropertyAtPathLocation = null;
|
||||||
|
|
||||||
// is the path an array (but not a string (= char[]))? In this case,
|
// is the path an array (but not a string (= char[]))? In this case,
|
||||||
// the path must end with "/position" or "/-", which we already determined before.
|
// the path must end with "/position" or "/-", which we already determined before.
|
||||||
|
|
@ -471,20 +515,24 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
|
|
||||||
if (array.Count <= positionInPathAsInteger)
|
if (array.Count <= positionInPathAsInteger)
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatInvalidIndexForArrayProperty(operation.op, operation.path),
|
Resources.FormatInvalidIndexForArrayProperty(operation.op, operation.path)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueAtPathLocation = array[positionInPathAsInteger];
|
valueAtPathLocation = array[positionInPathAsInteger];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatInvalidPathForArrayProperty(operation.op, operation.path),
|
Resources.FormatInvalidPathForArrayProperty(operation.op, operation.path)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -499,7 +547,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
operation.value);
|
operation.value);
|
||||||
|
|
||||||
// Is conversion successful
|
// Is conversion successful
|
||||||
CheckIfPropertyCanBeSet(conversionResultTuple, objectToApplyTo, operation, operation.path);
|
if (!CheckIfPropertyCanBeSet(conversionResultTuple, objectToApplyTo, operation, operation.path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Compare
|
//Compare
|
||||||
}
|
}
|
||||||
|
|
@ -571,7 +622,10 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
.FindPropertyAndParent(objectToApplyTo, actualFromProperty, ContractResolver);
|
.FindPropertyAndParent(objectToApplyTo, actualFromProperty, ContractResolver);
|
||||||
|
|
||||||
// does property at from exist?
|
// does property at from exist?
|
||||||
CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from);
|
if (!CheckIfPropertyExists(patchProperty, objectToApplyTo, operation, operation.from))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// get the property path
|
// get the property path
|
||||||
// is the path an array (but not a string (= char[]))? In this case,
|
// is the path an array (but not a string (= char[]))? In this case,
|
||||||
|
|
@ -588,19 +642,24 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
|
|
||||||
if (array.Count <= positionAsInteger)
|
if (array.Count <= positionAsInteger)
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(operation,
|
LogError(new JsonPatchError<T>(
|
||||||
Resources.FormatInvalidIndexForArrayProperty(operation.op, operation.from),
|
objectToApplyTo,
|
||||||
objectToApplyTo);
|
operation,
|
||||||
|
Resources.FormatInvalidIndexForArrayProperty(operation.op, operation.from)));
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueAtFromLocation = array[positionAsInteger];
|
valueAtFromLocation = array[positionAsInteger];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatInvalidPathForArrayProperty(operation.op, operation.from),
|
Resources.FormatInvalidPathForArrayProperty(operation.op, operation.from)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -614,7 +673,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
Add(operation.path, valueAtFromLocation, objectToApplyTo, operation);
|
Add(operation.path, valueAtFromLocation, objectToApplyTo, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckIfPropertyExists(
|
private bool CheckIfPropertyExists(
|
||||||
JsonPatchProperty patchProperty,
|
JsonPatchProperty patchProperty,
|
||||||
T objectToApplyTo,
|
T objectToApplyTo,
|
||||||
Operation<T> operation,
|
Operation<T> operation,
|
||||||
|
|
@ -622,18 +681,25 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
{
|
{
|
||||||
if (patchProperty == null)
|
if (patchProperty == null)
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatPropertyDoesNotExist(propertyPath),
|
Resources.FormatPropertyDoesNotExist(propertyPath)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patchProperty.Property.Ignored)
|
if (patchProperty.Property.Ignored)
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatCannotUpdateProperty(propertyPath),
|
Resources.FormatCannotUpdateProperty(propertyPath)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsNonStringArray(JsonPatchProperty patchProperty)
|
private bool IsNonStringArray(JsonPatchProperty patchProperty)
|
||||||
|
|
@ -643,7 +709,7 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
patchProperty.Property.PropertyType.GetTypeInfo());
|
patchProperty.Property.PropertyType.GetTypeInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckIfPropertyCanBeSet(
|
private bool CheckIfPropertyCanBeSet(
|
||||||
ConversionResult result,
|
ConversionResult result,
|
||||||
T objectToApplyTo,
|
T objectToApplyTo,
|
||||||
Operation<T> operation,
|
Operation<T> operation,
|
||||||
|
|
@ -651,10 +717,26 @@ namespace Microsoft.AspNet.JsonPatch.Adapters
|
||||||
{
|
{
|
||||||
if (!result.CanBeConverted)
|
if (!result.CanBeConverted)
|
||||||
{
|
{
|
||||||
throw new JsonPatchException<T>(
|
LogError(new JsonPatchError<T>(
|
||||||
|
objectToApplyTo,
|
||||||
operation,
|
operation,
|
||||||
Resources.FormatInvalidValueForProperty(result.ConvertedInstance, path),
|
Resources.FormatInvalidValueForProperty(result.ConvertedInstance, path)));
|
||||||
objectToApplyTo);
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogError(JsonPatchError<T> jsonPatchError)
|
||||||
|
{
|
||||||
|
if (LogErrorAction != null)
|
||||||
|
{
|
||||||
|
LogErrorAction(jsonPatchError);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new JsonPatchException<T>(jsonPatchError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,15 @@ namespace Microsoft.AspNet.JsonPatch.Exceptions
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonPatchException(Operation<T> operation, string message, T affectedObject)
|
public JsonPatchException(JsonPatchError<T> jsonPatchError)
|
||||||
{
|
{
|
||||||
FailedOperation = operation;
|
FailedOperation = jsonPatchError.Operation;
|
||||||
_message = message;
|
_message = jsonPatchError.ErrorMessage;
|
||||||
AffectedObject = affectedObject;
|
AffectedObject = jsonPatchError.AffectedObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonPatchException(Operation<T> operation, string message, T affectedObject, Exception innerException)
|
public JsonPatchException(JsonPatchError<T> jsonPatchError, Exception innerException)
|
||||||
: this(operation, message, affectedObject)
|
: this(jsonPatchError)
|
||||||
{
|
{
|
||||||
InnerException = innerException;
|
InnerException = innerException;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.JsonPatch.Operations;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.JsonPatch
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Captures error message and the related entity and the operation that caused it.
|
||||||
|
/// </summary>
|
||||||
|
public class JsonPatchError<T> where T : class
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="JsonPatchError{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="affectedObject">The object that is affected by the error.</param>
|
||||||
|
/// <param name="operation">The <see cref="Operation{T}"/> that caused the error.</param>
|
||||||
|
/// <param name="errorMessage">The error message.</param>
|
||||||
|
public JsonPatchError(
|
||||||
|
[NotNull] T affectedObject,
|
||||||
|
[NotNull] Operation<T> operation,
|
||||||
|
[NotNull] string errorMessage)
|
||||||
|
{
|
||||||
|
AffectedObject = affectedObject;
|
||||||
|
Operation = operation;
|
||||||
|
ErrorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the object that is affected by the error.
|
||||||
|
/// </summary>
|
||||||
|
public T AffectedObject { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="Operation{T}"/> that caused the error.
|
||||||
|
/// </summary>
|
||||||
|
public Operation<T> Operation { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the error message.
|
||||||
|
/// </summary>
|
||||||
|
public string ErrorMessage { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -354,12 +354,16 @@ namespace Microsoft.AspNet.JsonPatch
|
||||||
|
|
||||||
public void ApplyTo(T objectToApplyTo)
|
public void ApplyTo(T objectToApplyTo)
|
||||||
{
|
{
|
||||||
ApplyTo(objectToApplyTo, new ObjectAdapter<T>(ContractResolver));
|
ApplyTo(objectToApplyTo, new ObjectAdapter<T>(ContractResolver, logErrorAction: null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyTo(T objectToApplyTo, Action<JsonPatchError<T>> logErrorAction)
|
||||||
|
{
|
||||||
|
ApplyTo(objectToApplyTo, new ObjectAdapter<T>(ContractResolver, logErrorAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyTo(T objectToApplyTo, IObjectAdapter<T> adapter)
|
public void ApplyTo(T objectToApplyTo, IObjectAdapter<T> adapter)
|
||||||
{
|
{
|
||||||
|
|
||||||
// apply each operation in order
|
// apply each operation in order
|
||||||
foreach (var op in Operations)
|
foreach (var op in Operations)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.JsonPatch;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions for <see cref="JsonPatchDocument{T}"/>
|
||||||
|
/// </summary>
|
||||||
|
public static class JsonPatchExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Applies JSON patch operations on object and logs errors in <see cref="ModelStateDictionary"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="patchDoc">The <see cref="JsonPatchDocument{T}"/>.</param>
|
||||||
|
/// <param name="objectToApplyTo">The entity on which <see cref="JsonPatchDocument{T}"/> is applied.</param>
|
||||||
|
/// <param name="modelState">The <see cref="ModelStateDictionary"/> to add errors.</param>
|
||||||
|
public static void ApplyTo<T>(
|
||||||
|
[NotNull] this JsonPatchDocument<T> patchDoc,
|
||||||
|
[NotNull] T objectToApplyTo,
|
||||||
|
[NotNull] ModelStateDictionary modelState) where T : class
|
||||||
|
{
|
||||||
|
patchDoc.ApplyTo(objectToApplyTo, modelState, prefix: string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies JSON patch operations on object and logs errors in <see cref="ModelStateDictionary"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="patchDoc">The <see cref="JsonPatchDocument{T}"/>.</param>
|
||||||
|
/// <param name="objectToApplyTo">The entity on which <see cref="JsonPatchDocument{T}"/> is applied.</param>
|
||||||
|
/// <param name="modelState">The <see cref="ModelStateDictionary"/> to add errors.</param>
|
||||||
|
/// <param name="prefix">The prefix to use when looking up values in <see cref="ModelStateDictionary"/>.</param>
|
||||||
|
public static void ApplyTo<T>(
|
||||||
|
[NotNull] this JsonPatchDocument<T> patchDoc,
|
||||||
|
[NotNull] T objectToApplyTo,
|
||||||
|
[NotNull] ModelStateDictionary modelState,
|
||||||
|
string prefix) where T : class
|
||||||
|
{
|
||||||
|
patchDoc.ApplyTo(objectToApplyTo, jsonPatchError =>
|
||||||
|
{
|
||||||
|
var affectedObjectName = jsonPatchError.AffectedObject.GetType().Name;
|
||||||
|
var key = string.IsNullOrEmpty(prefix) ? affectedObjectName : prefix + "." + affectedObjectName;
|
||||||
|
|
||||||
|
modelState.TryAddModelError(key, jsonPatchError.ErrorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -282,8 +282,10 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<JsonPatchException<SimpleDTOWithNestedDTO>>(() => { patchDoc.ApplyTo(doc); });
|
var exception = Assert.Throws<JsonPatchException<SimpleDTOWithNestedDTO>>(() => { patchDoc.ApplyTo(doc); });
|
||||||
Assert.Equal("For operation 'add' on array property at path '/simpledto/integerlist/4', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'add' on array property at path '/simpledto/integerlist/4', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,8 +313,39 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
{
|
{
|
||||||
deserialized.ApplyTo(doc);
|
deserialized.ApplyTo(doc);
|
||||||
});
|
});
|
||||||
Assert.Equal("For operation 'add' on array property at path '/simpledto/integerlist/4', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'add' on array property at path '/simpledto/integerlist/4', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToListInvalidPositionTooLarge_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Add<int>(o => o.SimpleDTO.IntegerList, 4, 4);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTOWithNestedDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.Equal(
|
||||||
|
"For operation 'add' on array property at path '/simpledto/integerlist/4', the index is larger than " +
|
||||||
|
"the array size.",
|
||||||
|
logger.ErrorMessage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -363,6 +396,31 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", exception.Message);
|
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToListInvalidPositionTooSmall_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Add<int>(o => o.SimpleDTO.IntegerList, 4, -1);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTOWithNestedDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", logger.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddToListAppend()
|
public void AddToListAppend()
|
||||||
{
|
{
|
||||||
|
|
@ -528,8 +586,10 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<JsonPatchException<SimpleDTOWithNestedDTO>>(() => { patchDoc.ApplyTo(doc); });
|
var exception = Assert.Throws<JsonPatchException<SimpleDTOWithNestedDTO>>(() => { patchDoc.ApplyTo(doc); });
|
||||||
Assert.Equal("For operation 'remove' on array property at path '/simpledto/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'remove' on array property at path '/simpledto/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -556,8 +616,38 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
{
|
{
|
||||||
deserialized.ApplyTo(doc);
|
deserialized.ApplyTo(doc);
|
||||||
});
|
});
|
||||||
Assert.Equal("For operation 'remove' on array property at path '/simpledto/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'remove' on array property at path '/simpledto/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveFromListInvalidPositionTooLarge_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Remove<int>(o => o.SimpleDTO.IntegerList, 3);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTOWithNestedDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(
|
||||||
|
"For operation 'remove' on array property at path '/simpledto/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
logger.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -608,6 +698,31 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", exception.Message);
|
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveFromListInvalidPositionTooSmall_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Remove<int>(o => o.SimpleDTO.IntegerList, -1);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTOWithNestedDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", logger.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RemoveFromEndOfList()
|
public void RemoveFromEndOfList()
|
||||||
{
|
{
|
||||||
|
|
@ -1014,8 +1129,10 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<JsonPatchException<SimpleDTOWithNestedDTO>>(() => { patchDoc.ApplyTo(doc); });
|
var exception = Assert.Throws<JsonPatchException<SimpleDTOWithNestedDTO>>(() => { patchDoc.ApplyTo(doc); });
|
||||||
Assert.Equal("For operation 'replace' on array property at path '/simpledto/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'replace' on array property at path '/simpledto/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1042,8 +1159,38 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
{
|
{
|
||||||
deserialized.ApplyTo(doc);
|
deserialized.ApplyTo(doc);
|
||||||
});
|
});
|
||||||
Assert.Equal("For operation 'replace' on array property at path '/simpledto/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'replace' on array property at path '/simpledto/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReplaceInListInvalid_PositionTooLarge_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Replace<int>(o => o.SimpleDTO.IntegerList, 5, 3);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTOWithNestedDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(
|
||||||
|
"For operation 'replace' on array property at path '/simpledto/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
logger.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1091,6 +1238,31 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", exception.Message);
|
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReplaceInListInvalidPositionTooSmall_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTOWithNestedDTO()
|
||||||
|
{
|
||||||
|
SimpleDTO = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTOWithNestedDTO>();
|
||||||
|
patchDoc.Replace<int>(o => o.SimpleDTO.IntegerList, 5, -1);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTOWithNestedDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Property does not exist at path '/simpledto/integerlist/-1'.", logger.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Copy()
|
public void Copy()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,10 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { patchDoc.ApplyTo(doc); });
|
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { patchDoc.ApplyTo(doc); });
|
||||||
Assert.Equal("For operation 'add' on array property at path '/integerlist/4', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'add' on array property at path '/integerlist/4', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -135,8 +137,35 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { deserialized.ApplyTo(doc); });
|
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { deserialized.ApplyTo(doc); });
|
||||||
Assert.Equal("For operation 'add' on array property at path '/integerlist/4', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'add' on array property at path '/integerlist/4', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToListInvalidPositionTooLarge_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTO>();
|
||||||
|
patchDoc.Add<int>(o => o.IntegerList, 4, 4);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(
|
||||||
|
"For operation 'add' on array property at path '/integerlist/4', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
logger.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -264,6 +293,28 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
Assert.Equal("Property does not exist at path '/integerlist/-1'.", exception.Message);
|
Assert.Equal("Property does not exist at path '/integerlist/-1'.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddToListInvalidPositionTooSmall_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTO>();
|
||||||
|
patchDoc.Add<int>(o => o.IntegerList, 4, -1);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Property does not exist at path '/integerlist/-1'.", logger.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void AddToListAppend()
|
public void AddToListAppend()
|
||||||
{
|
{
|
||||||
|
|
@ -408,8 +459,10 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { patchDoc.ApplyTo(doc); });
|
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { patchDoc.ApplyTo(doc); });
|
||||||
Assert.Equal("For operation 'remove' on array property at path '/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'remove' on array property at path '/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -430,8 +483,35 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { deserialized.ApplyTo(doc); });
|
var exception = Assert.Throws<JsonPatchException<SimpleDTO>>(() => { deserialized.ApplyTo(doc); });
|
||||||
Assert.Equal("For operation 'remove' on array property at path '/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'remove' on array property at path '/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveFromListInvalidPositionTooLarge_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTO>();
|
||||||
|
patchDoc.Remove<int>(o => o.IntegerList, 3);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(
|
||||||
|
"For operation 'remove' on array property at path '/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
logger.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -473,6 +553,28 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
Assert.Equal("Property does not exist at path '/integerlist/-1'.", exception.Message);
|
Assert.Equal("Property does not exist at path '/integerlist/-1'.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveFromListInvalidPositionTooSmall_LogsError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var doc = new SimpleDTO()
|
||||||
|
{
|
||||||
|
IntegerList = new List<int>() { 1, 2, 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// create patch
|
||||||
|
var patchDoc = new JsonPatchDocument<SimpleDTO>();
|
||||||
|
patchDoc.Remove<int>(o => o.IntegerList, -1);
|
||||||
|
|
||||||
|
var logger = new TestErrorLogger<SimpleDTO>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(doc, logger.LogErrorMessage);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Property does not exist at path '/integerlist/-1'.", logger.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RemoveFromEndOfList()
|
public void RemoveFromEndOfList()
|
||||||
{
|
{
|
||||||
|
|
@ -971,8 +1073,10 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
{
|
{
|
||||||
patchDoc.ApplyTo(doc);
|
patchDoc.ApplyTo(doc);
|
||||||
});
|
});
|
||||||
Assert.Equal("For operation 'replace' on array property at path '/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'replace' on array property at path '/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -995,8 +1099,10 @@ namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
{
|
{
|
||||||
deserialized.ApplyTo(doc);
|
deserialized.ApplyTo(doc);
|
||||||
});
|
});
|
||||||
Assert.Equal("For operation 'replace' on array property at path '/integerlist/3', the index is " +
|
Assert.Equal(
|
||||||
"larger than the array size.", exception.Message);
|
"For operation 'replace' on array property at path '/integerlist/3', the index is " +
|
||||||
|
"larger than the array size.",
|
||||||
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.JsonPatch.Test
|
||||||
|
{
|
||||||
|
public class TestErrorLogger<T> where T: class
|
||||||
|
{
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public void LogErrorMessage(JsonPatchError<T> patchError)
|
||||||
|
{
|
||||||
|
ErrorMessage = patchError.ErrorMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using JsonPatchWebSite;
|
||||||
|
using JsonPatchWebSite.Models;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class JsonPatchTest
|
||||||
|
{
|
||||||
|
private const string SiteName = nameof(JsonPatchWebSite);
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
|
||||||
|
private readonly Action<IServiceCollection> _configureServices = new Startup().ConfigureServices;
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http://localhost/jsonpatch/JsonPatchWithModelState")]
|
||||||
|
[InlineData("http://localhost/jsonpatch/JsonPatchWithModelStateAndPrefix?prefix=Patch")]
|
||||||
|
public async Task JsonPatch_ValidAddOperation_List(string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var input = "[{ \"op\": \"add\", \"path\": \"Customer/Orders/2\", " +
|
||||||
|
"\"value\": { \"OrderName\": \"Name2\" }}]";
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = new StringContent(input, Encoding.UTF8, "application/json-patch+json"),
|
||||||
|
Method = new HttpMethod("PATCH"),
|
||||||
|
RequestUri = new Uri(url)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var customer = JsonConvert.DeserializeObject<Customer>(body);
|
||||||
|
Assert.Equal("Name2", customer.Orders[2].OrderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http://localhost/jsonpatch/JsonPatchWithModelState")]
|
||||||
|
[InlineData("http://localhost/jsonpatch/JsonPatchWithModelStateAndPrefix?prefix=Patch")]
|
||||||
|
public async Task JsonPatch_MultipleValidOperations_Success(string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var input = "[{ \"op\": \"add\", \"path\": \"Customer/Orders/2\", " +
|
||||||
|
"\"value\": { \"OrderName\": \"Name2\" }}, {\"op\": \"copy\", \"from\": \"Customer/Orders/2\", " +
|
||||||
|
"\"path\": \"Customer/Orders/3\" }, {\"op\": \"replace\", \"path\": \"Customer/Orders/2/OrderName\", " +
|
||||||
|
"\"value\": \"ReplacedName\" }]";
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = new StringContent(input, Encoding.UTF8, "application/json-patch+json"),
|
||||||
|
Method = new HttpMethod("PATCH"),
|
||||||
|
RequestUri = new Uri(url)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var customer = JsonConvert.DeserializeObject<Customer>(body);
|
||||||
|
Assert.Equal("ReplacedName", customer.Orders[2].OrderName);
|
||||||
|
Assert.Equal("Name2", customer.Orders[3].OrderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> InvalidJsonPatchData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new object[] {
|
||||||
|
"http://localhost/jsonpatch/JsonPatchWithModelStateAndPrefix?prefix=Patch",
|
||||||
|
"[{ \"op\": \"add\", \"path\": \"Customer/Orders/5\", " +
|
||||||
|
"\"value\": { \"OrderName\": \"Name5\" }}]",
|
||||||
|
"{\"Patch.Customer\":[\"For operation 'add' on array property at path " +
|
||||||
|
"'Customer/Orders/5', the index is larger than the array size.\"]}"
|
||||||
|
},
|
||||||
|
new object[] {
|
||||||
|
"http://localhost/jsonpatch/JsonPatchWithModelState",
|
||||||
|
"[{ \"op\": \"add\", \"path\": \"Customer/Orders/5\", " +
|
||||||
|
"\"value\": { \"OrderName\": \"Name5\" }}]",
|
||||||
|
"{\"Customer\":[\"For operation 'add' on array property at path " +
|
||||||
|
"'Customer/Orders/5', the index is larger than the array size.\"]}"
|
||||||
|
},
|
||||||
|
new object[] {
|
||||||
|
"http://localhost/jsonpatch/JsonPatchWithModelStateAndPrefix?prefix=Patch",
|
||||||
|
"[{ \"op\": \"add\", \"path\": \"Customer/Orders/2\", \"value\": " +
|
||||||
|
"{ \"OrderName\": \"Name2\" }}, {\"op\": \"copy\", \"from\": \"Customer/Orders/4\", " +
|
||||||
|
"\"path\": \"Customer/Orders/3\" }, {\"op\": \"replace\", \"path\": " +
|
||||||
|
"\"Customer/Orders/2/OrderName\", \"value\": \"ReplacedName\" }]",
|
||||||
|
"{\"Patch.Customer\":[\"For operation 'copy' on array property at path " +
|
||||||
|
"'Customer/Orders/4', the index is larger than the array size.\"]}"
|
||||||
|
},
|
||||||
|
new object[] {
|
||||||
|
"http://localhost/jsonpatch/JsonPatchWithModelState",
|
||||||
|
"[{ \"op\": \"add\", \"path\": \"Customer/Orders/2\", \"value\": " +
|
||||||
|
"{ \"OrderName\": \"Name2\" }}, {\"op\": \"copy\", \"from\": \"Customer/Orders/4\", " +
|
||||||
|
"\"path\": \"Customer/Orders/3\" }, {\"op\": \"replace\", \"path\": " +
|
||||||
|
"\"Customer/Orders/2/OrderName\", \"value\": \"ReplacedName\" }]",
|
||||||
|
"{\"Customer\":[\"For operation 'copy' on array property at path " +
|
||||||
|
"'Customer/Orders/4', the index is larger than the array size.\"]}"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, MemberData("InvalidJsonPatchData")]
|
||||||
|
public async Task JsonPatch_InvalidOperations_failure(string url, string input, string errorMessage)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
Content = new StringContent(input, Encoding.UTF8, "application/json-patch+json"),
|
||||||
|
Method = new HttpMethod("PATCH"),
|
||||||
|
RequestUri = new Uri(url)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(errorMessage, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
"FormatFilterWebSite": "1.0.0-*",
|
"FormatFilterWebSite": "1.0.0-*",
|
||||||
"FormatterWebSite": "1.0.0",
|
"FormatterWebSite": "1.0.0",
|
||||||
"InlineConstraintsWebSite": "1.0.0",
|
"InlineConstraintsWebSite": "1.0.0",
|
||||||
|
"JsonPatchWebSite": "1.0.0",
|
||||||
"LoggingWebSite": "1.0.0",
|
"LoggingWebSite": "1.0.0",
|
||||||
"LowercaseUrlsWebSite": "1.0.0-*",
|
"LowercaseUrlsWebSite": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.JsonPatch;
|
||||||
|
using Microsoft.AspNet.JsonPatch.Operations;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
public class JsonPatchExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ApplyTo_JsonPatchDocument_ModelState()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var operation = new Operation<Customer>("add", "Customer/CustomerId", from: null, value: "TestName");
|
||||||
|
var patchDoc = new JsonPatchDocument<Customer>();
|
||||||
|
patchDoc.Operations.Add(operation);
|
||||||
|
|
||||||
|
var modelState = new ModelStateDictionary();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(new Customer(), modelState);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var error = Assert.Single(modelState["Customer"].Errors);
|
||||||
|
Assert.Equal("Property does not exist at path 'Customer/CustomerId'.", error.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ApplyTo_JsonPatchDocument_PrefixModelState()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var operation = new Operation<Customer>("add", "Customer/CustomerId", from: null, value: "TestName");
|
||||||
|
var patchDoc = new JsonPatchDocument<Customer>();
|
||||||
|
patchDoc.Operations.Add(operation);
|
||||||
|
|
||||||
|
var modelState = new ModelStateDictionary();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
patchDoc.ApplyTo(new Customer(), modelState, "jsonpatch");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var error = Assert.Single(modelState["jsonpatch.Customer"].Errors);
|
||||||
|
Assert.Equal("Property does not exist at path 'Customer/CustomerId'.", error.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Customer
|
||||||
|
{
|
||||||
|
public string CustomerName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using JsonPatchWebSite.Models;
|
||||||
|
using Microsoft.AspNet.JsonPatch;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace JsonPatchWebSite.Controllers
|
||||||
|
{
|
||||||
|
[Route("jsonpatch/[action]")]
|
||||||
|
public class JsonPatchController : Controller
|
||||||
|
{
|
||||||
|
[HttpPatch]
|
||||||
|
public IActionResult JsonPatchWithModelState([FromBody] JsonPatchDocument<Customer> patchDoc)
|
||||||
|
{
|
||||||
|
var customer = CreateCustomer();
|
||||||
|
|
||||||
|
patchDoc.ApplyTo(customer, ModelState);
|
||||||
|
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return HttpBadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectResult(customer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPatch]
|
||||||
|
public IActionResult JsonPatchWithModelStateAndPrefix(
|
||||||
|
[FromBody] JsonPatchDocument<Customer> patchDoc,
|
||||||
|
string prefix)
|
||||||
|
{
|
||||||
|
var customer = CreateCustomer();
|
||||||
|
|
||||||
|
patchDoc.ApplyTo(customer, ModelState, prefix);
|
||||||
|
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return HttpBadRequest(ModelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectResult(customer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Customer CreateCustomer()
|
||||||
|
{
|
||||||
|
return new Customer
|
||||||
|
{
|
||||||
|
CustomerName = "John",
|
||||||
|
Orders = new List<Order>()
|
||||||
|
{
|
||||||
|
new Order
|
||||||
|
{
|
||||||
|
OrderName = "Order1"
|
||||||
|
},
|
||||||
|
new Order
|
||||||
|
{
|
||||||
|
OrderName = "Order2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>dab1252d-577c-4912-98be-1a812bf83f86</ProjectGuid>
|
||||||
|
<RootNamespace>JsonPatchWebSite</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>29149</DevelopmentServerPort>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace JsonPatchWebSite.Models
|
||||||
|
{
|
||||||
|
public class Customer
|
||||||
|
{
|
||||||
|
public string CustomerName { get; set; }
|
||||||
|
|
||||||
|
public List<Order> Orders { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace JsonPatchWebSite.Models
|
||||||
|
{
|
||||||
|
public class Order
|
||||||
|
{
|
||||||
|
public string OrderName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace JsonPatchWebSite
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
// Set up application services
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
// Add MVC services to the services container
|
||||||
|
services.AddMvc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
app.UseCultureReplacer();
|
||||||
|
|
||||||
|
// Add MVC to the request pipeline
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
|
||||||
|
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Kestrel": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
|
||||||
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": { },
|
||||||
|
"dnxcore50": { }
|
||||||
|
},
|
||||||
|
"webroot": "wwwroot"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
JsonPatchWebSite
|
||||||
|
===
|
||||||
|
|
||||||
|
This web site illustrates how to use JSON Patch operation on an object.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
HelloWorld
|
||||||
Loading…
Reference in New Issue