Megosztás a következőn keresztül:


JSON-javítás támogatása a ASP.NET Core webes API-ban

Ez a cikk bemutatja, hogyan kezelheti a JSON-javítás kéréseit egy ASP.NET Core webes API-ban.

A JSON Patch támogatása a ASP.NET Core webes API-ban szerializáláson System.Text.Json alapul, és a Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet-csomagot igényli.

Mi a JSON Patch szabvány?

A JSON-javítás szabványa:

  • A JSON-dokumentumokra alkalmazandó módosítások leírásának szabványos formátuma.

  • Az RFC 6902-ben van definiálva, és széles körben használják a RESTful API-kban a JSON-erőforrások részleges frissítéséhez.

  • A JSON-dokumentumokat módosító műveletek sorozatát ismerteti, például:

    • add
    • remove
    • replace
    • move
    • copy
    • test

A webalkalmazásokban a JSON-javítást gyakran használják a PATCH műveletben az erőforrás részleges frissítéséhez. Ahelyett, hogy a teljes erőforrást elküldené egy frissítéshez, az ügyfelek küldhetnek egy JSON Patch-dokumentumot, amely csak a módosításokat tartalmazza. A javítás csökkenti a hasznos adatok méretét és javítja a hatékonyságot.

A JSON-javítás szabványának áttekintéséhez tekintse meg a jsonpatch.com.

JSON-javítás támogatása a ASP.NET Core webes API-ban

A JSON Patch támogatása az ASP.NET Core webes API-ban a System.Text.Json szerializáláson alapul, és a .NET 10-től kezdve a Microsoft.AspNetCore.JsonPatch szerializáláson alapuló System.Text.Json implementálást valósítja meg. Ez a funkció:

Note

A Microsoft.AspNetCore.JsonPatch-alapú szerializáción alapuló System.Text.Json implementáció nem helyettesíti a régi Newtonsoft.Json-alapú implementációt. Nem támogatja például ExpandoObjecta dinamikus típusokat.

Important

A JSON Patch szabvány belső biztonsági kockázatokkal jár. Mivel ezek a kockázatok a JSON Patch szabványhoz tartoznak, a ASP.NET Core-implementáció nem próbálja csökkenteni az eredendő biztonsági kockázatokat. A fejlesztő felelőssége annak biztosítása, hogy a JSON Patch-dokumentum biztonságosan alkalmazható legyen a célobjektumra. További információ: A biztonsági kockázatok mérséklése szakasz.

A JSON-javítás támogatásának engedélyezése System.Text.Json

A JSON-javítás támogatásának System.Text.Jsonengedélyezéséhez telepítse a Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet-csomagot.

dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease

Ez a csomag egy JsonPatchDocument<TModel> osztályt biztosít a JSON Patch dokumentumok T típusú objektumokhoz való reprezentálásához, valamint egyedi logikát a JSON Patch dokumentumok System.Text.Json szerializálásához és deszerializálásához. A JsonPatchDocument<TModel> osztály kulcsmetódusa a ApplyTo(Object), amely a javításműveleteket egy T típusú célobjektumra alkalmazza.

A JSON-javítást alkalmazó műveletmetódus kódja

Egy API-vezérlőben a JSON-javítás műveletmetódusa:

Példa vezérlőműveleti módszerre:

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

A mintaalkalmazásból származó kód a következőkkel Customer és Order modellekkel működik:

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

A mintaműveleti módszer fő lépései:

  • Kérje le az ügyfelet:
    • A metódus egy Customer objektumot kér le az adatbázisból AppDb a megadott azonosító használatával.
    • Ha nem található Customer objektum, 404 Not Found választ ad.
  • JSON-javítás alkalmazása:
    • A ApplyTo(Object) metódus a patchDoc JSON Patch-műveleteit alkalmazza a lekért Customer objektumra.
    • Ha a javításalkalmazás során hibák lépnek fel, például érvénytelen műveletek vagy ütközések, a hibakezelő meghatalmazott rögzíti őket. Ez a meghatalmazott hibaüzeneteket ad hozzá az ModelState érintett objektum típusnevének és a hibaüzenetnek a használatával.
  • ModelState ellenőrzése:
    • A javítás alkalmazása után a metódus ellenőrzi a ModelState hibákat.
    • Ha a ModelState érvénytelen, például javítási hibák miatt, akkor 400 Bad Request választ ad vissza az érvényesítési hibákkal.
  • Visszatéríti a frissített ügyfelet:
    • Ha a javítás sikeresen alkalmazva van, és az ModelState érvényes, a metódus visszaadja a frissített Customer objektumot a válaszban.

