JsonPatch ve webovém rozhraní API ASP.NET Core

Tento článek vysvětluje, jak zpracovávat JSpožadavky ON Patch ve webovém rozhraní API ASP.NET Core.

Instalace balíčku

JSPodpora ON Patch ve webovém rozhraní API ASP.NET Core je založená a Newtonsoft.Json vyžaduje Microsoft.AspNetCore.Mvc.NewtonsoftJson balíček NuGet. JSPovolení podpory ON Patch:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson Nainstalujte balíček NuGet.

  • Zavolejte AddNewtonsoftJson. Příklad:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddControllers()
        .AddNewtonsoftJson();
    
    var app = builder.Build();
    
    app.UseHttpsRedirection();
    
    app.UseAuthorization();
    
    app.MapControllers();
    
    app.Run();
    

AddNewtonsoftJson nahrazuje výchozí System.Text.Jsonvstupní a výstupní formátovací moduly použité pro formátování veškeréhoJSobsahu ON. Tato metoda rozšíření je kompatibilní s následujícími metodami registrace služby MVC:

Přidání podpory pro JSFUNKCI ON Patch při použití souboru System.Text.Json

Formátovací System.Text.Jsonmodul pro zadávání na základě nepodporuje JSfunkci ON Patch. Pokud chcete přidat podporu pro JSon Patch pomocí Newtonsoft.Json, zatímco ostatní vstupní a výstupní formátovací moduly zůstanou beze změny:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson Nainstalujte balíček NuGet.

  • Aktualizace 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();
        }
    }
    

Předchozí kód vytvoří instanci NewtonsoftJsonPatchInputFormatter a vloží ji jako první položku v kolekci MvcOptions.InputFormatters . Toto pořadí registrace zajišťuje, že:

  • NewtonsoftJsonPatchInputFormatter zpracovává JSžádosti ON Patch.
  • Existující vstupní a formátovací moduly založené na stávajícím System.Text.Jsonprocesu zpracovávají všechny ostatní JSpožadavky a odpovědi ON.

Newtonsoft.Json.JsonConvert.SerializeObject Použití metody serializace JsonPatchDocument.

Metoda požadavku PATCH HTTP

Metody PUT a PATCH slouží k aktualizaci existujícího prostředku. Rozdíl mezi nimi spočívá v tom, že PUT nahrazuje celý prostředek, zatímco PATCH určuje pouze změny.

JSON Patch

JSON Patch je formát pro určení aktualizací, které se mají použít u prostředku. Dokument JSON Patch obsahuje pole operací. Každá operace identifikuje konkrétní typ změny. Mezi příklady takových změn patří přidání prvku pole nebo nahrazení hodnoty vlastnosti.

Například následující JSdokumenty ON představují prostředek, JSdokument ON Patch pro prostředek a výsledek použití operací oprav.

Příklad prostředku

{
  "customerName": "John",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    }
  ]
}

JSPříklad opravy ON

[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]

V předchozím on JS:

  • Vlastnost op označuje typ operace.
  • Vlastnost path označuje prvek, který se má aktualizovat.
  • Vlastnost value poskytuje novou hodnotu.

Prostředek po opravě

Po použití předchozího JSdokumentu ON Patch je tento prostředek:

{
  "customerName": "Barry",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    },
    {
      "orderName": "Order2",
      "orderType": null
    }
  ]
}

Změny provedené použitím JSdokumentu ON Patch na prostředek jsou atomické. Pokud některá operace v seznamu selže, nepoužije se žádná operace v seznamu.

Syntaxe cesty

Vlastnost cesty objektu operace má lomítka mezi úrovněmi. Například, "/address/zipCode".

Indexy založené na nule se používají k určení prvků pole. První prvek addresses pole by byl na /addresses/0. Chcete-li add na konec pole, použijte místo indexového čísla spojovník (-) spojovník: /addresses/-.

Operace

Následující tabulka uvádí podporované operace definované ve JSspecifikaci ON Patch:

Operace Poznámky
add Přidejte vlastnost nebo prvek pole. Pro existující vlastnost: nastavte hodnotu.
remove Odeberte vlastnost nebo prvek pole.
replace Stejné jako remove u add stejného umístění.
move Stejné jako remove ze zdroje, add za nímž následuje cíl s použitím hodnoty ze zdroje.
copy Stejné jako add cíl s použitím hodnoty ze zdroje.
test Vrátí stavový kód úspěchu, pokud je hodnota = path zadaná value.

JSON Patch v ASP.NET Core

Základní implementace JSon Patch ASP.NET je k dispozici v balíčku NuGet Microsoft.AspNetCore.JsonPatch .

