aspnetcore/web-api/jsonpatch/includes/jsonpatch9.md
:::moniker range=">= aspnetcore-6.0 < aspnetcore-10.0"
This article explains how to handle JSON Patch requests in an ASP.NET Core web API.
[!IMPORTANT] The JSON Patch standard has inherent security risks. This implementation doesn't attempt to mitigate these inherent security risks. It's the responsibility of the developer to ensure that the JSON Patch document is safe to apply to the target object. For more information, see the Mitigating Security Risks section.
JSON Patch support in ASP.NET Core web API is based on Newtonsoft.Json and requires the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.
To enable JSON Patch support:
Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.
Call xref:Microsoft.Extensions.DependencyInjection.NewtonsoftJsonMvcBuilderExtensions.AddNewtonsoftJson%2A. For example:
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Program.cs" id="snippet1" highlight="4":::
AddNewtonsoftJson replaces the default System.Text.Json-based input and output formatters used for formatting all JSON content. This extension method is compatible with the following MVC service registration methods:
JsonPatch requires setting the Content-Type header to application/json-patch+json.
The System.Text.Json-based input formatter doesn't support JSON Patch. To add support for JSON Patch using Newtonsoft.Json, while leaving the other input and output formatters unchanged:
Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.
Update Program.cs:
:::code language="csharp" source="/web-api/jsonpatch/samples/6.x/api/Program.cs" id="snippet_both" highlight="6-9":::
:::code language="csharp" source="/web-api/jsonpatch/samples/6.x/api/MyJPIF.cs":::
The preceding code creates an instance of xref:Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonPatchInputFormatter and inserts it as the first entry in the xref:Microsoft.AspNetCore.Mvc.MvcOptions.InputFormatters%2A?displayProperty=nameWithType collection. This order of registration ensures that:
NewtonsoftJsonPatchInputFormatter processes JSON Patch requests.System.Text.Json-based input and formatters process all other JSON requests and responses.Use the Newtonsoft.Json.JsonConvert.SerializeObject method to serialize a xref:Microsoft.AspNetCore.JsonPatch.JsonPatchDocument.
The PUT and PATCH methods are used to update an existing resource. The difference between them is that PUT replaces the entire resource, while PATCH specifies only the changes.
JSON Patch is a format for specifying updates to be applied to a resource. A JSON Patch document has an array of operations. Each operation identifies a particular type of change. Examples of such changes include adding an array element or replacing a property value.
For example, the following JSON documents represent a resource, a JSON Patch document for the resource, and the result of applying the Patch operations.
:::code language="json" source="~/web-api/jsonpatch/snippets/customer.json":::
:::code language="json" source="~/web-api/jsonpatch/snippets/add.json":::
In the preceding JSON:
op property indicates the type of operation.path property indicates the element to update.value property provides the new value.Here's the resource after applying the preceding JSON Patch document:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
The changes made by applying a JSON Patch document to a resource are atomic. If any operation in the list fails, no operation in the list is applied.
The path property of an operation object has slashes between levels. For example, "/address/zipCode".
Zero-based indexes are used to specify array elements. The first element of the addresses array would be at /addresses/0. To add to the end of an array, use a hyphen (-) rather than an index number: /addresses/-.
The following table shows supported operations as defined in the JSON Patch specification:
| Operation | Notes |
|---|---|
add | Add a property or array element. For existing property: set value. |
remove | Remove a property or array element. |
replace | Same as remove followed by add at same location. |
move | Same as remove from source followed by add to destination using value from source. |
copy | Same as add to destination using value from source. |
test | Return success status code if value at path = provided value. |
The ASP.NET Core implementation of JSON Patch is provided in the Microsoft.AspNetCore.JsonPatch NuGet package.
In an API controller, an action method for JSON Patch:
HttpPatch attribute.[FromBody].Here's an example:
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/api/Controllers/HomeController.cs" id="snippet_PatchAction" highlight="1,3,9":::
This code from the sample app works with the following Customer model:
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Models/Customer.cs":::
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Models/Order.cs":::
The sample action method:
Customer.In a real app, the code would retrieve the data from a store such as a database and update the database after applying the patch.
The preceding action method example calls an overload of ApplyTo that takes model state as one of its parameters. With this option, you can get error messages in responses. The following example shows the body of a 400 Bad Request response for a test operation:
{
"Customer": [
"The current value 'John' at path 'customerName' != test value 'Nancy'."
]
}
The following action method example shows how to apply a patch to a dynamic object:
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Controllers/HomeController.cs" id="snippet_Dynamic":::
path points to an array element: inserts new element before the one specified by path.path points to a property: sets the property value.path points to a nonexistent location:
The following sample patch document sets the value of CustomerName and adds an Order object to the end of the Orders array.
:::code language="json" source="~/web-api/jsonpatch/snippets/add.json":::
path points to an array element: removes the element.path points to a property:
default<T>.The following sample patch document sets CustomerName to null and deletes Orders[0]:
:::code language="json" source="~/web-api/jsonpatch/snippets/remove.json":::
This operation is functionally the same as a remove followed by an add.
The following sample patch document sets the value of CustomerName and replaces Orders[0]with a new Order object:
:::code language="json" source="~/web-api/jsonpatch/snippets/replace.json":::
path points to an array element: copies from element to location of path element, then runs a remove operation on the from element.path points to a property: copies value of from property to path property, then runs a remove operation on the from property.path points to a nonexistent property:
from property to location indicated by path, then runs a remove operation on the from property.The following sample patch document:
Orders[0].OrderName to CustomerName.Orders[0].OrderName to null.Orders[1] to before Orders[0].:::code language="json" source="~/web-api/jsonpatch/snippets/move.json":::
This operation is functionally the same as a move operation without the final remove step.
The following sample patch document:
Orders[0].OrderName to CustomerName.Orders[1] before Orders[0].:::code language="json" source="~/web-api/jsonpatch/snippets/copy.json":::
If the value at the location indicated by path is different from the value provided in value, the request fails. In that case, the whole PATCH request fails even if all other operations in the patch document would otherwise succeed.
The test operation is commonly used to prevent an update when there's a concurrency conflict.
The following sample patch document has no effect if the initial value of CustomerName is "John", because the test fails:
:::code language="json" source="~/web-api/jsonpatch/snippets/test-fail.json":::
View or download sample code. (How to download).
To test the sample, run the app and send HTTP requests with the following settings:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstatePATCHContent-Type: application/json-patch+jsonWhen using the Microsoft.AspNetCore.JsonPatch package with the Newtonsoft.Json-based implementation, it's critical to understand and mitigate potential security risks. The following sections outline the identified security risks associated with JSON Patch and provide recommended mitigations to ensure secure usage of the package.
[!IMPORTANT] This is not an exhaustive list of threats. App developers must conduct their own threat model reviews to determine an app-specific comprehensive list and come up with appropriate mitigations as needed. For example, apps which expose collections to patch operations should consider the potential for algorithmic complexity attacks if those operations insert or remove elements at the beginning of the collection.
By running comprehensive threat models for their own apps and addressing identified threats while following the recommended mitigations below, consumers of these packages can integrate JSON Patch functionality into their apps while minimizing security risks.
copy operation that duplicates large object graphs multiple times, leading to excessive memory consumption.ApplyTo.public void Validate(JsonPatchDocument patch)
{
// This is just an example. It's up to the developer to make sure that
// this case is handled properly, based on the app needs.
if (patch.Operations.Where(op => op.OperationType == OperationType.Copy).Count()
> MaxCopyOperationsCount)
{
throw new InvalidOperationException();
}
}
:::moniker-end
:::moniker range="< aspnetcore-6.0"
This article explains how to handle JSON Patch requests in an ASP.NET Core web API.
[!IMPORTANT] The JSON Patch standard has inherent security risks. Since these risks are inherent to the JSON Patch standard, this implementation doesn't attempt to mitigate inherent security risks. It's the responsibility of the developer to ensure that the JSON Patch document is safe to apply to the target object. For more information, see the Mitigating Security Risks section.
To enable JSON Patch support in your app, complete the following steps:
Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.
Update the project's Startup.ConfigureServices method to call xref:Microsoft.Extensions.DependencyInjection.NewtonsoftJsonMvcBuilderExtensions.AddNewtonsoftJson%2A. For example:
services
.AddControllersWithViews()
.AddNewtonsoftJson();
AddNewtonsoftJson is compatible with the MVC service registration methods:
AddNewtonsoftJson replaces the System.Text.Json-based input and output formatters used for formatting all JSON content. To add support for JSON Patch using Newtonsoft.Json, while leaving the other formatters unchanged, update the project's Startup.ConfigureServices method as follows:
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/WebApp1/Startup.cs" id="snippet":::
The preceding code requires the Microsoft.AspNetCore.Mvc.NewtonsoftJson package and the following using statements:
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/WebApp1/Startup.cs" id="snippet1":::
Use the Newtonsoft.Json.JsonConvert.SerializeObject method to serialize a JsonPatchDocument.
The PUT and PATCH methods are used to update an existing resource. The difference between them is that PUT replaces the entire resource, while PATCH specifies only the changes.
JSON Patch is a format for specifying updates to be applied to a resource. A JSON Patch document has an array of operations. Each operation identifies a particular type of change. Examples of such changes include adding an array element or replacing a property value.
For example, the following JSON documents represent a resource, a JSON Patch document for the resource, and the result of applying the Patch operations.
:::code language="json" source="~/web-api/jsonpatch/snippets/customer.json":::
:::code language="json" source="~/web-api/jsonpatch/snippets/add.json":::
In the preceding JSON:
op property indicates the type of operation.path property indicates the element to update.value property provides the new value.Here's the resource after applying the preceding JSON Patch document:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
The changes made by applying a JSON Patch document to a resource are atomic. If any operation in the list fails, no operation in the list is applied.
The path property of an operation object has slashes between levels. For example, "/address/zipCode".
Zero-based indexes are used to specify array elements. The first element of the addresses array would be at /addresses/0. To add to the end of an array, use a hyphen (-) rather than an index number: /addresses/-.
The following table shows supported operations as defined in the JSON Patch specification:
| Operation | Notes |
|---|---|
add | Add a property or array element. For existing property: set value. |
remove | Remove a property or array element. |
replace | Same as remove followed by add at same location. |
move | Same as remove from source followed by add to destination using value from source. |
copy | Same as add to destination using value from source. |
test | Return success status code if value at path = provided value. |
The ASP.NET Core implementation of JSON Patch is provided in the Microsoft.AspNetCore.JsonPatch NuGet package.
In an API controller, an action method for JSON Patch:
HttpPatch attribute.JsonPatchDocument<T>, typically with [FromBody].ApplyTo on the patch document to apply the changes.Here's an example:
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/api/Controllers/HomeController.cs" id="snippet_PatchAction" highlight="1,3,9":::
This code from the sample app works with the following Customer model:
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/api/Models/Customer.cs":::
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/api/Models/Order.cs":::
The sample action method:
Customer.In a real app, the code would retrieve the data from a store such as a database and update the database after applying the patch.
The preceding action method example calls an overload of ApplyTo that takes model state as one of its parameters. With this option, you can get error messages in responses. The following example shows the body of a 400 Bad Request response for a test operation:
{
"Customer": [
"The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
]
}
The following action method example shows how to apply a patch to a dynamic object:
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/api/Controllers/HomeController.cs" id="snippet_Dynamic":::
path points to an array element: inserts new element before the one specified by path.path points to a property: sets the property value.path points to a nonexistent location:
The following sample patch document sets the value of CustomerName and adds an Order object to the end of the Orders array.
:::code language="json" source="~/web-api/jsonpatch/snippets/add.json":::
path points to an array element: removes the element.path points to a property:
default<T>.The following sample patch document sets CustomerName to null and deletes Orders[0]:
:::code language="json" source="~/web-api/jsonpatch/snippets/remove.json":::
This operation is functionally the same as a remove followed by an add.
The following sample patch document sets the value of CustomerName and replaces Orders[0]with a new Order object:
:::code language="json" source="~/web-api/jsonpatch/snippets/replace.json":::
path points to an array element: copies from element to location of path element, then runs a remove operation on the from element.path points to a property: copies value of from property to path property, then runs a remove operation on the from property.path points to a nonexistent property:
from property to location indicated by path, then runs a remove operation on the from property.The following sample patch document:
Orders[0].OrderName to CustomerName.Orders[0].OrderName to null.Orders[1] to before Orders[0].:::code language="json" source="~/web-api/jsonpatch/snippets/move.json":::
This operation is functionally the same as a move operation without the final remove step.
The following sample patch document:
Orders[0].OrderName to CustomerName.Orders[1] before Orders[0].:::code language="json" source="~/web-api/jsonpatch/snippets/copy.json":::
If the value at the location indicated by path is different from the value provided in value, the request fails. In that case, the whole PATCH request fails even if all other operations in the patch document would otherwise succeed.
The test operation is commonly used to prevent an update when there's a concurrency conflict.
The following sample patch document has no effect if the initial value of CustomerName is "John", because the test fails:
:::code language="json" source="~/web-api/jsonpatch/snippets/test-fail.json":::
View or download sample code. (How to download).
To test the sample, run the app and send HTTP requests with the following settings:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstatePATCHContent-Type: application/json-patch+json:::moniker-end