Példa hibaválasz:

Az alábbi példa egy JSON-javításművelet válaszának törzsét 400 Bad Request mutatja be, ha a megadott elérési út érvénytelen:

{
  "Customer": [
    "The target location specified by path segment 'foobar' was not found."
  ]
}

JSON Patch-dokumentum alkalmazása objektumra

Az alábbi példák bemutatják, hogyan alkalmazhat JSON Patch-dokumentumot egy objektumra a ApplyTo(Object) metódus használatával.

Példa: Alkalmazzon egy JsonPatchDocument<TModel> egy objektumra

Az alábbi példa a következőket mutatja be:

  • A add, replace és remove műveletek.
  • Műveletek beágyazott tulajdonságokon.
  • Új elem hozzáadása tömbhöz.
  • JSON-sztring Enum-konverter használata egy JSON módosító dokumentumban.
// 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));

Az előző példa a frissített objektum következő kimenetét eredményezi:

{
    "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"
        }
    ]
}

A ApplyTo(Object) módszer általában a System.Text.Json konvencióit és lehetőségeit követi az JsonPatchDocument<TModel> feldolgozásához, beleértve a következő beállítások által szabályozott viselkedést is:

Főbb különbségek a System.Text.Json és az új JsonPatchDocument<TModel> implementáció között:

  • A célobjektum futásidejű típusa, nem a deklarált típus határozza meg, hogy mely tulajdonságokra vonatkoznak a ApplyTo(Object) módosítások.
  • System.Text.Json A deszerializálás a deklarált típusra támaszkodik a jogosult tulajdonságok azonosításához.

Példa: JsonPatchDocument alkalmazása hibakezeléssel

A JSON-javítás dokumentumainak alkalmazásakor különböző hibák léphetnek fel. Előfordulhat például, hogy a célobjektum nem rendelkezik a megadott tulajdonsággal, vagy a megadott érték nem kompatibilis a tulajdonságtípussal.

A JSON Patch támogatja a test műveletet, amely ellenőrzi, hogy egy megadott érték megegyezik-e a céltulajdonságokkal. Ha nem, hibát ad vissza.

Az alábbi példa bemutatja, hogyan kezelheti ezeket a hibákat elegánsan.

Important

A metódusnak ApplyTo(Object) átadott objektum a helyén módosul. Ha egy művelet meghiúsul, a hívó felelős a módosítások elvetéséért.

// 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));

Az előző példa a következő kimenetet eredményezi:

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": []
}

A biztonsági kockázatok mérséklése

A csomag használatakor Microsoft.AspNetCore.JsonPatch.SystemTextJson kritikus fontosságú a lehetséges biztonsági kockázatok megértése és csökkentése. A következő szakaszok ismertetik a JSON-javításhoz kapcsolódó azonosított biztonsági kockázatokat, és ajánlott megoldásokat nyújtanak a csomag biztonságos használatának biztosítása érdekében.

Important

Ez nem a fenyegetések teljes listája. Az alkalmazásfejlesztőknek saját fenyegetésmodell-felülvizsgálatokat kell végezniük az alkalmazásspecifikus átfogó lista meghatározásához, és szükség szerint megfelelő megoldásokat kell készíteniük. Például azoknak az alkalmazásoknak, amelyek javításműveleteknek teszik ki a gyűjteményeket, figyelembe kell venniük az algoritmikus összetettségi támadások lehetőségét, ha ezek a műveletek elemeket szúrnak be vagy távolítanak el a gyűjtemény elején.

A JSON Patch funkcióinak alkalmazásba való integrálása során felmerülő biztonsági kockázatok minimalizálása érdekében a fejlesztőknek a következőkkel kell rendelkezniük:

  • Futtasson átfogó fenyegetésmodelleket saját alkalmazásaikhoz.
  • Elhárítja az azonosított fenyegetéseket.
  • Kövesse az alábbi szakaszokban javasolt intézkedéseket.