Kód metody akce

V kontroleru rozhraní API metoda akce pro JSON Patch:

Tady je příklad:

[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);
    }
}

Tento kód z ukázkové aplikace funguje s následujícím Customer modelem:

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; }
}

Ukázková metoda akce:

  • Vytvoří .Customer
  • Použije opravu.
  • Vrátí výsledek v textu odpovědi.

V reálné aplikaci by kód načetl data z úložiště, jako je databáze, a po instalaci opravy databázi aktualizoval.

Stav modelu

Příklad předchozí metody akce volá přetížení ApplyTo , které přebírá stav modelu jako jeden z jeho parametrů. S touto možností můžete v odpovědích zobrazit chybové zprávy. Následující příklad ukazuje text odpovědi 400 Chybný požadavek pro test operaci:

{
  "Customer": [
    "The current value 'John' at path 'customerName' != test value 'Nancy'."
  ]
}

Dynamické objekty

Následující příklad metody akce ukazuje, jak použít opravu na dynamický objekt:

[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
    dynamic obj = new ExpandoObject();
    patch.ApplyTo(obj);

    return Ok(obj);
}

Operace přidání

  • Pokud path odkazuje na prvek pole: vloží nový prvek před prvek určený path.
  • Pokud path odkazuje na vlastnost: nastaví hodnotu vlastnosti.
  • Pokud path odkazuje na neexistující umístění:
    • Pokud je prostředek k opravě dynamickým objektem: přidá vlastnost.
    • Pokud je prostředek k opravě statický objekt: požadavek selže.

Následující ukázkový dokument opravy nastaví hodnotu CustomerName a přidá Order objekt na konec Orders pole.

[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]

Operace odebrání

  • Pokud path odkazuje na prvek pole: odebere prvek.
  • Pokud path odkazuje na vlastnost:
    • Pokud prostředek k opravě je dynamický objekt: odebere vlastnost.
    • Pokud je prostředek pro opravu statický objekt:
      • Pokud je vlastnost nullable: nastaví ji na hodnotu null.
      • Pokud vlastnost není null, nastaví ji na default<T>.

Následující ukázkový dokument opravy nastaví CustomerName hodnotu null a odstraní Orders[0]:

[
  {
    "op": "remove",
    "path": "/customerName"
  },
  {
    "op": "remove",
    "path": "/orders/0"
  }
]

Operace nahrazení

Tato operace je funkčně stejná jako remove za ním add.

Následující ukázkový dokument opravy nastaví hodnotu CustomerName a nahradí Orders[0]novým Order objektem:

[
  {
    "op": "replace",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "replace",
    "path": "/orders/0",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]

Operace přesunutí

  • Pokud path odkazuje na prvek pole: zkopíruje from prvek do umístění elementu path , spustí remove operaci na elementu from .
  • Pokud path odkazuje na vlastnost: zkopíruje hodnotu from vlastnosti do path vlastnosti, pak spustí remove operaci s vlastností from .
  • Pokud path odkazuje na neexistující vlastnost:
    • Pokud je prostředek k opravě statickým objektem: požadavek selže.
    • Pokud je prostředek k opravě dynamickým objektem: zkopíruje from vlastnost do umístění označeného path, pak spustí remove operaci u from vlastnosti.

Následující ukázkový dokument opravy:

  • Zkopíruje hodnotu Orders[0].OrderName do CustomerName.
  • Nastaví Orders[0].OrderName na hodnotu null.
  • Přesune Orders[1] se na před Orders[0].
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Operace kopírování

Tato operace je funkčně stejná jako move operace bez posledního remove kroku.

Následující ukázkový dokument opravy:

  • Zkopíruje hodnotu Orders[0].OrderName do CustomerName.
  • Vloží kopii Orders[1] před Orders[0].
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Testovací operace

Pokud se hodnota v umístění označeném path hodnotou liší od zadané hodnoty value, požadavek selže. V takovém případě se celý požadavek PATCH nezdaří, i když by všechny ostatní operace v dokumentu opravy jinak proběhly úspěšně.

Tato test operace se běžně používá k zabránění aktualizaci v případě konfliktu souběžnosti.

Následující ukázkový dokument opravy nemá žádný vliv, pokud je počáteční hodnota CustomerName "John", protože test selže:

[
  {
    "op": "test",
    "path": "/customerName",
    "value": "Nancy"
  },
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  }
]

Získání kódu

Zobrazení nebo stažení ukázkového kódu (Jak stáhnout).

