web-dev-qa-db-fra.com

Web API 2 - Implémentation d'un correctif

J'ai actuellement une API Web qui implémente une API RESTFul. Le modèle de mon API ressemble à ceci:

public class Member
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Created { get; set; }
    public DateTime BirthDate { get; set; }
    public bool IsDeleted { get; set; }
}

J'ai implémenté une méthode PUT pour mettre à jour une ligne similaire à celle-ci (par souci de brièveté, j'ai omis certains éléments non pertinents):

[Route("{id}")]
[HttpPut]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id, 
    [FromBody]Models.Member model)
{
    // Do some error checking
    // ...
    // ...

    var myDatabaseEntity = new BusinessLayer.Member(id);
    myDatabaseEntity.FirstName = model.FirstName;
    myDatabaseEntity.LastName = model.LastName;
    myDatabaseEntity.Created = model.Created;
    myDatabaseEntity.BirthDate = model.BirthDate;
    myDatabaseEntity.IsDeleted = model.IsDeleted;

    await myDatabaseEntity.SaveAsync();
}

Avec PostMan , je peux envoyer le JSON suivant et tout fonctionne correctement:

{
    firstName: "Sara",
    lastName: "Smith",
    created: '2018/05/10",
    birthDate: '1977/09/12",
    isDeleted: false
}

Si j'envoie ceci en tant que corps à http://localhost:8311/api/v1/Member/12 en tant que demande PUT, l'enregistrement de mes données avec l'ID 12 est mis à jour pour correspondre à ce que vous voyez dans le JSON.

Ce que je voudrais cependant faire, c’est d’implémenter un verbe PATCH où je peux faire des mises à jour partielles. Si Sara se marie, j'aimerais pouvoir envoyer ce JSON:

{
    lastName: "Jones"
}

J'aimerais pouvoir envoyer uniquement ce JSON, mettre à jour JUST le champ LastName et laisser tous les autres champs seuls.

J'ai essayé ceci:

[Route("{id}")]
[HttpPatch]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id, 
    [FromBody]Models.Member model)
{
}

Mon problème est que cela renvoie tous les champs de l'objet model (tous sont nuls sauf le champ LastName), ce qui est logique puisque je dis que je veux un objet Models.Member. Ce que j'aimerais savoir, c'est s'il existe un moyen de détecter les propriétés qui ont été réellement envoyées dans la demande JSON afin que je puisse mettre à jour uniquement ces champs?

6
Icemanind

Les opérations PATCH ne sont généralement pas définies à l'aide du même modèle que les opérations POST ou PUT exactement pour cette raison: comment différenciez-vous une null et un don't change. De l'IETF :

Avec PATCH, toutefois, l'entité incluse contient un ensemble de des instructions décrivant comment une ressource résidant actuellement sur le Le serveur d'origine doit être modifié pour produire une nouvelle version.

Vous pouvez regarder ici pour leur suggestion PATCH, mais sumarilly est:

[
    { "op": "test", "path": "/a/b/c", "value": "foo" },
    { "op": "remove", "path": "/a/b/c" },
    { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
    { "op": "replace", "path": "/a/b/c", "value": 42 },
    { "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
    { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]
9
Tipx

La réponse de @ Tipx concernant l'utilisation de PATCH est parfaite, mais comme vous l'avez probablement déjà constaté, atteindre cet objectif dans un langage à typage statique comme C # est un exercice non trivial.

Dans le cas où vous utilisez une variable PATCH pour représenter un ensemble de mises à jour partielles pour une entité de domaine unique (par exemple, pour mettre à jour le prénom et le nom de famille uniquement pour un contact avec beaucoup plus de propriétés), vous devez effectuer une opération similaire à boucler chaque instruction dans la requête 'PATCH' puis appliquer cette instruction à une instance de votre classe.

L’application d’une instruction individuelle comportera alors: 

  • Recherche de la propriété de l'instance qui correspond au nom dans l'instruction Ou gestion des noms de propriété inattendus
  • Pour une mise à jour: essayez d’analyser la valeur soumise dans le correctif dans la propriété d’instance et gérez l’erreur, par exemple. la propriété d'instance est un bool mais l'instruction de patch contient une date
  • Décider quoi faire avec Ajouter des instructions car vous ne pouvez pas ajouter de nouvelles propriétés à une classe C # statiquement typée. Une approche consiste à dire que Ajouter signifie "définir la valeur de la propriété de l'instance uniquement si la valeur existante de la propriété est null"

Pour le Web API 2 sur l'intégralité du .NET Framework, le projet github JSONPatch tente de fournir un code pour fournir ce code, bien qu'il ne semble pas y avoir eu beaucoup de développement sur ce repo récemment et le readme indique : 

C’est encore un projet précoce, ne l’utilisez pas en production pourtant, à moins que vous ne compreniez la source et que vous ayez l'esprit tranquille, corrigez quelques bugs ;)

Les choses sont plus simples sur .NET Core, car il dispose d'un ensemble de fonctionnalités pour le prendre en charge dans l'espace de noms Microsoft.AspNetCore.JsonPatch .

Le site très utile jsonpatch.com répertorie également quelques options supplémentaires pour Patch dans .NET:

  • Asp.Net Core JsonPatch (implémentation officielle de Microsoft)
  • Ramone (un cadre pour la consommation de services REST, comprend une implémentation de correctif JSON)
  • JsonPatch (Ajoute la prise en charge du correctif JSON à l'API Web ASP.NET)
  • Starcounter (Application Engine en mémoire, utilise le correctif JSON avec OT pour la synchronisation client-serveur)
  • Nancy.JsonPatch (Ajoute la prise en charge du correctif JSON à NancyFX)
  • Manatee.Json (JSON-tout, y compris le correctif JSON)

Je dois ajouter cette fonctionnalité à l'un de nos projets Web API 2 existant. Je vais donc mettre à jour cette réponse si je trouve autre chose utile en le faisant.

0
tomRedox