Szolgáltatásmegtagadás (DoS) memóriaerősítéssel

  • Forgatókönyv: Egy rosszindulatú ügyfél olyan műveletet küld, copy amely többször duplikálja a nagy objektumdiagramokat, ami túlzott memóriahasználathoz vezet.
  • Hatás: Lehetséges kimenőOf-Memory (OOM) feltételek, ami szolgáltatáskimaradásokat okoz.
  • Mitigation:
    • Ellenőrizze a bejövő JSON Patch-dokumentumokat a méret és a struktúra szempontjából, mielőtt hívja a ApplyTo(Object)-t.
    • Az érvényesítésnek alkalmazásspecifikusnak kell lennie, de egy példaérvényesítés a következőhöz hasonló lehet:
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();
    }
}

Üzleti logika megváltoztatása

  • Forgatókönyv: A javításműveletek implicit invariánsokkal (például belső jelzőkkel, azonosítókkal vagy számított mezőkkel) módosíthatják a mezőket, megsértve az üzleti korlátozásokat.
  • Hatás: Adatintegritási problémák és nem szándékos alkalmazás viselkedése.
  • Mitigation:
    • Használjon POCOS-objektumokat (egyszerű régi CLR-objektumokat) explicit módon definiált, biztonságosan módosítható tulajdonságokkal.
      • Kerülje a bizalmas vagy biztonsági szempontból kritikus tulajdonságok felfedését a célobjektumban.
      • Ha nem használ POCO-objektumot, ellenőrizze a javított objektumot a műveletek alkalmazása után annak érdekében, hogy az üzleti szabályok és az invariánsok ne sérüljenek.

Hitelesítés és engedélyezés

  • Forgatókönyv: A nem hitelesített vagy jogosulatlan ügyfelek rosszindulatú JSON-javításkéréseket küldenek.
  • Hatás: Jogosulatlan hozzáférés bizalmas adatok módosításához vagy az alkalmazás viselkedésének megzavarásához.
  • Mitigation:
    • A JSON Patch-kéréseket elfogadó végpontok védelme megfelelő hitelesítési és engedélyezési mechanizmusokkal.
    • Korlátozza a hozzáférést a megbízható ügyfelekhez vagy a megfelelő engedélyekkel rendelkező felhasználókhoz.

A kód lekérése

Mintakód megtekintése vagy letöltése. (A letöltés módja).

A minta teszteléséhez futtassa az alkalmazást, és küldjön HTTP-kéréseket a következő beállításokkal:

  • URL-cím: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • HTTP-metódus: PATCH
  • Fejléc: Content-Type: application/json-patch+json
  • Törzs: Másolja és illessze be a JSON-javítás dokumentummintáinak egyikét a JSON projektmappából.

További erőforrások

Ez a cikk bemutatja, hogyan kezelheti a JSON-javítás kéréseit egy ASP.NET Core webes API-ban.

Important

A JSON Patch szabvány belső biztonsági kockázatokkal jár. Ez az implementáció nem próbálja enyhíteni ezeket az eredendő biztonsági kockázatokat. A fejlesztő felelőssége annak biztosítása, hogy a JSON Patch-dokumentum biztonságosan alkalmazható legyen a célobjektumra. További információ: A biztonsági kockázatok mérséklése szakasz.

Csomag telepítése

Az ASP.NET Core webes API-ban a JSON Patch támogatás a Newtonsoft.Json alapul, és megköveteli a Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet-csomagot.

A JSON-javítás támogatásának engedélyezése:

  • Telepítse a Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet-csomagot.

  • Hívja fel: AddNewtonsoftJson. Például:

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

AddNewtonsoftJsonAz System.Text.Json JSON-tartalom formázásához használt alapértelmezett bemeneti és kimeneti formátumot cseréli le. Ez a bővítménymetódus a következő MVC-szolgáltatásregisztrációs módszerekkel kompatibilis:

A JsonPatch használatához be kell állítani a fejlécet Content-Type-ről application/json-patch+json-re.

JSON Patch támogatás hozzáadása a System.Text.Json használatakor

A System.Text.Json alapú bemeneti formázó nem támogatja a JSON-javításokat. A JSON-javítás Newtonsoft.Jsontámogatásának hozzáadása a többi bemeneti és kimeneti formátum változatlan maradása mellett:

  • Telepítse a Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet-csomagot.

  • Frissítés 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();
        }
    }
    

