Eventos
Junte-se a nós na FabCon Vegas
31 de mar., 23 - 2 de abr., 23
O melhor evento liderado pela comunidade Microsoft Fabric, Power BI, SQL e AI. 31 de março a 2 de abril de 2025.
Registre-se hoje mesmoNão há mais suporte para esse navegador.
Atualize o Microsoft Edge para aproveitar os recursos, o suporte técnico e as atualizações de segurança mais recentes.
Este artigo explica como lidar com solicitações de JSON Patch em uma API Web do ASP.NET Core.
O suporte ao patch JSON na API Web do ASP.NET Core é baseado em Newtonsoft.Json
e precisa do pacote NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson
. Para habilitar o suporte ao patch JSON:
Instale o pacote do NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson
.
Chame AddNewtonsoftJson. Por exemplo:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers()
.AddNewtonsoftJson();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
AddNewtonsoftJson
substitui os formatadores padrão de entrada e saída baseados em System.Text.Json
usados para formatar todo o conteúdo JSON. Esse método de extensão é compatível com os seguintes métodos de registro do serviço MVC:
O JsonPatch exige a configuração do cabeçalho Content-Type
como application/json-patch+json
.
O formatador de entrada baseado em System.Text.Json
não suporta o patch JSON. Para adicionar suporte ao patch JSON usando Newtonsoft.Json
, deixando inalterados os outros formatadores de entrada e saída:
Instale o pacote do NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson
.
Atualizar Program.cs
:
using JsonPatchSample;
using Microsoft.AspNetCore.Mvc.Formatters;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, MyJPIF.GetJsonPatchInputFormatter());
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Options;
namespace JsonPatchSample;
public static class MyJPIF
{
public static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
var builder = new ServiceCollection()
.AddLogging()
.AddMvc()
.AddNewtonsoftJson()
.Services.BuildServiceProvider();
return builder
.GetRequiredService<IOptions<MvcOptions>>()
.Value
.InputFormatters
.OfType<NewtonsoftJsonPatchInputFormatter>()
.First();
}
}
O código anterior cria uma instância de NewtonsoftJsonPatchInputFormatter e a insere como a primeira entrada na coleção MvcOptions.InputFormatters. Essa ordem de registro assegura que:
NewtonsoftJsonPatchInputFormatter
processa solicitações patch JSON.System.Text.Json
existentes processam todas as outras solicitações e respostas JSON.Use o método Newtonsoft.Json.JsonConvert.SerializeObject
para serializar um JsonPatchDocument.
Os métodos PUT e PATCH são usados para atualizar um recurso existente. A diferença entre eles é que PUT substitui o recurso inteiro, enquanto PATCH especifica apenas as alterações.
JSON Patch é um formato para especificar as atualizações a serem aplicadas a um recurso. Um documento de JSON Patch tem uma matriz de operações. Cada operação identifica um tipo específico de alteração. Exemplos dessas alterações incluem a adição de um elemento de matriz ou a substituição de um valor de propriedade.
Por exemplo, os documentos JSON a seguir representam um recurso, um documento de patch JSON para o recurso e o resultado da aplicação de operações patch.
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
No JSON anterior:
op
indica o tipo de operação.path
indica o elemento a ser atualizado.value
fornece o novo valor.Este é o recurso após a aplicação do documento JSON Patch anterior:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
As alterações feitas pela aplicação de um documento patch JSON a um recurso são atômicas. Se alguma operação da lista falhar, nenhuma operação da lista será aplicada.
A propriedade path de um objeto de operação tem barras entre os níveis. Por exemplo, "/address/zipCode"
.
Índices baseados em zero são usados para especificar os elementos da matriz. O primeiro elemento da matriz addresses
estaria em /addresses/0
. Para add
até o final de uma matriz, use um hífen (-
) em vez de um número de índice: /addresses/-
.
A tabela a seguir mostra operações compatíveis conforme definido na especificação de JSON Patch:
Operação | Observações |
---|---|
add |
Adicione uma propriedade ou elemento de matriz. Para a propriedade existente: defina o valor. |
remove |
Remova uma propriedade ou elemento de matriz. |
replace |
É o mesmo que remove , seguido por add no mesmo local. |
move |
É o mesmo que remove da origem, seguido por add ao destino usando um valor da origem. |
copy |
É o mesmo que add ao destino usando um valor da origem. |
test |
Retorna o código de status de êxito se o valor em path é igual ao value fornecido. |
A implementação do ASP.NET Core de JSON Patch é fornecida no pacote do NuGet Microsoft.AspNetCore.JsonPatch.
Em um controlador de API, um método de ação para JSON Patch:
HttpPatch
.[FromBody]
.Veja um exemplo:
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
Esse código do aplicativo de exemplo funciona com o seguinte modelo Customer
:
namespace JsonPatchSample.Models;
public class Customer
{
public string? CustomerName { get; set; }
public List<Order>? Orders { get; set; }
}
namespace JsonPatchSample.Models;
public class Order
{
public string OrderName { get; set; }
public string OrderType { get; set; }
}
O exemplo de método de ação:
Customer
.Em um aplicativo real, o código recuperaria os dados de um repositório, como um banco de dados, e atualizaria o banco de dados após a aplicação do patch.
O exemplo de método de ação anterior chama uma sobrecarga de ApplyTo
que utiliza o estado do modelo como um de seus parâmetros. Com essa opção, você pode receber mensagens de erro nas respostas. O exemplo a seguir mostra o corpo de uma resposta 400 Solicitação Incorreta para uma operação test
:
{
"Customer": [
"The current value 'John' at path 'customerName' != test value 'Nancy'."
]
}
O exemplo do método de ação a seguir mostra como aplicar um patch a um objeto dinâmico:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
path
aponta para um elemento de matriz: insere um novo elemento antes do especificado por path
.path
aponta para uma propriedade: define o valor da propriedade.path
aponta para um local não existente: O exemplo de documento de patch a seguir define o valor de CustomerName
e adiciona um objeto Order
ao final da matriz Orders
.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
path
aponta para um elemento de matriz: remove o elemento.path
aponta para uma propriedade: default<T>
.O seguinte exemplo de documento de patch define CustomerName
como nulo e exclui Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
Esta operação é funcionalmente a mesma que remove
seguida por add
.
O exemplo de documento de patch a seguir define o valor de CustomerName
e substitui Orders[0]
por um novo objeto Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
path
aponta para um elemento de matriz: copia o elemento from
para o local do elemento path
e, em seguida, executa uma operação remove
no elemento from
.path
aponta para uma propriedade: copia o valor da propriedade from
para a propriedade path
, depois executa uma operação remove
na propriedade from
.path
aponta para uma propriedade não existente: from
para o local indicado por path
e, em seguida, executa uma operação remove
na propriedade from
.O seguinte exemplo de documento de patch:
Orders[0].OrderName
para CustomerName
.Orders[0].OrderName
como nulo.Orders[1]
para antes de Orders[0]
.[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
Esta operação é funcionalmente a mesma que uma operação move
, sem a etapa final remove
.
O seguinte exemplo de documento de patch:
Orders[0].OrderName
para CustomerName
.Orders[1]
antes de Orders[0]
.[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
Se o valor no local indicado por path
for diferente do valor fornecido em value
, a solicitação falhará. Nesse caso, toda a solicitação de PATCH falhará, mesmo se todas as outras operações no documento de patch forem bem-sucedidas.
A operação test
normalmente é usada para impedir uma atualização quando há um conflito de simultaneidade.
O seguinte exemplo de documento de patch não terá nenhum efeito se o valor inicial de CustomerName
for "John", porque o teste falha:
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Exibir ou baixar o código de exemplo. (Como baixar.)
Para testar o exemplo, execute o aplicativo e envie solicitações HTTP com as seguintes configurações:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
PATCH
Content-Type: application/json-patch+json
Este artigo explica como lidar com solicitações de JSON Patch em uma API Web do ASP.NET Core.
Para habilitar o suporte ao patch JSON no seu aplicativo, conclua as etapas a seguir:
Instale o pacote do NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson
.
Atualize o método Startup.ConfigureServices
do projeto para chamar AddNewtonsoftJson. Por exemplo:
services
.AddControllersWithViews()
.AddNewtonsoftJson();
AddNewtonsoftJson
é compatível com os métodos de registro do serviço MVC:
AddNewtonsoftJson
substitui os formatadores de entrada e saída baseados em System.Text.Json
usados para formatar todo o conteúdo JSON. Para adicionar suporte ao patch JSON usando Newtonsoft.Json
sem alterar os outros formatadores, atualize o método Startup.ConfigureServices
do projeto da seguinte forma:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
});
}
private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
var builder = new ServiceCollection()
.AddLogging()
.AddMvc()
.AddNewtonsoftJson()
.Services.BuildServiceProvider();
return builder
.GetRequiredService<IOptions<MvcOptions>>()
.Value
.InputFormatters
.OfType<NewtonsoftJsonPatchInputFormatter>()
.First();
}
O código anterior exige o pacote Microsoft.AspNetCore.Mvc.NewtonsoftJson
e as seguintes instruções using
:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Linq;
Use o método Newtonsoft.Json.JsonConvert.SerializeObject
para serializar um JsonPatchDocument.
Os métodos PUT e PATCH são usados para atualizar um recurso existente. A diferença entre eles é que PUT substitui o recurso inteiro, enquanto PATCH especifica apenas as alterações.
JSON Patch é um formato para especificar as atualizações a serem aplicadas a um recurso. Um documento de JSON Patch tem uma matriz de operações. Cada operação identifica um tipo específico de alteração. Exemplos dessas alterações incluem a adição de um elemento de matriz ou a substituição de um valor de propriedade.
Por exemplo, os documentos JSON a seguir representam um recurso, um documento de patch JSON para o recurso e o resultado da aplicação de operações patch.
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
No JSON anterior:
op
indica o tipo de operação.path
indica o elemento a ser atualizado.value
fornece o novo valor.Este é o recurso após a aplicação do documento JSON Patch anterior:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
As alterações feitas pela aplicação de um documento patch JSON a um recurso são atômicas. Se alguma operação da lista falhar, nenhuma operação da lista será aplicada.
A propriedade path de um objeto de operação tem barras entre os níveis. Por exemplo, "/address/zipCode"
.
Índices baseados em zero são usados para especificar os elementos da matriz. O primeiro elemento da matriz addresses
estaria em /addresses/0
. Para add
até o final de uma matriz, use um hífen (-
) em vez de um número de índice: /addresses/-
.
A tabela a seguir mostra operações compatíveis conforme definido na especificação de JSON Patch:
Operação | Observações |
---|---|
add |
Adicione uma propriedade ou elemento de matriz. Para a propriedade existente: defina o valor. |
remove |
Remova uma propriedade ou elemento de matriz. |
replace |
É o mesmo que remove , seguido por add no mesmo local. |
move |
É o mesmo que remove da origem, seguido por add ao destino usando um valor da origem. |
copy |
É o mesmo que add ao destino usando um valor da origem. |
test |
Retorna o código de status de êxito se o valor em path é igual ao value fornecido. |
A implementação do ASP.NET Core de JSON Patch é fornecida no pacote do NuGet Microsoft.AspNetCore.JsonPatch.
Em um controlador de API, um método de ação para JSON Patch:
HttpPatch
.JsonPatchDocument<T>
, normalmente com [FromBody]
.ApplyTo
no documento de patch para aplicar as alterações.Veja um exemplo:
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
Esse código do aplicativo de exemplo funciona com o seguinte modelo Customer
:
using System.Collections.Generic;
namespace JsonPatchSample.Models
{
public class Customer
{
public string CustomerName { get; set; }
public List<Order> Orders { get; set; }
}
}
namespace JsonPatchSample.Models
{
public class Order
{
public string OrderName { get; set; }
public string OrderType { get; set; }
}
}
O exemplo de método de ação:
Customer
.Em um aplicativo real, o código recuperaria os dados de um repositório, como um banco de dados, e atualizaria o banco de dados após a aplicação do patch.
O exemplo de método de ação anterior chama uma sobrecarga de ApplyTo
que utiliza o estado do modelo como um de seus parâmetros. Com essa opção, você pode receber mensagens de erro nas respostas. O exemplo a seguir mostra o corpo de uma resposta 400 Solicitação Incorreta para uma operação test
:
{
"Customer": [
"The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
]
}
O exemplo do método de ação a seguir mostra como aplicar um patch a um objeto dinâmico:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
path
aponta para um elemento de matriz: insere um novo elemento antes do especificado por path
.path
aponta para uma propriedade: define o valor da propriedade.path
aponta para um local não existente: O exemplo de documento de patch a seguir define o valor de CustomerName
e adiciona um objeto Order
ao final da matriz Orders
.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
path
aponta para um elemento de matriz: remove o elemento.path
aponta para uma propriedade: default<T>
.O seguinte exemplo de documento de patch define CustomerName
como nulo e exclui Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
Esta operação é funcionalmente a mesma que remove
seguida por add
.
O exemplo de documento de patch a seguir define o valor de CustomerName
e substitui Orders[0]
por um novo objeto Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
path
aponta para um elemento de matriz: copia o elemento from
para o local do elemento path
e, em seguida, executa uma operação remove
no elemento from
.path
aponta para uma propriedade: copia o valor da propriedade from
para a propriedade path
, depois executa uma operação remove
na propriedade from
.path
aponta para uma propriedade não existente: from
para o local indicado por path
e, em seguida, executa uma operação remove
na propriedade from
.O seguinte exemplo de documento de patch:
Orders[0].OrderName
para CustomerName
.Orders[0].OrderName
como nulo.Orders[1]
para antes de Orders[0]
.[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
Esta operação é funcionalmente a mesma que uma operação move
, sem a etapa final remove
.
O seguinte exemplo de documento de patch:
Orders[0].OrderName
para CustomerName
.Orders[1]
antes de Orders[0]
.[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
Se o valor no local indicado por path
for diferente do valor fornecido em value
, a solicitação falhará. Nesse caso, toda a solicitação de PATCH falhará, mesmo se todas as outras operações no documento de patch forem bem-sucedidas.
A operação test
normalmente é usada para impedir uma atualização quando há um conflito de simultaneidade.
O seguinte exemplo de documento de patch não terá nenhum efeito se o valor inicial de CustomerName
for "John", porque o teste falha:
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Exibir ou baixar o código de exemplo. (Como baixar.)
Para testar o exemplo, execute o aplicativo e envie solicitações HTTP com as seguintes configurações:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
PATCH
Content-Type: application/json-patch+json
Comentários do ASP.NET Core
O ASP.NET Core é um projeto código aberto. Selecione um link para fornecer comentários:
Eventos
Junte-se a nós na FabCon Vegas
31 de mar., 23 - 2 de abr., 23
O melhor evento liderado pela comunidade Microsoft Fabric, Power BI, SQL e AI. 31 de março a 2 de abril de 2025.
Registre-se hoje mesmo