Pokud chcete ukázku otestovat, spusťte aplikaci a odešlete požadavky HTTP s následujícím nastavením:

  • ADRESU URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • Metoda HTTP: PATCH
  • Záhlaví: Content-Type: application/json-patch+json
  • Text: Zkopírujte a vložte jednu z JSukázek dokumentu opravy ON ze JS složky projektu ON.

Další materiály

Tento článek vysvětluje, jak zpracovávat JSpožadavky ON Patch ve webovém rozhraní API ASP.NET Core.

Instalace balíčku

Pokud chcete v aplikaci povolit JSpodporu ON Patch, postupujte následovně:

  1. Microsoft.AspNetCore.Mvc.NewtonsoftJson Nainstalujte balíček NuGet.

  2. Aktualizujte metodu Startup.ConfigureServices projektu tak, aby volala AddNewtonsoftJson. Příklad:

    services
        .AddControllersWithViews()
        .AddNewtonsoftJson();
    

AddNewtonsoftJson je kompatibilní s metodami registrace služby MVC:

JSON Patch, AddNewtonsoftJson a System.Text.Json

AddNewtonsoftJson nahradí vstupní a výstupní formátovací moduly System.Text.Jsonzaložené na formátování veškeréhoJSobsahu ON. Pokud chcete přidat podporu pro JSon Patch pomocí Newtonsoft.Json, zatímco ostatní formátovací moduly ponecháte beze změny, aktualizujte metodu Startup.ConfigureServices projektu následujícím způsobem:

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();
}

Předchozí kód vyžaduje Microsoft.AspNetCore.Mvc.NewtonsoftJson balíček a následující using příkazy:

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;

Použijte metodu Newtonsoft.Json.JsonConvert.SerializeObject k serializaci JsonPatchDocument.

Metoda požadavku PATCH HTTP

Metody PUT a PATCH slouží k aktualizaci existujícího prostředku. Rozdíl mezi nimi spočívá v tom, že FUNKCE PUT nahrazuje celý prostředek, zatímco patch určuje pouze změny.

JSON Patch

JSON Patch je formát pro určení aktualizací, které se mají použít u prostředku. JSDokument ON Patch má pole operací. Každá operace identifikuje konkrétní typ změny. Mezi příklady takových změn patří přidání maticového prvku nebo nahrazení hodnoty vlastnosti.

Například následující JSdokumenty ON představují prostředek, JSdokument ON Patch pro daný prostředek a výsledek použití operací opravy.

Příklad prostředku

{
  "customerName": "John",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    }
  ]
}

JSPříklad opravy ON

[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]

V předchozím zapnutí JS:

  • Vlastnost op označuje typ operace.
  • Vlastnost path označuje prvek, který se má aktualizovat.
  • Vlastnost value poskytuje novou hodnotu.

Prostředek po opravě

Tady je prostředek po použití předchozího JSdokumentu ON Patch:

{
  "customerName": "Barry",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    },
    {
      "orderName": "Order2",
      "orderType": null
    }
  ]
}

Změny provedené použitím JSdokumentu ON Patch na prostředek jsou atomické. Pokud nějaká operace v seznamu selže, nebude použita žádná operace v seznamu.

Syntaxe cesty

Vlastnost cesty objektu operace má lomítka mezi úrovněmi. Například, "/address/zipCode".

Indexy založené na nule slouží k určení prvků pole. První prvek addresses pole by byl na /addresses/0. Na add konec pole použijte místo čísla /addresses/-indexu spojovník (-).

Operace

Následující tabulka ukazuje podporované operace definované ve JSspecifikaci ON Patch:

Operace Poznámky
add Přidejte vlastnost nebo maticový prvek. Pro existující vlastnost: nastavit hodnotu.
remove Odeberte vlastnost nebo prvek pole.
replace Stejné jako remove v případě, že add následuje na stejném místě.
move Stejné jako remove ze zdroje následované add cílem pomocí hodnoty ze zdroje.
copy Stejné jako add cíl s použitím hodnoty ze zdroje.
test Vrátí stavový kód úspěchu, pokud je hodnota zadaná pathvalue.

JSON Patch in ASP.NET Core

ASP.NET V balíčku NuGet Microsoft.AspNetCore.JsonPatch je k dispozici základní implementace JSon Patch.

Kód metody akce

V kontroleru rozhraní API metoda akce pro JSON Patch:

  • Je označen atributem HttpPatch .
  • JsonPatchDocument<T>Přijímá , obvykle s [FromBody].
  • Zavolá ApplyTo dokument opravy, aby změny použil.

Tady je příklad:

[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);
    }
}

Tento kód z ukázkové aplikace funguje s následujícím Customer modelem:

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; }
    }
}