Az előző kód létrehoz egy példányt NewtonsoftJsonPatchInputFormatter, és beszúrja azt a MvcOptions.InputFormatters gyűjteménybe első bejegyzésként. Ez a regisztrációs sorrend biztosítja, hogy:

  • NewtonsoftJsonPatchInputFormatter JSON Patch-kérelmeket dolgoz fel.
  • A meglévő System.Text.Json-alapú bemenetek és formázók dolgozzák fel az összes többi JSON kérést és választ.

Newtonsoft.Json.JsonConvert.SerializeObject A metódussal szerializálhat egy JsonPatchDocument.

PATCH HTTP kérési módszer

A PUT és a PATCH metódusok egy meglévő erőforrás frissítésére szolgálnak. A különbség köztük az, hogy a PUT a teljes erőforrást lecseréli, míg a PATCH csak a módosításokat határozza meg.

JSON-javítás

A JSON-javítás az erőforrásra alkalmazni kívánt frissítések megadására szolgáló formátum. A JSON-javítások dokumentumai műveletek tömbjéből áll. Minden művelet azonosít egy adott típusú módosítást. Ilyen módosítások például tömbelem hozzáadása vagy tulajdonságérték cseréje.

A következő JSON-dokumentumok például egy erőforrást, egy JSON patch-dokumentumot jelölnek az erőforráshoz, valamint a Javításműveletek alkalmazásának eredményét.

Példa erőforrásra

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

Példa JSON-módosításra

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

A fenti JSON-ban:

  • A op tulajdonság a művelet típusát jelzi.
  • A path tulajdonság a frissíteni kívánt elemet jelzi.
  • A value tulajdonság az új értéket adja meg.

Erőforrás frissítés után

Az erőforrás az előző JSON Patch-dokumentum alkalmazása után:

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

A JSON Patch-dokumentum erőforrásra való alkalmazásával végrehajtott módosítások atomiak. Ha a listában található bármely művelet meghiúsul, a listában egyetlen művelet sem lesz alkalmazva.

Elérési út szintaxisa

A művelet objektum elérési út tulajdonsága perjeleket tartalmaz a szintek között. Például: "/address/zipCode".

A tömbelemek megadására nulla alapú indexek szolgálnak. A addresses tömb első eleme a /addresses/0 helyen található. A add tömb végéig használjon kötőjelet (-) indexszám helyett: /addresses/-.

Operations

Az alábbi táblázat a JSON-javítás specifikációjában meghatározott támogatott műveleteket mutatja be:

Operation Notes
add Adjon hozzá egy tulajdonságot vagy tömbelemet. Meglévő tulajdonság esetén: érték beállítása.
remove Távolítson el egy tulajdonságot vagy tömbelemet.
replace Ugyanaz, mint remove, amelyet ugyanazon a helyen add követ.
move Ugyanaz, mint remove a forrásból, majd add a célhelyre a forrásból származó érték használatával.
copy Ugyanaz, mint add a forrásból származó értéket használó célhely.
test Sikerességi állapotkódot ad vissza, ha az érték értéke = path meg van adva value.

JSON-javítás a ASP.NET Core-ban

A JSON Patch ASP.NET core implementációját a Microsoft.AspNetCore.JsonPatch NuGet csomag biztosítja.

Műveletmetódus kódja

Egy API-vezérlőben a JSON-javítás műveletmetódusa:

Íme egy példa:

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

A mintaalkalmazásból származó kód a következő Customer modellel működik:

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

A mintaműveleti módszer:

  • Létrehoz egy Customer-t.
  • Alkalmazza a javítást.
  • Az eredményt a válasz törzsében adja vissza.

Egy valós alkalmazásban a kód lekéri az adatokat egy tárolóból, például egy adatbázisból, és frissíti az adatbázist a javítás alkalmazása után.

Modell állapota

Az előző műveletmetódus-példa egy olyan ApplyTo túlterhelt verziót hív meg, amely a modellállapotot az egyik paramétereként fogadja. Ezzel a beállítással hibaüzeneteket kaphat a válaszokban. Az alábbi példa egy művelet 400 hibás kérésre adott test válaszának törzsét mutatja be:

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

Dinamikus objektumok

