Skip to content

Commit a9dc43e

Browse files
JsonPatch mon prep (#35447)
* JsonPatch mon prep * JsonPatch mon prep * JsonPatch mon prep
1 parent 15d3979 commit a9dc43e

File tree

2 files changed

+503
-257
lines changed

2 files changed

+503
-257
lines changed

aspnetcore/web-api/jsonpatch.md

Lines changed: 17 additions & 257 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ uid: web-api/jsonpatch
1010
---
1111
# JsonPatch in ASP.NET Core web API
1212

13-
:::moniker range=">= aspnetcore-6.0"
13+
:::moniker range=">= aspnetcore-10.0"
1414

1515
This article explains how to handle JSON Patch requests in an ASP.NET Core web API.
1616

@@ -21,7 +21,7 @@ JSON Patch support in ASP.NET Core web API is based on `Newtonsoft.Json` and req
2121
* Install the [`Microsoft.AspNetCore.Mvc.NewtonsoftJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson/) NuGet package.
2222
* Call <xref:Microsoft.Extensions.DependencyInjection.NewtonsoftJsonMvcBuilderExtensions.AddNewtonsoftJson%2A>. For example:
2323

24-
:::code language="csharp" source="jsonpatch/samples/6.x/api/Program.cs" id="snippet1" highlight="4":::
24+
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Program.cs" id="snippet1" highlight="4":::
2525

2626
`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:
2727

@@ -38,8 +38,8 @@ The `System.Text.Json`-based input formatter doesn't support JSON Patch. To add
3838
* Install the [`Microsoft.AspNetCore.Mvc.NewtonsoftJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson/) NuGet package.
3939
* Update `Program.cs`:
4040

41-
:::code language="csharp" source="jsonpatch/samples/6.x/api/Program.cs" id="snippet_both" highlight="6-9":::
42-
:::code language="csharp" source="jsonpatch/samples/6.x/api/MyJPIF.cs":::
41+
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Program.cs" id="snippet_both" highlight="6-9":::
42+
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/MyJPIF.cs":::
4343

4444
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:
4545

@@ -60,11 +60,11 @@ For example, the following JSON documents represent a resource, a JSON Patch doc
6060

6161
### Resource example
6262

63-
:::code language="json" source="jsonpatch/snippets/customer.json":::
63+
:::code language="json" source="~/web-api/jsonpatch/snippets/customer.json":::
6464

6565
### JSON patch example
6666

67-
:::code language="json" source="jsonpatch/snippets/add.json":::
67+
:::code language="json" source="~/web-api/jsonpatch/snippets/add.json":::
6868

6969
In the preceding JSON:
7070

@@ -131,13 +131,13 @@ In an API controller, an action method for JSON Patch:
131131

132132
Here's an example:
133133

134-
:::code language="csharp" source="jsonpatch/samples/3.x/api/Controllers/HomeController.cs" id="snippet_PatchAction" highlight="1,3,9":::
134+
:::code language="csharp" source="~/web-api/jsonpatch/samples/3.x/api/Controllers/HomeController.cs" id="snippet_PatchAction" highlight="1,3,9":::
135135

136136
This code from the sample app works with the following `Customer` model:
137137

138-
:::code language="csharp" source="jsonpatch/samples/6.x/api/Models/Customer.cs":::
138+
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Models/Customer.cs":::
139139

140-
:::code language="csharp" source="jsonpatch/samples/6.x/api/Models/Order.cs":::
140+
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Models/Order.cs":::
141141

142142
The sample action method:
143143

@@ -163,7 +163,7 @@ The preceding action method example calls an overload of `ApplyTo` that takes mo
163163

164164
The following action method example shows how to apply a patch to a dynamic object:
165165

166-
:::code language="csharp" source="jsonpatch/samples/6.x/api/Controllers/HomeController.cs" id="snippet_Dynamic":::
166+
:::code language="csharp" source="~/web-api/jsonpatch/samples/6.x/api/Controllers/HomeController.cs" id="snippet_Dynamic":::
167167

168168
## The add operation
169169

@@ -175,7 +175,7 @@ The following action method example shows how to apply a patch to a dynamic obje
175175

176176
The following sample patch document sets the value of `CustomerName` and adds an `Order` object to the end of the `Orders` array.
177177

178-
:::code language="json" source="jsonpatch/snippets/add.json":::
178+
:::code language="json" source="~/web-api/jsonpatch/snippets/add.json":::
179179

180180
## The remove operation
181181

@@ -188,15 +188,15 @@ The following sample patch document sets the value of `CustomerName` and adds an
188188

189189
The following sample patch document sets `CustomerName` to null and deletes `Orders[0]`:
190190

191-
:::code language="json" source="jsonpatch/snippets/remove.json":::
191+
:::code language="json" source="~/web-api/jsonpatch/snippets/remove.json":::
192192

193193
## The replace operation
194194

195195
This operation is functionally the same as a `remove` followed by an `add`.
196196

197197
The following sample patch document sets the value of `CustomerName` and replaces `Orders[0]`with a new `Order` object:
198198

199-
:::code language="json" source="jsonpatch/snippets/replace.json":::
199+
:::code language="json" source="~/web-api/jsonpatch/snippets/replace.json":::
200200

201201
## The move operation
202202

@@ -212,7 +212,7 @@ The following sample patch document:
212212
* Sets `Orders[0].OrderName` to null.
213213
* Moves `Orders[1]` to before `Orders[0]`.
214214

215-
:::code language="json" source="jsonpatch/snippets/move.json":::
215+
:::code language="json" source="~/web-api/jsonpatch/snippets/move.json":::
216216

217217
## The copy operation
218218

@@ -223,7 +223,7 @@ The following sample patch document:
223223
* Copies the value of `Orders[0].OrderName` to `CustomerName`.
224224
* Inserts a copy of `Orders[1]` before `Orders[0]`.
225225

226-
:::code language="json" source="jsonpatch/snippets/copy.json":::
226+
:::code language="json" source="~/web-api/jsonpatch/snippets/copy.json":::
227227

228228
## The test operation
229229

@@ -233,7 +233,7 @@ The `test` operation is commonly used to prevent an update when there's a concur
233233

234234
The following sample patch document has no effect if the initial value of `CustomerName` is "John", because the test fails:
235235

236-
:::code language="json" source="jsonpatch/snippets/test-fail.json":::
236+
:::code language="json" source="~/web-api/jsonpatch/snippets/test-fail.json":::
237237

238238
## Get the code
239239

@@ -255,244 +255,4 @@ To test the sample, run the app and send HTTP requests with the following settin
255255

256256
:::moniker-end
257257

258-
:::moniker range="< aspnetcore-6.0"
259-
260-
This article explains how to handle JSON Patch requests in an ASP.NET Core web API.
261-
262-
## Package installation
263-
264-
To enable JSON Patch support in your app, complete the following steps:
265-
266-
1. Install the [`Microsoft.AspNetCore.Mvc.NewtonsoftJson`](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.NewtonsoftJson/) NuGet package.
267-
1. Update the project's `Startup.ConfigureServices` method to call <xref:Microsoft.Extensions.DependencyInjection.NewtonsoftJsonMvcBuilderExtensions.AddNewtonsoftJson%2A>. For example:
268-
269-
```csharp
270-
services
271-
.AddControllersWithViews()
272-
.AddNewtonsoftJson();
273-
```
274-
275-
`AddNewtonsoftJson` is compatible with the MVC service registration methods:
276-
277-
* <xref:Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddRazorPages%2A>
278-
* <xref:Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllersWithViews%2A>
279-
* <xref:Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddControllers%2A>
280-
281-
## JSON Patch, AddNewtonsoftJson, and System.Text.Json
282-
283-
`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:
284-
285-
:::code language="csharp" source="jsonpatch/samples/3.x/WebApp1/Startup.cs" id="snippet":::
286-
287-
The preceding code requires the `Microsoft.AspNetCore.Mvc.NewtonsoftJson` package and the following `using` statements:
288-
289-
:::code language="csharp" source="jsonpatch/samples/3.x/WebApp1/Startup.cs" id="snippet1":::
290-
291-
Use the `Newtonsoft.Json.JsonConvert.SerializeObject` method to serialize a JsonPatchDocument.
292-
293-
## PATCH HTTP request method
294-
295-
The PUT and [PATCH](https://tools.ietf.org/html/rfc5789) 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.
296-
297-
## JSON Patch
298-
299-
[JSON Patch](https://tools.ietf.org/html/rfc6902) 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.
300-
301-
For example, the following JSON documents represent a resource, a JSON Patch document for the resource, and the result of applying the Patch operations.
302-
303-
### Resource example
304-
305-
:::code language="json" source="jsonpatch/snippets/customer.json":::
306-
307-
### JSON patch example
308-
309-
:::code language="json" source="jsonpatch/snippets/add.json":::
310-
311-
In the preceding JSON:
312-
313-
* The `op` property indicates the type of operation.
314-
* The `path` property indicates the element to update.
315-
* The `value` property provides the new value.
316-
317-
### Resource after patch
318-
319-
Here's the resource after applying the preceding JSON Patch document:
320-
321-
```json
322-
{
323-
"customerName": "Barry",
324-
"orders": [
325-
{
326-
"orderName": "Order0",
327-
"orderType": null
328-
},
329-
{
330-
"orderName": "Order1",
331-
"orderType": null
332-
},
333-
{
334-
"orderName": "Order2",
335-
"orderType": null
336-
}
337-
]
338-
}
339-
```
340-
341-
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.
342-
343-
## Path syntax
344-
345-
The [path](https://tools.ietf.org/html/rfc6901) property of an operation object has slashes between levels. For example, `"/address/zipCode"`.
346-
347-
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/-`.
348-
349-
### Operations
350-
351-
The following table shows supported operations as defined in the [JSON Patch specification](https://tools.ietf.org/html/rfc6902):
352-
353-
|Operation | Notes |
354-
|-----------|--------------------------------|
355-
| `add` | Add a property or array element. For existing property: set value.|
356-
| `remove` | Remove a property or array element. |
357-
| `replace` | Same as `remove` followed by `add` at same location. |
358-
| `move` | Same as `remove` from source followed by `add` to destination using value from source. |
359-
| `copy` | Same as `add` to destination using value from source. |
360-
| `test` | Return success status code if value at `path` = provided `value`.|
361-
362-
## JSON Patch in ASP.NET Core
363-
364-
The ASP.NET Core implementation of JSON Patch is provided in the [Microsoft.AspNetCore.JsonPatch](https://www.nuget.org/packages/microsoft.aspnetcore.jsonpatch/) NuGet package.
365-
366-
## Action method code
367-
368-
In an API controller, an action method for JSON Patch:
369-
370-
* Is annotated with the `HttpPatch` attribute.
371-
* Accepts a `JsonPatchDocument<T>`, typically with `[FromBody]`.
372-
* Calls `ApplyTo` on the patch document to apply the changes.
373-
374-
Here's an example:
375-
376-
:::code language="csharp" source="jsonpatch/samples/3.x/api/Controllers/HomeController.cs" id="snippet_PatchAction" highlight="1,3,9":::
377-
378-
This code from the sample app works with the following `Customer` model:
379-
380-
:::code language="csharp" source="jsonpatch/samples/3.x/api/Models/Customer.cs":::
381-
382-
:::code language="csharp" source="jsonpatch/samples/3.x/api/Models/Order.cs":::
383-
384-
The sample action method:
385-
386-
* Constructs a `Customer`.
387-
* Applies the patch.
388-
* Returns the result in the body of the response.
389-
390-
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.
391-
392-
### Model state
393-
394-
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:
395-
396-
```json
397-
{
398-
"Customer": [
399-
"The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
400-
]
401-
}
402-
```
403-
404-
### Dynamic objects
405-
406-
The following action method example shows how to apply a patch to a dynamic object:
407-
408-
:::code language="csharp" source="jsonpatch/samples/3.x/api/Controllers/HomeController.cs" id="snippet_Dynamic":::
409-
410-
## The add operation
411-
412-
* If `path` points to an array element: inserts new element before the one specified by `path`.
413-
* If `path` points to a property: sets the property value.
414-
* If `path` points to a nonexistent location:
415-
* If the resource to patch is a dynamic object: adds a property.
416-
* If the resource to patch is a static object: the request fails.
417-
418-
The following sample patch document sets the value of `CustomerName` and adds an `Order` object to the end of the `Orders` array.
419-
420-
:::code language="json" source="jsonpatch/snippets/add.json":::
421-
422-
## The remove operation
423-
424-
* If `path` points to an array element: removes the element.
425-
* If `path` points to a property:
426-
* If resource to patch is a dynamic object: removes the property.
427-
* If resource to patch is a static object:
428-
* If the property is nullable: sets it to null.
429-
* If the property is non-nullable, sets it to `default<T>`.
430-
431-
The following sample patch document sets `CustomerName` to null and deletes `Orders[0]`:
432-
433-
:::code language="json" source="jsonpatch/snippets/remove.json":::
434-
435-
## The replace operation
436-
437-
This operation is functionally the same as a `remove` followed by an `add`.
438-
439-
The following sample patch document sets the value of `CustomerName` and replaces `Orders[0]`with a new `Order` object:
440-
441-
:::code language="json" source="jsonpatch/snippets/replace.json":::
442-
443-
## The move operation
444-
445-
* If `path` points to an array element: copies `from` element to location of `path` element, then runs a `remove` operation on the `from` element.
446-
* If `path` points to a property: copies value of `from` property to `path` property, then runs a `remove` operation on the `from` property.
447-
* If `path` points to a nonexistent property:
448-
* If the resource to patch is a static object: the request fails.
449-
* If the resource to patch is a dynamic object: copies `from` property to location indicated by `path`, then runs a `remove` operation on the `from` property.
450-
451-
The following sample patch document:
452-
453-
* Copies the value of `Orders[0].OrderName` to `CustomerName`.
454-
* Sets `Orders[0].OrderName` to null.
455-
* Moves `Orders[1]` to before `Orders[0]`.
456-
457-
:::code language="json" source="jsonpatch/snippets/move.json":::
458-
459-
## The copy operation
460-
461-
This operation is functionally the same as a `move` operation without the final `remove` step.
462-
463-
The following sample patch document:
464-
465-
* Copies the value of `Orders[0].OrderName` to `CustomerName`.
466-
* Inserts a copy of `Orders[1]` before `Orders[0]`.
467-
468-
:::code language="json" source="jsonpatch/snippets/copy.json":::
469-
470-
## The test operation
471-
472-
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.
473-
474-
The `test` operation is commonly used to prevent an update when there's a concurrency conflict.
475-
476-
The following sample patch document has no effect if the initial value of `CustomerName` is "John", because the test fails:
477-
478-
:::code language="json" source="jsonpatch/snippets/test-fail.json":::
479-
480-
## Get the code
481-
482-
[View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/web-api/jsonpatch/samples). ([How to download](xref:index#how-to-download-a-sample)).
483-
484-
To test the sample, run the app and send HTTP requests with the following settings:
485-
486-
* URL: `http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate`
487-
* HTTP method: `PATCH`
488-
* Header: `Content-Type: application/json-patch+json`
489-
* Body: Copy and paste one of the JSON patch document samples from the *JSON* project folder.
490-
491-
## Additional resources
492-
493-
* [IETF RFC 5789 PATCH method specification](https://tools.ietf.org/html/rfc5789)
494-
* [IETF RFC 6902 JSON Patch specification](https://tools.ietf.org/html/rfc6902)
495-
* [IETF RFC 6901 JSON Patch path format spec](https://tools.ietf.org/html/rfc6901)
496-
* [ASP.NET Core JSON Patch source code](https://github.com/dotnet/AspNetCore/tree/main/src/Features/JsonPatch/src)
497-
498-
:::moniker-end
258+
[!INCLUDE[](~/web-api/jsonpatch/includes/jsonpatch9.md)]

0 commit comments

Comments
 (0)