19 KiB
title | author | description | ms.author | ms.custom | ms.date | no-loc | uid | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
JsonPatch in ASP.NET Core web API | rick-anderson | Learn how to handle JSON Patch requests in an ASP.NET Core web API. | riande | mvc | 04/02/2020 |
|
web-api/jsonpatch |
JsonPatch in ASP.NET Core web API
By Tom Dykstra and Kirk Larkin
::: moniker range=">= aspnetcore-3.0"
This article explains how to handle JSON Patch requests in an ASP.NET Core web API.
Package installation
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*. For example:services .AddControllersWithViews() .AddNewtonsoftJson();
AddNewtonsoftJson
is compatible with the MVC service registration methods:
- xref:Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddRazorPages*
- xref:Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllersWithViews*
- xref:Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllers*
JSON Patch, AddNewtonsoftJson, and System.Text.Json
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:
The preceding code requires the Microsoft.AspNetCore.Mvc.NewtonsoftJson
package and the following using
statements:
Use the Newtonsoft.Json.JsonConvert.SerializeObject
method to serialize a JsonPatchDocument.
PATCH HTTP request method
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
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.
Resource example
JSON patch example
In the preceding JSON:
- The
op
property indicates the type of operation. - The
path
property indicates the element to update. - The
value
property provides the new value.
Resource after patch
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.
Path syntax
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/-
.
Operations
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 . |
JSON Patch in ASP.NET Core
The ASP.NET Core implementation of JSON Patch is provided in the Microsoft.AspNetCore.JsonPatch NuGet package.
Action method code
In an API controller, an action method for JSON Patch:
- Is annotated with the
HttpPatch
attribute. - Accepts a
JsonPatchDocument<T>
, typically with[FromBody]
. - Calls
ApplyTo
on the patch document to apply the changes.
Here's an example:
This code from the sample app works with the following Customer
model:
The sample action method:
- Constructs a
Customer
. - Applies the patch.
- Returns the result in the body of the response.
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.
Model state
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'."
]
}
Dynamic objects
The following action method example shows how to apply a patch to a dynamic object:
The add operation
- If
path
points to an array element: inserts new element before the one specified bypath
. - If
path
points to a property: sets the property value. - If
path
points to a nonexistent location:- If the resource to patch is a dynamic object: adds a property.
- If the resource to patch is a static object: the request fails.
The following sample patch document sets the value of CustomerName
and adds an Order
object to the end of the Orders
array.
The remove operation
- If
path
points to an array element: removes the element. - If
path
points to a property:- If resource to patch is a dynamic object: removes the property.
- If resource to patch is a static object:
- If the property is nullable: sets it to null.
- If the property is non-nullable, sets it to
default<T>
.
The following sample patch document sets CustomerName
to null and deletes Orders[0]
:
The replace operation
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:
The move operation
- If
path
points to an array element: copiesfrom
element to location ofpath
element, then runs aremove
operation on thefrom
element. - If
path
points to a property: copies value offrom
property topath
property, then runs aremove
operation on thefrom
property. - If
path
points to a nonexistent property:- If the resource to patch is a static object: the request fails.
- If the resource to patch is a dynamic object: copies
from
property to location indicated bypath
, then runs aremove
operation on thefrom
property.
The following sample patch document:
- Copies the value of
Orders[0].OrderName
toCustomerName
. - Sets
Orders[0].OrderName
to null. - Moves
Orders[1]
to beforeOrders[0]
.
The copy operation
This operation is functionally the same as a move
operation without the final remove
step.
The following sample patch document:
- Copies the value of
Orders[0].OrderName
toCustomerName
. - Inserts a copy of
Orders[1]
beforeOrders[0]
.
The test operation
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:
Get the code
View or download sample code. (How to download).
To test the sample, run the app and send HTTP requests with the following settings:
- URL:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- HTTP method:
PATCH
- Header:
Content-Type: application/json-patch+json
- Body: Copy and paste one of the JSON patch document samples from the JSON project folder.
Additional resources
- IETF RFC 5789 PATCH method specification
- IETF RFC 6902 JSON Patch specification
- IETF RFC 6901 JSON Patch path format spec
- JSON Patch documentation. Includes links to resources for creating JSON Patch documents.
- ASP.NET Core JSON Patch source code
::: moniker-end
::: moniker range="< aspnetcore-3.0"
This article explains how to handle JSON Patch requests in an ASP.NET Core web API.
PATCH HTTP request method
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
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, such as add an array element or replace 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.
Resource example
JSON patch example
In the preceding JSON:
- The
op
property indicates the type of operation. - The
path
property indicates the element to update. - The
value
property provides the new value.
Resource after patch
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.
Path syntax
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/-
.
Operations
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 . |
JsonPatch in ASP.NET Core
The ASP.NET Core implementation of JSON Patch is provided in the Microsoft.AspNetCore.JsonPatch NuGet package. The package is included in the Microsoft.AspnetCore.App metapackage.
Action method code
In an API controller, an action method for JSON Patch:
- Is annotated with the
HttpPatch
attribute. - Accepts a
JsonPatchDocument<T>
, typically with[FromBody]
. - Calls
ApplyTo
on the patch document to apply the changes.
Here's an example:
This code from the sample app works with the following Customer
model.
The sample action method:
- Constructs a
Customer
. - Applies the patch.
- Returns the result in the body of the response.
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.
Model state
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'."
]
}
Dynamic objects
The following action method example shows how to apply a patch to a dynamic object.
The add operation
- If
path
points to an array element: inserts new element before the one specified bypath
. - If
path
points to a property: sets the property value. - If
path
points to a nonexistent location:- If the resource to patch is a dynamic object: adds a property.
- If the resource to patch is a static object: the request fails.
The following sample patch document sets the value of CustomerName
and adds an Order
object to the end of the Orders
array.
The remove operation
- If
path
points to an array element: removes the element. - If
path
points to a property:- If resource to patch is a dynamic object: removes the property.
- If resource to patch is a static object:
- If the property is nullable: sets it to null.
- If the property is non-nullable, sets it to
default<T>
.
The following sample patch document sets CustomerName
to null and deletes Orders[0]
.
The replace operation
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.
The move operation
- If
path
points to an array element: copiesfrom
element to location ofpath
element, then runs aremove
operation on thefrom
element. - If
path
points to a property: copies value offrom
property topath
property, then runs aremove
operation on thefrom
property. - If
path
points to a nonexistent property:- If the resource to patch is a static object: the request fails.
- If the resource to patch is a dynamic object: copies
from
property to location indicated bypath
, then runs aremove
operation on thefrom
property.
The following sample patch document:
- Copies the value of
Orders[0].OrderName
toCustomerName
. - Sets
Orders[0].OrderName
to null. - Moves
Orders[1]
to beforeOrders[0]
.
The copy operation
This operation is functionally the same as a move
operation without the final remove
step.
The following sample patch document:
- Copies the value of
Orders[0].OrderName
toCustomerName
. - Inserts a copy of
Orders[1]
beforeOrders[0]
.
The test operation
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:
Get the code
View or download sample code. (How to download).
To test the sample, run the app and send HTTP requests with the following settings:
- URL:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- HTTP method:
PATCH
- Header:
Content-Type: application/json-patch+json
- Body: Copy and paste one of the JSON patch document samples from the JSON project folder.
Additional resources
- IETF RFC 5789 PATCH method specification
- IETF RFC 6902 JSON Patch specification
- IETF RFC 6901 JSON Patch path format spec
- JSON Patch documentation. Includes links to resources for creating JSON Patch documents.
- ASP.NET Core JSON Patch source code
::: moniker-end