Az alábbi műveletmetódus-példa bemutatja, hogyan alkalmazhat javításokat dinamikus objektumokra:

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

    return Ok(obj);
}

A hozzáadási művelet

  • Ha path egy tömbelemre mutat: új elemet szúr be a megadott pathelem elé.
  • Ha path egy tulajdonságra mutat: beállítja a tulajdonság értékét.
  • Ha path nem létező helyre mutat:
    • Ha a javításhoz szükséges erőforrás egy dinamikus objektum: hozzáad egy tulajdonságot.
    • Ha a javításhoz szükséges erőforrás statikus objektum: a kérés meghiúsul.

Az alábbi mintajavító dokumentum beállítja CustomerName a tömb értékét, és hozzáad egy Order objektumot a Orders tömb végéhez.

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

Az eltávolítási művelet

  • Ha path egy tömbelemre mutat: eltávolítja az elemet.
  • Ha path egy tulajdonságra mutat:
    • Ha a javításhoz szükséges erőforrás dinamikus objektum: eltávolítja a tulajdonságot.
    • Ha a javításhoz szükséges erőforrás statikus objektum:
      • Ha a tulajdonság null értékű: null értékűre állítja.
      • Ha a tulajdonság nem null értékű, akkor a következőre default<T>állítja: .

A következő minta javítási dokumentum CustomerName értékét nullává állítja, és törli a Orders[0]-t.

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

A csereművelet

Ez a művelet funkcionálisan megegyezik egy remove , majd egy add-vel.

Az alábbi mintajavító dokumentum beállítja a(z) CustomerName értékét, és a(z) Orders[0] helyére egy új Order objektumot helyez.

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

Az áthelyezési művelet

  • Ha path egy tömbelemre mutat: from elemet másolja path elem helyére, majd végrehajt egy remove műveletet az from elemen.
  • Ha path egy tulajdonságra mutat: a tulajdonság értékét from átmásolja a tulajdonságba path , majd futtat egy remove műveletet a from tulajdonságon.
  • Ha path nem létező tulajdonságra mutat:
    • Ha a javításhoz szükséges erőforrás statikus objektum: a kérés meghiúsul.
    • Ha a javításhoz szükséges erőforrás egy dinamikus objektum: a tulajdonságot a megadott helyre másoljafrom, majd futtat egy path műveletet a remove tulajdonságon.from

A következő mintajavító dokumentum:

  • A(z) Orders[0].OrderName értékének másolása a CustomerName-re.
  • Null értékre van adva Orders[0].OrderName .
  • Áthelyez Orders[1] közvetlenül Orders[0] elé.
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

A másolási művelet

Ez a művelet funkcionálisan megegyezik az move utolsó remove lépés nélküli művelettel.

A következő mintajavító dokumentum:

  • A(z) Orders[0].OrderName értékének másolása a CustomerName-re.
  • Beszúr egy másolatot a Orders[1] elé Orders[0].
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

A tesztművelet

Ha a megadott path helyen lévő érték eltér a megadott valueértéktől, a kérés meghiúsul. Ebben az esetben a teljes PATCH-kérés akkor is meghiúsul, ha a javításdokumentum összes többi művelete egyébként sikeres lenne.

A test műveletet gyakran használják a frissítés megelőzésére egyidejűségi ütközés esetén.

A következő mintajavító dokumentumnak nincs hatása, ha a kezdeti érték CustomerName "John", mert a teszt sikertelen:

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

A kód lekérése

Mintakód megtekintése vagy letöltése. (A letöltés módja).

A minta teszteléséhez futtassa az alkalmazást, és küldjön HTTP-kéréseket a következő beállításokkal:

  • URL-cím: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • HTTP-metódus: PATCH
  • Fejléc: Content-Type: application/json-patch+json
  • Törzs: Másolja és illessze be a JSON-javítás dokumentummintáinak egyikét a JSON projektmappából.

A biztonsági kockázatok mérséklése

A(z) Microsoft.AspNetCore.JsonPatch csomag Newtonsoft.Json-alapú implementációval való használata esetén elengedhetetlen a lehetséges biztonsági kockázatok megértése és csökkentése. A következő szakaszok ismertetik a JSON-javításhoz kapcsolódó azonosított biztonsági kockázatokat, és ajánlott megoldásokat nyújtanak a csomag biztonságos használatának biztosítása érdekében.

