Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article explique comment gérer les demandes de correctifs JSON dans une API web ASP.NET Core.
La prise en charge de JSON Patch dans l'API Web ASP.NET Core est basée sur la sérialisation System.Text.Json et nécessite le package Microsoft.AspNetCore.JsonPatch.SystemTextJson
NuGet.
Quelle est la spécification JSON Patch ?
La norme JSON Patch :
Format standard pour décrire les modifications à appliquer à un document JSON.
Est défini dans RFC 6902 et est largement utilisé dans les API RESTful pour effectuer des mises à jour partielles des ressources JSON.
Décrit une séquence d’opérations qui modifient un document JSON tel que :
add
remove
replace
move
copy
test
Dans les applications web, JSON Patch est couramment utilisé dans une opération PATCH pour effectuer des mises à jour partielles d’une ressource. Au lieu d’envoyer la ressource entière pour une mise à jour, les clients peuvent envoyer un document patch JSON contenant uniquement les modifications. La mise à jour corrective réduit la taille de la charge utile et améliore l’efficacité.
Pour obtenir une vue d’ensemble de la norme de correctif JSON, consultez jsonpatch.com.
Prise en charge des correctifs JSON dans l’API web ASP.NET Core
La prise en charge des correctifs JSON dans l'API web ASP.NET Core repose sur la sérialisation System.Text.Json, à partir de .NET 10, en mettant en œuvre Microsoft.AspNetCore.JsonPatch sur la base de la sérialisation System.Text.Json. Cette fonctionnalité :
- Nécessite le
Microsoft.AspNetCore.JsonPatch.SystemTextJson
paquet NuGet. - S’aligne sur les pratiques .NET modernes en tirant parti de la System.Text.Json bibliothèque, qui est optimisée pour .NET.
- Offre des performances améliorées et une utilisation réduite de la mémoire par rapport à l’implémentation héritée
Newtonsoft.Json
. Pour plus d’informations sur l’implémentation basée sur l'héritageNewtonsoft.Json
, consultez la version .NET 9 de cet article.
Remarque
L'implémentation de Microsoft.AspNetCore.JsonPatch, basée sur la sérialisation System.Text.Json, n'est pas un remplacement direct de l'implémentation héritée basée sur Newtonsoft.Json
. Il ne prend pas en charge les types dynamiques, par exemple ExpandoObject.
Important
La norme json Patch présente des risques de sécurité inhérents. Étant donné que ces risques sont inhérents à la norme de correctif JSON, l’implémentation ASP.NET Core ne tente pas d’atténuer les risques de sécurité inhérents. Il incombe au développeur de s’assurer que le document patch JSON est sûr de s’appliquer à l’objet cible. Pour plus d’informations, consultez la section Atténuation des risques de sécurité .
Activer la prise en charge de JSON Patch avec System.Text.Json
Pour activer la prise en charge des correctifs JSON avec System.Text.Json, installez le Microsoft.AspNetCore.JsonPatch.SystemTextJson
package NuGet.
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
Ce package fournit une classe JsonPatchDocument<TModel> pour représenter un document JSON Patch pour les objets de type T
et une logique personnalisée pour la sérialisation et la désérialisation des documents JSON Patch à l’aide de System.Text.Json. La méthode clé de la JsonPatchDocument<TModel> classe est ApplyTo(Object), qui applique les opérations correctives à un objet cible de type T
.
Code de méthode d'action appliquant JSON Patch
Dans un contrôleur d’API, une méthode d’action pour JSON Patch :
- est annotée avec l’attribut HttpPatchAttribute ;
- Accepte un JsonPatchDocument<TModel>, généralement avec FromBodyAttribute.
- appelle ApplyTo(Object) sur le document de correctif pour appliquer les modifications.
Exemple de méthode d’action du contrôleur :
[HttpPatch("{id}", Name = "UpdateCustomer")]
public IActionResult Update(AppDb db, string id, [FromBody] JsonPatchDocument<Customer> patchDoc)
{
// Retrieve the customer by ID
var customer = db.Customers.FirstOrDefault(c => c.Id == id);
// Return 404 Not Found if customer doesn't exist
if (customer == null)
{
return NotFound();
}
patchDoc.ApplyTo(customer, jsonPatchError =>
{
var key = jsonPatchError.AffectedObject.GetType().Name;
ModelState.AddModelError(key, jsonPatchError.ErrorMessage);
}
);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
Ce code de l’exemple d’application fonctionne avec les modèles suivants Customer
Order
:
namespace App.Models;
public class Customer
{
public string Id { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
public string? Address { get; set; }
public List<Order>? Orders { get; set; }
public Customer()
{
Id = Guid.NewGuid().ToString();
}
}
namespace App.Models;
public class Order
{
public string Id { get; set; }
public DateTime? OrderDate { get; set; }
public DateTime? ShipDate { get; set; }
public decimal TotalAmount { get; set; }
public Order()
{
Id = Guid.NewGuid().ToString();
}
}
Étapes clés de l’exemple de méthode d’action :
- Récupérez le client :
- La méthode récupère un
Customer
objet de la base de donnéesAppDb
à l’aide de l’ID fourni. - Si aucun objet n’est
Customer
trouvé, il retourne une404 Not Found
réponse.
- La méthode récupère un
- Appliquer le correctif JSON :
- La ApplyTo(Object) méthode applique les opérations de correctif JSON du patchDoc à l’objet récupéré
Customer
. - Si des erreurs se produisent pendant l’application corrective, telles que des opérations ou des conflits non valides, elles sont capturées par un délégué de gestion des erreurs. Ce délégué ajoute des messages d'erreur au
ModelState
en utilisant le nom de type de l'objet affecté et le message d'erreur.
- La ApplyTo(Object) méthode applique les opérations de correctif JSON du patchDoc à l’objet récupéré
- Valider ModelState :
- Après avoir appliqué le correctif, la méthode vérifie le
ModelState
pour des erreurs. - Si
ModelState
n’est pas valide, par exemple en raison d’erreurs de correctif, elle retourne une400 Bad Request
réponse avec les erreurs de validation.
- Après avoir appliqué le correctif, la méthode vérifie le
- Retournez le client mis à jour :
- Si le correctif est appliqué avec succès et si
ModelState
est valide, la méthode retourne l'objet mis à jourCustomer
dans la réponse.
- Si le correctif est appliqué avec succès et si
Exemple de réponse d’erreur :
L’exemple suivant montre le corps d’une 400 Bad Request
réponse pour une opération de correctif JSON lorsque le chemin d’accès spécifié n’est pas valide :
{
"Customer": [
"The target location specified by path segment 'foobar' was not found."
]
}
Appliquer un document patch JSON à un objet
Les exemples suivants montrent comment utiliser la ApplyTo(Object) méthode pour appliquer un document patch JSON à un objet.
Exemple : Appliquer un JsonPatchDocument<TModel> à un objet
L’exemple suivant illustre cette situation :
- Les opérations
add
,replace
, etremove
. - Opérations sur les propriétés imbriquées.
- Ajout d’un nouvel élément à un tableau.
- Utilisation d’un convertisseur d’énumération de chaîne JSON dans un document de correctif JSON.
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com",
PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
Address = new Address
{
Street = "123 Main St",
City = "Anytown",
State = "TX"
}
};
// Raw JSON patch document
string jsonPatch = """
[
{ "op": "replace", "path": "/FirstName", "value": "Jane" },
{ "op": "remove", "path": "/Email"},
{ "op": "add", "path": "/Address/ZipCode", "value": "90210" },
{ "op": "add", "path": "/PhoneNumbers/-", "value": { "Number": "987-654-3210",
"Type": "Work" } }
]
""";
// Deserialize the JSON patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON patch document
patchDoc!.ApplyTo(person);
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
L’exemple précédent génère la sortie suivante de l’objet mis à jour :
{
"firstName": "Jane",
"lastName": "Doe",
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "TX",
"zipCode": "90210"
},
"phoneNumbers": [
{
"number": "123-456-7890",
"type": "Mobile"
},
{
"number": "987-654-3210",
"type": "Work"
}
]
}
La ApplyTo(Object) méthode suit généralement les conventions et les options de System.Text.Json traitement du JsonPatchDocument<TModel>, y compris le comportement contrôlé par les options suivantes :
- JsonNumberHandling: indique si les propriétés numériques sont lues à partir de chaînes.
- PropertyNameCaseInsensitive : si les noms de propriété respectent la casse.
Principales différences entre System.Text.Json et la nouvelle JsonPatchDocument<TModel> implémentation :
- Le type d’exécution de l’objet cible, et non le type déclaré, détermine les correctifs de propriétés ApplyTo(Object) .
- System.Text.Json la désérialisation s’appuie sur le type déclaré pour identifier les propriétés éligibles.
Exemple : Appliquer un JsonPatchDocument avec gestion des erreurs
Il existe différentes erreurs qui peuvent se produire lors de l’application d’un document patch JSON. Par exemple, l’objet cible peut ne pas avoir la propriété spécifiée, ou la valeur spécifiée peut être incompatible avec le type de propriété.
JSON Patch
prend en charge l’opération test
, qui vérifie si une valeur spécifiée est égale à la propriété cible. Si ce n’est pas le cas, elle retourne une erreur.
L’exemple suivant montre comment gérer ces erreurs correctement.
Important
L’objet passé à la méthode ApplyTo(Object) est modifié en place. L’appelant est responsable de l’abandon des modifications en cas d’échec d’une opération.
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com"
};
// Raw JSON patch document
string jsonPatch = """
[
{ "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
{ "op": "test", "path": "/FirstName", "value": "Jane" },
{ "op": "replace", "path": "/LastName", "value": "Smith" }
]
""";
// Deserialize the JSON patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON patch document, catching any errors
Dictionary<string, string[]>? errors = null;
patchDoc!.ApplyTo(person, jsonPatchError =>
{
errors ??= new ();
var key = jsonPatchError.AffectedObject.GetType().Name;
if (!errors.ContainsKey(key))
{
errors.Add(key, new string[] { });
}
errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
});
if (errors != null)
{
// Print the errors
foreach (var error in errors)
{
Console.WriteLine($"Error in {error.Key}: {string.Join(", ", error.Value)}");
}
}
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
L’exemple précédent génère la sortie suivante :
Error in Person: The current value 'John' at path 'FirstName' is not equal
to the test value 'Jane'.
{
"firstName": "John",
"lastName": "Smith", <<< Modified!
"email": "janedoe@gmail.com", <<< Modified!
"phoneNumbers": []
}
Réduction des risques de sécurité
Lorsque vous utilisez le Microsoft.AspNetCore.JsonPatch.SystemTextJson
package, il est essentiel de comprendre et d’atténuer les risques de sécurité potentiels. Les sections suivantes décrivent les risques de sécurité identifiés associés au correctif JSON et fournissent des atténuations recommandées pour garantir l’utilisation sécurisée du package.
Important
Il ne s’agit pas d’une liste exhaustive de menaces. Les développeurs d’applications doivent effectuer leurs propres révisions de modèle de menace pour déterminer une liste complète spécifique à l’application et fournir des atténuations appropriées en fonction des besoins. Par exemple, les applications qui exposent des regroupements à des opérations correctives doivent tenir compte du risque d’attaques de complexité algorithmique si ces opérations insèrent ou suppriment des éléments au début de la collection.
Pour réduire les risques de sécurité lors de l’intégration des fonctionnalités de correctif JSON dans leurs applications, les développeurs doivent :
- Exécutez des modèles de menace complets pour leurs propres applications.
- Résoudre les menaces identifiées.
- Suivez les atténuations recommandées dans les sections suivantes.
Déni de service (DoS) via l’amplification de la mémoire
- Scénario : un client malveillant envoie une
copy
opération qui duplique plusieurs fois des graphiques d’objets volumineux, ce qui entraîne une consommation excessive de mémoire. - Impact : conditions Out-Of-Memory (OOM) potentielles, provoquant des interruptions de service.
- Atténuation :
- Validez les documents de patch JSON entrants quant à la taille et à la structure avant d’appeler ApplyTo(Object).
- La validation doit être spécifique à l’application, mais un exemple de validation peut ressembler à ce qui suit :
public void Validate(JsonPatchDocument<T> 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();
}
}
Subversion de la logique métier
- Scénario : les opérations correctives peuvent manipuler des champs avec des invariants implicites (par exemple, des indicateurs internes, des ID ou des champs calculés), en violation des contraintes métier.
- Impact : problèmes d’intégrité des données et comportement inattendu de l’application.
- Atténuation :
- Utilisez des objets POC (objets CLR simples) avec des propriétés définies explicitement qui sont sécurisées pour les modifier.
- Évitez d’exposer des propriétés sensibles ou critiques de sécurité dans l’objet cible.
- Si un objet POCO n’est pas utilisé, validez l’objet corrigé après avoir appliqué des opérations pour vous assurer que les règles d’entreprise et les invariants ne sont pas violés.
- Utilisez des objets POC (objets CLR simples) avec des propriétés définies explicitement qui sont sécurisées pour les modifier.
Authentification et autorisation
- Scénario : les clients non authentifiés ou non autorisés envoient des demandes de correctif JSON malveillantes.
- Impact : accès non autorisé pour modifier des données sensibles ou perturber le comportement de l’application.
- Atténuation :
- Protégez les points de terminaison acceptant les demandes de correctif JSON avec des mécanismes d’authentification et d’autorisation appropriés.
- Restreindre l’accès aux clients ou utilisateurs approuvés disposant d’autorisations appropriées.
Obtenir le code
Affichez ou téléchargez l’exemple de code. (Guide pratique de téléchargement).
Pour tester l’exemple, exécutez l’application et envoyez des demandes HTTP avec les paramètres suivants :
- URL :
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Méthode HTTP :
PATCH
- En-tête :
Content-Type: application/json-patch+json
- Corps : Copiez et collez l’un des exemples de document JSON Patch à partir du dossier de projet JSON.
Ressources supplémentaires
Cet article explique comment gérer les demandes de correctifs JSON dans une API web ASP.NET Core.
Important
La norme json Patch présente des risques de sécurité inhérents. Cette implémentation ne tente pas d’atténuer ces risques de sécurité inhérents. Il incombe au développeur de s’assurer que le document patch JSON est sûr de s’appliquer à l’objet cible. Pour plus d’informations, consultez la section Atténuation des risques de sécurité .
Installation de package
La prise en charge de JSON Patch dans l'API Web ASP.NET Core est basée sur Newtonsoft.Json
et nécessite le package Microsoft.AspNetCore.Mvc.NewtonsoftJson
NuGet.
Pour activer la prise en charge de JSON Patch :
Installez le package NuGet
Microsoft.AspNetCore.Mvc.NewtonsoftJson
.Appelez AddNewtonsoftJson. Par exemple :
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddNewtonsoftJson(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
AddNewtonsoftJson
remplace les formateurs d’entrée et de sortie par défaut basés sur System.Text.Json
utilisés pour formater tout le contenu JSON. Cette méthode d'extension est compatible avec les méthodes d'inscription de service MVC suivantes :
JsonPatch nécessite de définir l'en-tête Content-Type
sur application/json-patch+json
.
Ajout de la prise en charge de JSON Patch lors de l’utilisation de System.Text.Json
Le formateur d’entrée basé sur System.Text.Json
ne prend pas en charge JSON Patch. Pour ajouter la prise en charge de JSON Patch à l’aide de Newtonsoft.Json
, tout en laissant les autres formateurs d’entrée et de sortie inchangés :
Installez le package NuGet
Microsoft.AspNetCore.Mvc.NewtonsoftJson
.Mettez à jour
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(); } }
Le code précédent crée une instance de NewtonsoftJsonPatchInputFormatter et l'insère en tant que première entrée de la collection MvcOptions.InputFormatters. Cet ordre d'inscription garantit que :
-
NewtonsoftJsonPatchInputFormatter
traite les requêtes de JSON Patch. - Les formateurs et les entrées basés sur
System.Text.Json
actuels gèrent toutes les autres requêtes et réponses JSON.
Utilisez la méthode Newtonsoft.Json.JsonConvert.SerializeObject
pour sérialiser un JsonPatchDocument.
Méthode de demande HTTP PATCH
Les méthodes PUT et PATCH sont utilisées pour mettre à jour une ressource existante. La différence est que PUT remplace la ressource complète, tandis que PATCH spécifie uniquement les modifications.
Correctif JSON
JSON Patch est un format qui spécifie des mises à jour à appliquer à une ressource. Un document JSON Patch possède un tableau des opérations. Chaque opération identifie un type particulier de changement. Des exemples de telles modifications incluent l'ajout d'un élément de tableau ou le remplacement d'une valeur de propriété.
Par exemple, les documents JSON suivants représentent une ressource, un document JSON Patch pour la ressource et le résultat de l’application des opérations Patch.
Exemple de ressource
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
Exemple de correctif JSON
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Dans le code JSON précédent :
- La propriété
op
indique le type d’opération. - La propriété
path
indique l’élément à mettre à jour. - La propriété
value
fournit la nouvelle valeur.
Ressources après correction
Voici la ressource après l’application du document JSON Patch précédent :
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
Les modifications apportées en appliquant un document JSON Patch à une ressource sont atomiques. Si une opération de la liste échoue, aucune opération de la liste n'est appliquée.
Syntaxe du chemin
Les différents niveaux de la propriété path d’un objet de l’opération sont séparés par des barres obliques. Par exemple : "/address/zipCode"
.
Les index de base zéro sont utilisés pour spécifier les éléments du tableau. Le premier élément du tableau addresses
serait à /addresses/0
. Pour add
à la fin d’un tableau, utilisez un trait d’union (-
) plutôt qu’un numéro d’index : /addresses/-
.
Opérations
Le tableau suivant mentionne les opérations prises en charge telles qu’elles sont définies dans la spécification JSON Patch :
Opération | Remarques |
---|---|
add |
Ajouter une propriété ou élément de tableau. Pour la propriété existante : définir la valeur. |
remove |
Supprimer une propriété ou un élément de tableau. |
replace |
Identique à remove suivi de add au même emplacement. |
move |
Identique à remove de la source suivi de add à la destination à l’aide de la valeur de la source. |
copy |
Identique à add à la destination à l’aide de la valeur de la source. |
test |
Retourne le code d’état de réussite si la valeur à path = value fournie. |
JSON Patch dans ASP.NET Core
L’implémentation ASP.NET Core de JSON Patch est fournie dans le package NuGet Microsoft.AspNetCore.JsonPatch.
Code de méthode d’action
Dans un contrôleur d’API, une méthode d’action pour JSON Patch :
- est annotée avec l’attribut
HttpPatch
; - Accepte un JsonPatchDocument<TModel>, généralement avec
[FromBody]
. - appelle ApplyTo(Object) sur le document de correctif pour appliquer les modifications.
Voici un exemple :
[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);
}
}
Ce code de l’exemple d’application fonctionne avec le modèle Customer
suivant :
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; }
}
L’exemple de méthode d’action :
- Construit un objet
Customer
. - Applique le correctif.
- Retourne le résultat dans le corps de la réponse.
Dans une application réelle, le code récupérerait les données dans un magasin tel qu’une base de données et mettrait à jour la base de données après avoir appliqué le correctif.
État du modèle
L’exemple de méthode d’action précédent appelle une surcharge de ApplyTo
qui prend l’état du modèle comme un de ses paramètres. Avec cette option, vous pouvez obtenir des messages d’erreur dans les réponses. L’exemple suivant montre le corps d’une demande-réponse incorrecte 400 pour une opération test
:
{
"Customer": [
"The current value 'John' at path 'customerName' != test value 'Nancy'."
]
}
Objets dynamiques
L’exemple de méthode d’action suivant montre comment appliquer un correctif à un objet dynamique :
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
L’opération d’ajout
- Si
path
pointe vers un élément du tableau : insère un nouvel élément avant celui spécifié parpath
. - Si
path
pointe vers une propriété : définit la valeur de propriété. - Si
path
pointe vers un emplacement qui n’existe pas :- Si la ressource à corriger est un objet dynamique : ajoute une propriété.
- Si la ressource à corriger est un objet statique : la demande échoue.
L’exemple de document de correctif suivant définit la valeur de CustomerName
et ajoute un objet Order
à la fin du tableau Orders
.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de suppression
- Si
path
pointe vers un élément du tableau : supprime l’élément. - Si
path
pointe vers une propriété :- Si la ressource à corriger est un objet dynamique : supprime la propriété.
- Si la ressource à corriger est un objet statique :
- Si la propriété est Nullable : met la valeur sur Null.
- Si la propriété n’est pas Nullable : met la valeur sur
default<T>
.
L’exemple suivant de document Patch définit CustomerName
sur Null et supprime Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
L’opération de remplacement
Cette opération est fonctionnellement identique à remove
suivi de add
.
L’exemple suivant de document Patch définit la valeur de CustomerName
et remplace Orders[0]
avec un nouvel objet Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de déplacement
- Si
path
pointe vers un élément du tableau : copie l’élémentfrom
à l’emplacement de l’élémentpath
, puis exécute une opérationremove
sur l’élémentfrom
. - Si
path
pointe vers une propriété : copie la valeur de la propriétéfrom
vers la propriétépath
, puis exécute une opérationremove
sur la propriétéfrom
. - Si
path
pointe vers une propriété qui n’existe pas :- Si la ressource à corriger est un objet statique : la demande échoue.
- Si la ressource à corriger est un objet dynamique : copie la propriété
from
à un emplacement indiqué parpath
, puis exécute une opérationremove
sur la propriétéfrom
.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Met
Orders[0].OrderName
sur la valeur Null. - Déplace
Orders[1]
avantOrders[0]
.
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de copie
Cette opération est fonctionnellement identique à une opération move
sans l’étape remove
finale.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Insère une copie de
Orders[1]
avantOrders[0]
.
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de test
Si la valeur à l’emplacement indiqué par path
est différente de la valeur fournie dans value
, la demande échoue. Dans ce cas, toute la demande PATCH échoue, même si toutes les autres opérations dans le document de correctif réussiraient autrement.
L’opération test
est généralement utilisée pour empêcher une mise à jour lorsqu’il y a un conflit d’accès concurrentiel.
L’exemple de document de correctif suivant n’a aucun effet si la valeur initiale de CustomerName
est « John », car le test échoue :
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Obtenir le code
Affichez ou téléchargez l’exemple de code. (Guide pratique de téléchargement).
Pour tester l’exemple, exécutez l’application et envoyez des demandes HTTP avec les paramètres suivants :
- URL :
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Méthode HTTP :
PATCH
- En-tête :
Content-Type: application/json-patch+json
- Corps : Copiez et collez l’un des exemples de document JSON Patch à partir du dossier de projet JSON.
Réduction des risques de sécurité
Lorsque vous utilisez le Microsoft.AspNetCore.JsonPatch
paquet avec l'implémentation basée sur Newtonsoft.Json
, il est essentiel de comprendre et d’atténuer les risques de sécurité potentiels. Les sections suivantes décrivent les risques de sécurité identifiés associés au correctif JSON et fournissent des atténuations recommandées pour garantir l’utilisation sécurisée du package.
Important
Il ne s’agit pas d’une liste exhaustive de menaces. Les développeurs d’applications doivent effectuer leurs propres révisions de modèle de menace pour déterminer une liste complète spécifique à l’application et fournir des atténuations appropriées en fonction des besoins. Par exemple, les applications qui exposent des regroupements à des opérations correctives doivent tenir compte du risque d’attaques de complexité algorithmique si ces opérations insèrent ou suppriment des éléments au début de la collection.
En exécutant des modèles de menace complets pour leurs propres applications et en traitant les menaces identifiées tout en suivant les atténuations recommandées ci-dessous, les consommateurs de ces packages peuvent intégrer des fonctionnalités de correctif JSON dans leurs applications tout en réduisant les risques de sécurité.
Déni de service (DoS) via l’amplification de la mémoire
- Scénario : un client malveillant envoie une
copy
opération qui duplique plusieurs fois des graphiques d’objets volumineux, ce qui entraîne une consommation excessive de mémoire. - Impact : conditions Out-Of-Memory (OOM) potentielles, provoquant des interruptions de service.
- Atténuation :
- Validez les documents de patch JSON entrants quant à la taille et à la structure avant d’appeler
ApplyTo
. - La validation doit être spécifique à l’application, mais un exemple de validation peut ressembler à ce qui suit :
- Validez les documents de patch JSON entrants quant à la taille et à la structure avant d’appeler
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();
}
}
Subversion de la logique métier
- Scénario : les opérations correctives peuvent manipuler des champs avec des invariants implicites (par exemple, des indicateurs internes, des ID ou des champs calculés), en violation des contraintes métier.
- Impact : problèmes d’intégrité des données et comportement inattendu de l’application.
- Atténuation :
- Utilisez des objets POCO avec des propriétés définies explicitement qui sont sécurisées à modifier.
- Évitez d’exposer des propriétés sensibles ou critiques de sécurité dans l’objet cible.
- Si aucun objet POCO n’est utilisé, validez l’objet corrigé après l’application d’opérations pour vous assurer que les règles d’entreprise et les invariants ne sont pas violés.
Authentification et autorisation
- Scénario : les clients non authentifiés ou non autorisés envoient des demandes de correctif JSON malveillantes.
- Impact : accès non autorisé pour modifier des données sensibles ou perturber le comportement de l’application.
- Atténuation :
- Protégez les points de terminaison acceptant les demandes de correctif JSON avec des mécanismes d’authentification et d’autorisation appropriés.
- Restreindre l’accès aux clients ou utilisateurs approuvés disposant d’autorisations appropriées.
Ressources supplémentaires
Cet article explique comment gérer les demandes de correctifs JSON dans une API web ASP.NET Core.
Important
La norme json Patch présente des risques de sécurité inhérents. Étant donné que ces risques sont inhérents à la norme json Patch, cette implémentation ne tente pas d’atténuer les risques de sécurité inhérents. Il incombe au développeur de s’assurer que le document patch JSON est sûr de s’appliquer à l’objet cible. Pour plus d’informations, consultez la section Atténuation des risques de sécurité .
Installation de package
Pour activer la prise en charge de JSON Patch dans votre application, procédez comme suit :
Installez le package NuGet
Microsoft.AspNetCore.Mvc.NewtonsoftJson
.Mettez à jour la méthode
Startup.ConfigureServices
du projet pour appeler AddNewtonsoftJson. Par exemple :services .AddControllersWithViews() .AddNewtonsoftJson();
AddNewtonsoftJson
est compatible avec les méthodes d’inscription des services MVC :
JSON Patch, AddNewtonsoftJson et System.Text.Json
AddNewtonsoftJson
remplace les formateurs d’entrée et de sortie basés sur System.Text.Json
utilisés pour formater tout le contenu JSON. Pour ajouter la prise en charge des correctifs JSON en utilisant Newtonsoft.Json
, tout en laissant les autres formateurs inchangés, mettez à jour la méthode Startup.ConfigureServices
du projet comme suit :
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();
}
Le code précédent requiert le package Microsoft.AspNetCore.Mvc.NewtonsoftJson
et les instructions using
suivantes :
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;
La méthode Newtonsoft.Json.JsonConvert.SerializeObject
permet de sérialiser un JsonPatchDocument.
Méthode de demande HTTP PATCH
Les méthodes PUT et PATCH sont utilisées pour mettre à jour une ressource existante. La différence est que PUT remplace la ressource complète, tandis que PATCH spécifie uniquement les modifications.
Correctif JSON
JSON Patch est un format qui spécifie des mises à jour à appliquer à une ressource. Un document JSON Patch possède un tableau des opérations. Chaque opération identifie un type particulier de changement. Des exemples de telles modifications incluent l'ajout d'un élément de tableau ou le remplacement d'une valeur de propriété.
Par exemple, les documents JSON suivants représentent une ressource, un document JSON Patch pour la ressource et le résultat de l’application des opérations Patch.
Exemple de ressource
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
Exemple de correctif JSON
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Dans le code JSON précédent :
- La propriété
op
indique le type d’opération. - La propriété
path
indique l’élément à mettre à jour. - La propriété
value
fournit la nouvelle valeur.
Ressources après correction
Voici la ressource après l’application du document JSON Patch précédent :
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
Les modifications apportées en appliquant un document JSON Patch à une ressource sont atomiques. Si une opération de la liste échoue, aucune opération de la liste n'est appliquée.
Syntaxe du chemin
Les différents niveaux de la propriété path d’un objet de l’opération sont séparés par des barres obliques. Par exemple : "/address/zipCode"
.
Les index de base zéro sont utilisés pour spécifier les éléments du tableau. Le premier élément du tableau addresses
serait à /addresses/0
. Pour add
à la fin d’un tableau, utilisez un trait d’union (-
) plutôt qu’un numéro d’index : /addresses/-
.
Opérations
Le tableau suivant mentionne les opérations prises en charge telles qu’elles sont définies dans la spécification JSON Patch :
Opération | Remarques |
---|---|
add |
Ajouter une propriété ou élément de tableau. Pour la propriété existante : définir la valeur. |
remove |
Supprimer une propriété ou un élément de tableau. |
replace |
Identique à remove suivi de add au même emplacement. |
move |
Identique à remove de la source suivi de add à la destination à l’aide de la valeur de la source. |
copy |
Identique à add à la destination à l’aide de la valeur de la source. |
test |
Retourne le code d’état de réussite si la valeur à path = value fournie. |
JSON Patch dans ASP.NET Core
L’implémentation ASP.NET Core de JSON Patch est fournie dans le package NuGet Microsoft.AspNetCore.JsonPatch.
Code de méthode d’action
Dans un contrôleur d’API, une méthode d’action pour JSON Patch :
- est annotée avec l’attribut
HttpPatch
; - Accepte un
JsonPatchDocument<T>
, généralement avec[FromBody]
. - appelle
ApplyTo
sur le document de correctif pour appliquer les modifications.
Voici un exemple :
[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);
}
}
Ce code de l’exemple d’application fonctionne avec le modèle Customer
suivant :
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; }
}
}
L’exemple de méthode d’action :
- Construit un objet
Customer
. - Applique le correctif.
- Retourne le résultat dans le corps de la réponse.
Dans une application réelle, le code récupérerait les données dans un magasin tel qu’une base de données et mettrait à jour la base de données après avoir appliqué le correctif.
État du modèle
L’exemple de méthode d’action précédent appelle une surcharge de ApplyTo
qui prend l’état du modèle comme un de ses paramètres. Avec cette option, vous pouvez obtenir des messages d’erreur dans les réponses. L’exemple suivant montre le corps d’une demande-réponse incorrecte 400 pour une opération test
:
{
"Customer": [
"The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
]
}
Objets dynamiques
L’exemple de méthode d’action suivant montre comment appliquer un correctif à un objet dynamique :
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
L’opération d’ajout
- Si
path
pointe vers un élément du tableau : insère un nouvel élément avant celui spécifié parpath
. - Si
path
pointe vers une propriété : définit la valeur de propriété. - Si
path
pointe vers un emplacement qui n’existe pas :- Si la ressource à corriger est un objet dynamique : ajoute une propriété.
- Si la ressource à corriger est un objet statique : la demande échoue.
L’exemple de document de correctif suivant définit la valeur de CustomerName
et ajoute un objet Order
à la fin du tableau Orders
.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de suppression
- Si
path
pointe vers un élément du tableau : supprime l’élément. - Si
path
pointe vers une propriété :- Si la ressource à corriger est un objet dynamique : supprime la propriété.
- Si la ressource à corriger est un objet statique :
- Si la propriété est Nullable : met la valeur sur Null.
- Si la propriété n’est pas Nullable : met la valeur sur
default<T>
.
L’exemple suivant de document Patch définit CustomerName
sur Null et supprime Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
L’opération de remplacement
Cette opération est fonctionnellement identique à remove
suivi de add
.
L’exemple suivant de document Patch définit la valeur de CustomerName
et remplace Orders[0]
avec un nouvel objet Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
L’opération de déplacement
- Si
path
pointe vers un élément du tableau : copie l’élémentfrom
à l’emplacement de l’élémentpath
, puis exécute une opérationremove
sur l’élémentfrom
. - Si
path
pointe vers une propriété : copie la valeur de la propriétéfrom
vers la propriétépath
, puis exécute une opérationremove
sur la propriétéfrom
. - Si
path
pointe vers une propriété qui n’existe pas :- Si la ressource à corriger est un objet statique : la demande échoue.
- Si la ressource à corriger est un objet dynamique : copie la propriété
from
à un emplacement indiqué parpath
, puis exécute une opérationremove
sur la propriétéfrom
.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Met
Orders[0].OrderName
sur la valeur Null. - Déplace
Orders[1]
avantOrders[0]
.
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de copie
Cette opération est fonctionnellement identique à une opération move
sans l’étape remove
finale.
L’exemple de document de correctif suivant :
- Copie la valeur de
Orders[0].OrderName
versCustomerName
. - Insère une copie de
Orders[1]
avantOrders[0]
.
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
L’opération de test
Si la valeur à l’emplacement indiqué par path
est différente de la valeur fournie dans value
, la demande échoue. Dans ce cas, toute la demande PATCH échoue, même si toutes les autres opérations dans le document de correctif réussiraient autrement.
L’opération test
est généralement utilisée pour empêcher une mise à jour lorsqu’il y a un conflit d’accès concurrentiel.
L’exemple de document de correctif suivant n’a aucun effet si la valeur initiale de CustomerName
est « John », car le test échoue :
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Obtenir le code
Affichez ou téléchargez l’exemple de code. (Guide pratique de téléchargement).
Pour tester l’exemple, exécutez l’application et envoyez des demandes HTTP avec les paramètres suivants :
- URL :
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Méthode HTTP :
PATCH
- En-tête :
Content-Type: application/json-patch+json
- Corps : Copiez et collez l’un des exemples de document JSON Patch à partir du dossier de projet JSON.