Ukázková metoda akce:

  • CustomerVytvoří .
  • Použije opravu.
  • Vrátí výsledek v textu odpovědi.

V reálné aplikaci by kód načetl data z úložiště, jako je databáze, a po použití opravy databázi aktualizoval.

Stav modelu

Příklad předchozí metody akce volá přetížení ApplyTo , které přebírá stav modelu jako jeden z jeho parametrů. S touto možností můžete v odpovědích zobrazit chybové zprávy. Následující příklad ukazuje text odpovědi 400 Chybný požadavek pro test operaci:

{
    "Customer": [
        "The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
    ]
}

Dynamické objekty

Následující příklad metody akce ukazuje, jak použít opravu na dynamický objekt:

[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
    dynamic obj = new ExpandoObject();
    patch.ApplyTo(obj);

    return Ok(obj);
}

Operace přidání

  • Pokud path odkazuje na prvek pole: vloží nový prvek před prvek určený path.
  • Pokud path odkazuje na vlastnost: nastaví hodnotu vlastnosti.
  • Pokud path odkazuje na neexistující umístění:
    • Pokud je prostředek k opravě dynamickým objektem: přidá vlastnost.
    • Pokud je prostředek k opravě statický objekt: požadavek selže.

Následující ukázkový dokument opravy nastaví hodnotu CustomerName a přidá Order objekt na konec Orders pole.

[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]

Operace odebrání

  • Pokud path odkazuje na prvek pole: odebere prvek.
  • Pokud path odkazuje na vlastnost:
    • Pokud prostředek k opravě je dynamický objekt: odebere vlastnost.
    • Pokud je prostředek pro opravu statický objekt:
      • Pokud je vlastnost nullable: nastaví ji na hodnotu null.
      • Pokud vlastnost není null, nastaví ji na default<T>.

Následující ukázkový dokument opravy nastaví CustomerName hodnotu null a odstraní Orders[0]:

[
  {
    "op": "remove",
    "path": "/customerName"
  },
  {
    "op": "remove",
    "path": "/orders/0"
  }
]

Operace nahrazení

Tato operace je funkčně stejná jako remove za ním add.

Následující ukázkový dokument opravy nastaví hodnotu CustomerName a nahradí Orders[0]novým Order objektem:

[
  {
    "op": "replace",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "replace",
    "path": "/orders/0",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]

Operace přesunutí

  • Pokud path odkazuje na prvek pole: zkopíruje from prvek do umístění elementu path , spustí remove operaci na elementu from .
  • Pokud path odkazuje na vlastnost: zkopíruje hodnotu from vlastnosti do path vlastnosti, pak spustí remove operaci s vlastností from .
  • Pokud path odkazuje na neexistující vlastnost:
    • Pokud je prostředek k opravě statický objekt: požadavek selže.
    • Pokud je prostředek k opravě dynamickým objektem: zkopíruje from vlastnost do umístění označeného pathpomocí , spustí remove operaci u from vlastnosti.

Následující ukázkový dokument opravy:

  • Zkopíruje hodnotu Orders[0].OrderName do CustomerName.
  • Nastaví Orders[0].OrderName hodnotu null.
  • Přesune Orders[1] se na před Orders[0].
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Operace kopírování

Tato operace je funkčně stejná jako move operace bez posledního remove kroku.

Následující ukázkový dokument opravy:

  • Zkopíruje hodnotu Orders[0].OrderName do CustomerName.
  • Vloží kopii Orders[1] před Orders[0].
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Testovací operace

Pokud se hodnota v umístění označeném path hodnotou liší od hodnoty zadané v value, požadavek selže. V takovém případě se celý požadavek PATCH nezdaří, i když by všechny ostatní operace v dokumentu opravy jinak proběhly úspěšně.

Tato test operace se běžně používá k zabránění aktualizaci, pokud dojde ke konfliktu souběžnosti.

Následující ukázkový dokument opravy nemá žádný vliv, pokud je počáteční hodnota CustomerName "John", protože test selže:

[
  {
    "op": "test",
    "path": "/customerName",
    "value": "Nancy"
  },
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  }
]

Získání kódu

Zobrazení nebo stažení vzorového kódu (Jak stáhnout).

Ukázku otestujete spuštěním aplikace a odesláním požadavků HTTP s následujícím nastavením:

  • ADRESU URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • Metoda HTTP: PATCH
  • Záhlaví: Content-Type: application/json-patch+json
  • Text: Zkopírujte a vložte jednu z JSukázek dokumentu opravy ON ze JS složky projektu ON.

Další materiály