Important

Ez nem a fenyegetések teljes listája. Az alkalmazásfejlesztőknek saját fenyegetésmodell-felülvizsgálatokat kell végezniük az alkalmazásspecifikus átfogó lista meghatározásához, és szükség szerint megfelelő megoldásokat kell készíteniük. Például azoknak az alkalmazásoknak, amelyek javításműveleteknek teszik ki a gyűjteményeket, figyelembe kell venniük az algoritmikus összetettségi támadások lehetőségét, ha ezek a műveletek elemeket szúrnak be vagy távolítanak el a gyűjtemény elején.

Ha átfogó fenyegetésmodelleket futtat saját alkalmazásaikhoz, és kezeli az azonosított fenyegetéseket, miközben az alábbi javasolt megoldásokat követi, ezeknek a csomagoknak a felhasználói a biztonsági kockázatok minimalizálása mellett integrálhatják a JSON Patch funkcióit az alkalmazásaikba.

Szolgáltatásmegtagadás (DoS) memóriaerősítéssel

  • Forgatókönyv: Egy rosszindulatú ügyfél olyan műveletet küld, copy amely többször duplikálja a nagy objektumdiagramokat, ami túlzott memóriahasználathoz vezet.
  • Hatás: Lehetséges kimenőOf-Memory (OOM) feltételek, ami szolgáltatáskimaradásokat okoz.
  • Mitigation:
    • Ellenőrizze a bejövő JSON Patch-dokumentumokat a méret és a struktúra szempontjából, mielőtt hívja a ApplyTo-t.
    • Az ellenőrzésnek alkalmazásspecifikusnak kell lennie, de egy példaérvényesítés a következőhöz hasonló lehet:
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();
    }
}

Üzleti logika megváltoztatása

  • Forgatókönyv: A javításműveletek implicit invariánsokkal (például belső jelzőkkel, azonosítókkal vagy számított mezőkkel) módosíthatják a mezőket, megsértve az üzleti korlátozásokat.
  • Hatás: Adatintegritási problémák és nem szándékos alkalmazás viselkedése.
  • Mitigation:
    • Olyan POCO-objektumokat használjon, amelyek kifejezetten meghatározott tulajdonságokkal rendelkeznek, amelyek biztonságosan módosíthatók.
    • Kerülje a bizalmas vagy biztonsági szempontból kritikus tulajdonságok felfedését a célobjektumban.
    • Ha nem használ POCO-objektumot, ellenőrizze a javított objektumot a műveletek alkalmazása után annak biztosítása érdekében, hogy az üzleti szabályok és a változók ne sérüljenek.

Hitelesítés és engedélyezés

  • Forgatókönyv: A nem hitelesített vagy jogosulatlan ügyfelek rosszindulatú JSON-javításkéréseket küldenek.
  • Hatás: Jogosulatlan hozzáférés bizalmas adatok módosításához vagy az alkalmazás viselkedésének megzavarásához.
  • Mitigation:
    • A JSON Patch-kéréseket elfogadó végpontok védelme megfelelő hitelesítési és engedélyezési mechanizmusokkal.
    • Korlátozza a hozzáférést a megbízható ügyfelekhez vagy a megfelelő engedélyekkel rendelkező felhasználókhoz.

További erőforrások

Ez a cikk bemutatja, hogyan kezelheti a JSON-javítás kéréseit egy ASP.NET Core webes API-ban.

Important

A JSON Patch szabvány belső biztonsági kockázatokkal jár. Mivel ezek a kockázatok a JSON Patch szabványhoz tartoznak, ez a megvalósítás nem próbálja csökkenteni az eredendő biztonsági kockázatokat. A fejlesztő felelőssége annak biztosítása, hogy a JSON Patch-dokumentum biztonságosan alkalmazható legyen a célobjektumra. További információ: A biztonsági kockázatok mérséklése szakasz.

Csomag telepítése

Ha engedélyezni szeretné a JSON-javítások támogatását az alkalmazásban, hajtsa végre az alábbi lépéseket:

  1. Telepítse a Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet-csomagot.

  2. Frissítse a(z) Startup.ConfigureServices metódust a projektben, hogy meghívja AddNewtonsoftJson. Például:

    services
        .AddControllersWithViews()
        .AddNewtonsoftJson();
    

AddNewtonsoftJson kompatibilis az MVC szolgáltatásregisztrációs módszereivel:

JSON-javítás, AddNewtonsoftJson és System.Text.Json

AddNewtonsoftJson lecseréli az System.Text.Json alapú bemeneti és kimeneti formázókat, amelyeket az összes JSON-tartalom formázásához használnak. A JSON-javítás támogatásának hozzáadásához a többi formázó változatlanul hagyásával Newtonsoft.Jsonfrissítse a projekt metódusát Startup.ConfigureServices az alábbiak szerint:

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

Az előző kódhoz a Microsoft.AspNetCore.Mvc.NewtonsoftJson csomagra és a következő using utasításokra van szükség:

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;

A JsonPatchDocument szerializálásához használja a Newtonsoft.Json.JsonConvert.SerializeObject metódust.

PATCH HTTP kérési módszer

A PUT és a PATCH metódusok egy meglévő erőforrás frissítésére szolgálnak. A különbség köztük az, hogy a PUT a teljes erőforrást lecseréli, míg a PATCH csak a módosításokat határozza meg.

JSON-javítás

A JSON-javítás az erőforrásra alkalmazni kívánt frissítések megadására szolgáló formátum. A JSON-javítások dokumentumai műveletek tömbjéből áll. Minden művelet azonosít egy adott típusú módosítást. Ilyen módosítások például tömbelem hozzáadása vagy tulajdonságérték cseréje.

A következő JSON-dokumentumok például egy erőforrást, egy JSON patch-dokumentumot jelölnek az erőforráshoz, valamint a Javításműveletek alkalmazásának eredményét.

Példa erőforrásra

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

Példa JSON-módosításra

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

A fenti JSON-ban:

  • A op tulajdonság a művelet típusát jelzi.
  • A path tulajdonság a frissíteni kívánt elemet jelzi.
  • A value tulajdonság az új értéket adja meg.

Erőforrás frissítés után

Az erőforrás az előző JSON Patch-dokumentum alkalmazása után:

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

A JSON Patch-dokumentum erőforrásra való alkalmazásával végrehajtott módosítások atomiak. Ha a listában található bármely művelet meghiúsul, a listában egyetlen művelet sem lesz alkalmazva.

Elérési út szintaxisa

A művelet objektum elérési út tulajdonsága perjeleket tartalmaz a szintek között. Például: "/address/zipCode".

A tömbelemek megadására nulla alapú indexek szolgálnak. A addresses tömb első eleme a /addresses/0 helyen található. A add tömb végéig használjon kötőjelet (-) indexszám helyett: /addresses/-.

Operations

Az alábbi táblázat a JSON-javítás specifikációjában meghatározott támogatott műveleteket mutatja be:

Operation Notes
add Adjon hozzá egy tulajdonságot vagy tömbelemet. Meglévő tulajdonság esetén: érték beállítása.
remove Távolítson el egy tulajdonságot vagy tömbelemet.
replace Ugyanaz, mint remove, amelyet ugyanazon a helyen add követ.
move Ugyanaz, mint remove a forrásból, majd add a célhelyre a forrásból származó érték használatával.
copy Ugyanaz, mint add a forrásból származó értéket használó célhely.
test Sikerességi állapotkódot ad vissza, ha az érték értéke = path meg van adva value.

JSON-javítás a ASP.NET Core-ban

A JSON Patch ASP.NET core implementációját a Microsoft.AspNetCore.JsonPatch NuGet csomag biztosítja.

Műveletmetódus kódja

Egy API-vezérlőben a JSON-javítás műveletmetódusa:

  • A(z) HttpPatch attribútummal van megjelölve.
  • Elfogad egy JsonPatchDocument<T>, általában a [FromBody].
  • A módosítások alkalmazásához ApplyTo hívja meg a módosítási dokumentumot.

Íme egy példa:

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

A mintaalkalmazásból származó kód a következő Customer modellel működik:

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

A mintaműveleti módszer:

  • Létrehoz egy Customer-t.
  • Alkalmazza a javítást.
  • Az eredményt a válasz törzsében adja vissza.

Egy valós alkalmazásban a kód lekéri az adatokat egy tárolóból, például egy adatbázisból, és frissíti az adatbázist a javítás alkalmazása után.

Modell állapota

Az előző műveletmetódus-példa egy olyan ApplyTo túlterhelt verziót hív meg, amely a modellállapotot az egyik paramétereként fogadja. Ezzel a beállítással hibaüzeneteket kaphat a válaszokban. Az alábbi példa egy művelet 400 hibás kérésre adott test válaszának törzsét mutatja be:

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

Dinamikus objektumok

Az alábbi műveletmetódus-példa bemutatja, hogyan alkalmazhat javításokat dinamikus objektumokra:

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

    return Ok(obj);
}

A hozzáadási művelet

  • Ha path egy tömbelemre mutat: új elemet szúr be a megadott pathelem elé.
  • Ha path egy tulajdonságra mutat: beállítja a tulajdonság értékét.
  • Ha path nem létező helyre mutat:
    • Ha a javításhoz szükséges erőforrás egy dinamikus objektum: hozzáad egy tulajdonságot.
    • Ha a javításhoz szükséges erőforrás statikus objektum: a kérés meghiúsul.

Az alábbi mintajavító dokumentum beállítja CustomerName a tömb értékét, és hozzáad egy Order objektumot a Orders tömb végéhez.

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

Az eltávolítási művelet

  • Ha path egy tömbelemre mutat: eltávolítja az elemet.
  • Ha path egy tulajdonságra mutat:
    • Ha a javításhoz szükséges erőforrás dinamikus objektum: eltávolítja a tulajdonságot.
    • Ha a javításhoz szükséges erőforrás statikus objektum:
      • Ha a tulajdonság null értékű: null értékűre állítja.
      • Ha a tulajdonság nem null értékű, akkor a következőre default<T>állítja: .

A következő minta javítási dokumentum CustomerName értékét nullává állítja, és törli a Orders[0]-t.

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

A csereművelet

Ez a művelet funkcionálisan megegyezik egy remove , majd egy add-vel.

Az alábbi mintajavító dokumentum beállítja a(z) CustomerName értékét, és a(z) Orders[0] helyére egy új Order objektumot helyez.

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

Az áthelyezési művelet

  • Ha path egy tömbelemre mutat: from elemet másolja path elem helyére, majd végrehajt egy remove műveletet az from elemen.
  • Ha path egy tulajdonságra mutat: a tulajdonság értékét from átmásolja a tulajdonságba path , majd futtat egy remove műveletet a from tulajdonságon.
  • Ha path nem létező tulajdonságra mutat:
    • Ha a javításhoz szükséges erőforrás statikus objektum: a kérés meghiúsul.
    • Ha a javításhoz szükséges erőforrás egy dinamikus objektum: a tulajdonságot a megadott helyre másoljafrom, majd futtat egy path műveletet a remove tulajdonságon.from

A következő mintajavító dokumentum:

  • A(z) Orders[0].OrderName értékének másolása a CustomerName-re.
  • Null értékre van adva Orders[0].OrderName .
  • Áthelyez Orders[1] közvetlenül Orders[0] elé.
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

A másolási művelet

Ez a művelet funkcionálisan megegyezik az move utolsó remove lépés nélküli művelettel.

A következő mintajavító dokumentum:

  • A(z) Orders[0].OrderName értékének másolása a CustomerName-re.
  • Beszúr egy másolatot a Orders[1] elé Orders[0].
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

A tesztművelet

Ha a megadott path helyen lévő érték eltér a megadott valueértéktől, a kérés meghiúsul. Ebben az esetben a teljes PATCH-kérés akkor is meghiúsul, ha a javításdokumentum összes többi művelete egyébként sikeres lenne.

A test műveletet gyakran használják a frissítés megelőzésére egyidejűségi ütközés esetén.

A következő mintajavító dokumentumnak nincs hatása, ha a kezdeti érték CustomerName "John", mert a teszt sikertelen:

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

A kód lekérése

Mintakód megtekintése vagy letöltése. (A letöltés módja).

A minta teszteléséhez futtassa az alkalmazást, és küldjön HTTP-kéréseket a következő beállításokkal:

  • URL-cím: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • HTTP-metódus: PATCH
  • Fejléc: Content-Type: application/json-patch+json
  • Törzs: Másolja és illessze be a JSON-javítás dokumentummintáinak egyikét a JSON projektmappából.