Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit artikel wordt uitgelegd hoe u JSON Patch-aanvragen kunt verwerken in een ASP.NET Core-web-API.
Ondersteuning voor JSON Patch in ASP.NET Core-web-API is gebaseerd op System.Text.Json serialisatie en vereist het Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet-pakket.
Wat is de JSON Patch-standaard?
De JSON Patch-standaard:
Is een standaardindeling voor het beschrijven van wijzigingen die moeten worden toegepast op een JSON-document.
Wordt gedefinieerd in RFC 6902 en wordt veel gebruikt in RESTful-API's om gedeeltelijke updates uit te voeren voor JSON-resources.
Beschrijft een reeks bewerkingen die een JSON-document wijzigen, zoals:
addremovereplacemovecopytest
In web-apps wordt JSON Patch vaak gebruikt in een PATCH-bewerking om gedeeltelijke updates van een resource uit te voeren. In plaats van de hele resource voor een update te verzenden, kunnen clients een JSON Patch-document met alleen de wijzigingen verzenden. Patching vermindert de payload en verbetert de efficiëntie.
Zie jsonpatch.com voor een overzicht van de JSON Patch-standaard.
Ondersteuning voor JSON Patch in ASP.NET Core-web-API
Ondersteuning voor JSON Patch in de ASP.NET Core web-API is gebaseerd op System.Text.Json-serialisatie, te beginnen met .NET 10 en geïmplementeerd op basis van Microsoft.AspNetCore.JsonPatch met behulp van System.Text.Json-serialisatie. Deze functie:
- Vereist het 
Microsoft.AspNetCore.JsonPatch.SystemTextJsonNuGet-pakket. - Komt overeen met moderne .NET-procedures door gebruik te maken van de System.Text.Json bibliotheek, die is geoptimaliseerd voor .NET.
 - Biedt verbeterde prestaties en minder geheugengebruik in vergelijking met de verouderde implementatie op basis van een oudere 
Newtonsoft.Jsonversie. Zie deNewtonsoft.Jsonvoor meer informatie over de verouderde implementatie op basis van .NET. 
Note
De implementatie, gebaseerd op Microsoft.AspNetCore.JsonPatchSystem.Text.Json serialisatie, is geen directe vervanging voor de verouderde Newtonsoft.Json-gebaseerde implementatie. Het biedt bijvoorbeeld ExpandoObjectgeen ondersteuning voor dynamische typen.
Important
De JSON Patch-standaard heeft inherente beveiligingsrisico's. Omdat deze risico's inherent zijn aan de JSON Patch-standaard, probeert de ASP.NET Core-implementatie geen inherente beveiligingsrisico's te beperken. Het is de verantwoordelijkheid van de ontwikkelaar om ervoor te zorgen dat het JSON Patch-document veilig kan worden toegepast op het doelobject. Zie de sectie Beveiligingsrisico's beperken voor meer informatie.
Ondersteuning voor JSON Patch inschakelen met System.Text.Json
Als u ondersteuning voor JSON Patch wilt inschakelen, System.Text.Jsoninstalleert u het Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet-pakket.
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
Dit pakket biedt een JsonPatchDocument<TModel> klasse die een JSON Patch-document vertegenwoordigt voor objecten van het type T en aangepaste logica voor het serialiseren en deserialiseren van JSON Patch-documenten met behulp van System.Text.Json. De belangrijkste methode van de JsonPatchDocument<TModel> klasse is ApplyTo(Object), waarmee de patchbewerkingen worden toegepast op een doelobject van het type T.
Actiemethodecode voor het toepassen van JSON Patch
In een API-controller, een actiemethode voor JSON Patch:
- Wordt geannoteerd met het kenmerk HttpPatchAttribute.
 - Accepteert een JsonPatchDocument<TModel>, meestal met FromBodyAttribute.
 - Roept ApplyTo(Object) aan in het patchdocument om de wijzigingen toe te passen.
 
Voorbeeld van controlleractiemethode:
[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);
}
Deze code van de voorbeeld-app werkt met de volgende Customer en Order modellen:
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();
    }
}
De belangrijkste stappen van de voorbeeldactiemethode:
- 
              De klant ophalen: 
- Met de methode wordt een 
Customerobject opgehaald uit de databaseAppDbmet behulp van de opgegeven id. - Als er geen 
Customerobject wordt gevonden, wordt er een404 Not Foundantwoord geretourneerd. 
 - Met de methode wordt een 
 - 
              JSON-patch toepassen: 
- De ApplyTo(Object) methode past de JSON Patch-bewerkingen van de patchDoc toe op het opgehaalde 
Customerobject. - Als er fouten optreden tijdens de patchtoepassing, zoals ongeldige bewerkingen of conflicten, worden deze vastgelegd door een gemachtigde voor foutafhandeling. Deze delegate voegt foutberichten toe aan het 
ModelStatemet behulp van de typenaam van het betrokken object en het foutbericht. 
 - De ApplyTo(Object) methode past de JSON Patch-bewerkingen van de patchDoc toe op het opgehaalde 
 - 
              ModelState valideren: 
- Na het toepassen van de patch controleert de methode de 
ModelStateop fouten. - Als de 
ModelStatefout ongeldig is, bijvoorbeeld vanwege patchfouten, wordt er een400 Bad Requestantwoord geretourneerd met de validatiefouten. 
 - Na het toepassen van de patch controleert de methode de 
 - 
              Retourneer de bijgewerkte klant: 
- Als de patch is toegepast en de 
ModelStatepatch geldig is, retourneert de methode het bijgewerkteCustomerobject in het antwoord. 
 - Als de patch is toegepast en de 
 
Voorbeeld van een foutbericht:
In het volgende voorbeeld ziet u de hoofdtekst van een 400 Bad Request antwoord voor een JSON Patch-bewerking wanneer het opgegeven pad ongeldig is:
{
  "Customer": [
    "The target location specified by path segment 'foobar' was not found."
  ]
}
Een JSON Patch-document toepassen op een object
In de volgende voorbeelden ziet u hoe u de ApplyTo(Object) methode gebruikt om een JSON Patch-document toe te passen op een object.
Voorbeeld: Een JsonPatchDocument<TModel> toepassen op een object
In het volgende voorbeeld ziet u:
- De 
add,replaceenremovebewerkingen. - Bewerkingen op geneste eigenschappen.
 - Een nieuw item toevoegen aan een matrix.
 - Een JSON String Enum Converter gebruiken in een JSON-patchdocument.
 
// 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));
Het vorige voorbeeld resulteert in de volgende uitvoer van het bijgewerkte object:
{
    "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"
        }
    ]
}
De ApplyTo(Object) methode volgt over het algemeen de conventies en opties voor het verwerken van System.Text.Json de JsonPatchDocument<TModel>, inclusief het gedrag dat wordt beheerd door de volgende opties:
- JsonNumberHandling: Of numerieke eigenschappen worden gelezen uit tekenreeksen.
 - PropertyNameCaseInsensitive: Of eigenschapsnamen al dan niet hoofdlettergevoelig zijn.
 
Belangrijke verschillen tussen System.Text.Json en de nieuwe JsonPatchDocument<TModel> implementatie:
- Het looptijdtype van het doelobject, niet het gedeclareerde type, bepaalt welke eigenschappen door ApplyTo(Object) worden aangepast.
 - System.Text.Json deserialisatie is afhankelijk van het gedeclareerde type om in aanmerking komende eigenschappen te identificeren.
 
Voorbeeld: Een JsonPatchDocument toepassen met foutafhandeling
Er zijn verschillende fouten die kunnen optreden bij het toepassen van een JSON Patch-document. Het doelobject heeft bijvoorbeeld mogelijk niet de opgegeven eigenschap of de opgegeven waarde is mogelijk niet compatibel met het eigenschapstype.
JSON Patch ondersteunt de test bewerking, waarmee wordt gecontroleerd of een opgegeven waarde gelijk is aan de doeleigenschap. Als dat niet gebeurt, wordt er een fout geretourneerd.
In het volgende voorbeeld ziet u hoe u deze fouten probleemloos kunt afhandelen.
Important
Het object dat aan de ApplyTo(Object) methode wordt doorgegeven, wordt ter plaatse gewijzigd. De beller is verantwoordelijk voor het negeren van wijzigingen als een bewerking mislukt.
// 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));
Het vorige voorbeeld resulteert in de volgende uitvoer:
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": []
}
Beveiligingsrisico's beperken
Wanneer u het Microsoft.AspNetCore.JsonPatch.SystemTextJson pakket gebruikt, is het essentieel om mogelijke beveiligingsrisico's te begrijpen en te beperken. In de volgende secties worden de geïdentificeerde beveiligingsrisico's beschreven die zijn gekoppeld aan JSON Patch en worden aanbevolen oplossingen geboden om veilig gebruik van het pakket te garanderen.
Important
Dit is geen volledige lijst met bedreigingen. App-ontwikkelaars moeten hun eigen beoordelingen voor bedreigingsmodellen uitvoeren om een app-specifieke uitgebreide lijst te bepalen en zo nodig passende oplossingen te bedenken. Apps die verzamelingen beschikbaar maken voor patchbewerkingen, moeten bijvoorbeeld rekening houden met het potentieel voor algoritmen complexe aanvallen als deze bewerkingen elementen invoegen of verwijderen aan het begin van de verzameling.
Ontwikkelaars moeten het volgende doen om beveiligingsrisico's te minimaliseren bij het integreren van de JSON Patch-functionaliteit in hun apps:
- Voer uitgebreide bedreigingsmodellen uit voor hun eigen apps.
 - Identificeer geïdentificeerde bedreigingen.
 - Volg de aanbevolen maatregelen in de volgende gedeelten.
 
Denial of Service (DoS) via geheugenversterking
- 
              Scenario: Een kwaadwillende client verzendt een 
copybewerking waarmee grote objectgrafieken meerdere keren worden gedupliceerd, wat leidt tot overmatig geheugenverbruik. - Impact: Potentiële out-Of-Memory (OOM)-omstandigheden, waardoor serviceonderbrekingen ontstaan.
 - 
              Mitigation: 
- Valideer binnenkomende JSON Patch-documenten voor grootte en structuur voordat u aanroept ApplyTo(Object).
 - De validatie moet specifiek zijn voor de app, maar een voorbeeldvalidatie kan er ongeveer als volgt uitzien:
 
 
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();
    }
}
Subversie van bedrijfslogica
- Scenario: Patchbewerkingen kunnen velden bewerken met impliciete invarianten (bijvoorbeeld interne vlaggen, id's of berekende velden), die zakelijke beperkingen schenden.
 - Impact: Problemen met gegevensintegriteit en onbedoeld app-gedrag.
 - 
              Mitigation: 
- Gebruik POCOs (gewone oude CLR-objecten) met expliciet gedefinieerde eigenschappen die veilig kunnen worden gewijzigd.
- Vermijd het blootstellen van gevoelige of beveiligingskritieke eigenschappen in het doelobject.
 - Als een POCO-object niet wordt gebruikt, valideert u het gepatchte object na het toepassen van bewerkingen om ervoor te zorgen dat bedrijfsregels en invarianten niet worden geschonden.
 
 
 - Gebruik POCOs (gewone oude CLR-objecten) met expliciet gedefinieerde eigenschappen die veilig kunnen worden gewijzigd.
 
Authenticatie en autorisatie
- Scenario: Niet-geverifieerde of niet-geautoriseerde clients verzenden schadelijke JSON Patch-aanvragen.
 - Impact: onbevoegde toegang om gevoelige gegevens te wijzigen of het gedrag van apps te verstoren.
 - 
              Mitigation: 
- Beveilig eindpunten die JSON Patch-aanvragen accepteren met de juiste verificatie- en autorisatiemechanismen.
 - Beperk de toegang tot vertrouwde clients of gebruikers met de juiste machtigingen.
 
 
De code ophalen
voorbeeldcode weergeven of downloaden. (Hoe te downloaden).
Als u het voorbeeld wilt testen, voert u de app uit en verzendt u HTTP-aanvragen met de volgende instellingen:
- URL: 
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate - HTTP-methode: 
PATCH - Koptekst: 
Content-Type: application/json-patch+json - Hoofdtekst: Kopieer en plak een van de voorbeelden van JSON-patchdocumenten uit de JSON- projectmap.
 
Aanvullende bronnen
In dit artikel wordt uitgelegd hoe u JSON Patch-aanvragen kunt verwerken in een ASP.NET Core-web-API.
Important
De JSON Patch-standaard heeft inherente beveiligingsrisico's. Deze implementatie probeert deze inherente beveiligingsrisico's niet te beperken. Het is de verantwoordelijkheid van de ontwikkelaar om ervoor te zorgen dat het JSON Patch-document veilig kan worden toegepast op het doelobject. Zie de sectie Beveiligingsrisico's beperken voor meer informatie.
Pakketinstallatie
Ondersteuning voor JSON Patch in ASP.NET Core-web-API is gebaseerd op Newtonsoft.Json en vereist het Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet-pakket.
JSON Patch-ondersteuning inschakelen:
Installeer het
Microsoft.AspNetCore.Mvc.NewtonsoftJsonNuGet-pakket.Bel AddNewtonsoftJson. Voorbeeld:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddNewtonsoftJson(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
              AddNewtonsoftJson vervangt de standaard op System.Text.Jsongebaseerde invoer- en uitvoerindelingen die worden gebruikt voor het opmaken van alle JSON-inhoud. Deze extensiemethode is compatibel met de volgende MVC-serviceregistratiemethoden:
JsonPatch vereist het instellen van de Content-Type-header op application/json-patch+json.
Ondersteuning voor JSON Patch toevoegen bij het gebruik van System.Text.Json
De System.Text.Json-based input formatter biedt geen ondersteuning voor JSON Patch. Ondersteuning voor JSON Patch toevoegen met behulp van Newtonsoft.Json, terwijl de andere invoer- en uitvoerindelingen ongewijzigd blijven:
Installeer het
Microsoft.AspNetCore.Mvc.NewtonsoftJsonNuGet-pakket.Program.csbijwerken: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(); } }
Met de voorgaande code wordt een exemplaar van NewtonsoftJsonPatchInputFormatter gemaakt en als het eerste element in de MvcOptions.InputFormatters-collectie ingevoegd. Deze registratievolgorde zorgt ervoor dat:
- 
              
NewtonsoftJsonPatchInputFormatterverwerkt JSON Patch-aanvragen. - De bestaande op 
System.Text.Jsongebaseerde input en formatters verwerken alle andere JSON-aanvragen en -antwoorden. 
Gebruik de methode Newtonsoft.Json.JsonConvert.SerializeObject om een JsonPatchDocumentte serialiseren.
HTTP-aanvraagmethode PATCH
De methoden PUT en PATCH worden gebruikt om een bestaande resource bij te werken. Het verschil is dat PUT de hele resource vervangt, terwijl PATCH alleen de wijzigingen opgeeft.
JSON-patch
JSON Patch is een indeling voor het opgeven van updates die moeten worden toegepast op een resource. Een JSON Patch-document bevat een matrix met bewerkingen. Elke bewerking identificeert een bepaald type wijziging. Voorbeelden van dergelijke wijzigingen zijn het toevoegen van een matrixelement of het vervangen van een eigenschapswaarde.
De volgende JSON-documenten vertegenwoordigen bijvoorbeeld een resource, een JSON Patch-document voor de resource en het resultaat van het toepassen van de patchbewerkingen.
Resourcevoorbeeld
{
  "customerName": "John",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    }
  ]
}
Voorbeeld van JSON-patch
[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]
In de voorgaande JSON:
- De eigenschap 
opgeeft het type bewerking aan. - De eigenschap 
pathgeeft het element aan dat moet worden bijgewerkt. - De eigenschap 
valuebiedt de nieuwe waarde. 
Resource na de patch
Dit is de resource na het toepassen van het voorgaande JSON Patch-document:
{
  "customerName": "Barry",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    },
    {
      "orderName": "Order2",
      "orderType": null
    }
  ]
}
De wijzigingen die zijn aangebracht door een JSON Patch-document toe te passen op een resource, zijn atomisch. Als een bewerking in de lijst mislukt, wordt er geen bewerking in de lijst toegepast.
Padsyntaxis
De eigenschap van het "/address/zipCode".
Indexen op basis van nul worden gebruikt om matrixelementen op te geven. Het eerste element van de addresses matrix bevindt zich op /addresses/0. Als u aan het einde van een matrix wilt add, gebruikt u een afbreekstreepje (-) in plaats van een indexnummer: /addresses/-.
Operations
In de volgende tabel ziet u ondersteunde bewerkingen zoals gedefinieerd in de JSON Patch-specificatie:
| Operation | Notes | 
|---|---|
add | 
Voeg een eigenschap of matrixelement toe. Voor bestaande eigenschap: waarde instellen. | 
remove | 
Een eigenschap of matrixelement verwijderen. | 
replace | 
Hetzelfde als remove gevolgd door add op dezelfde locatie. | 
move | 
Hetzelfde als remove van de bron, gevolgd door add naar de bestemming met behulp van de waarde uit de bron. | 
copy | 
Hetzelfde als add naar bestemming met behulp van waarde uit de bron. | 
test | 
Retourneer de success-statuscode als de waarde op path = opgegeven value. | 
JSON Patch in ASP.NET Core
De ASP.NET Core-implementatie van JSON Patch is beschikbaar in het Microsoft.AspNetCore.JsonPatch NuGet-pakket.
Code van actiemethode
In een API-controller, een actiemethode voor JSON Patch:
- Wordt geannoteerd met het kenmerk 
HttpPatch. - Accepteert een JsonPatchDocument<TModel>, meestal met 
[FromBody]. - Roept ApplyTo(Object) aan in het patchdocument om de wijzigingen toe te passen.
 
Hier volgt een voorbeeld:
[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);
    }
}
Deze code van de voorbeeld-app werkt met het volgende Customer model:
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; }
}
De voorbeeldactiemethode:
- Maakt een 
Customer. - De patch wordt toegepast.
 - Retourneert het resultaat in de hoofdtekst van het antwoord.
 
In een echte app haalt de code de gegevens op uit een archief, zoals een database, en werkt de database bij nadat de patch is toegepast.
Modelstatus
In het voorgaande voorbeeld van de actiemethode wordt een overbelasting van ApplyTo aangeroepen die de modelstatus als een van de parameters gebruikt. Met deze optie kunt u foutberichten in antwoorden krijgen. In het volgende voorbeeld ziet u de body van een 400 Ongeldige Aanvraagreactie bij een test-bewerking:
{
  "Customer": [
    "The current value 'John' at path 'customerName' != test value 'Nancy'."
  ]
}
Dynamische objecten
In het volgende voorbeeld van een actiemethode ziet u hoe u een patch toepast op een dynamisch object:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
    dynamic obj = new ExpandoObject();
    patch.ApplyTo(obj);
    return Ok(obj);
}
De toevoegbewerking
- Als 
pathverwijst naar een matrixelement: voegt nieuw element in vóór het element dat is opgegeven doorpath. - Als 
pathverwijst naar een eigenschap: stelt de eigenschapswaarde in. - Als 
pathverwijst naar een niet-bestaande locatie:- Als de resource die moet worden gepatcht een dynamisch object is: voegt een eigenschap toe.
 - Als de resource om te patchen een statisch object is: de aanvraag mislukt.
 
 
In het volgende voorbeeldpatchdocument wordt de waarde van CustomerName ingesteld en wordt een Order-object toegevoegd aan het einde van de Orders-matrix.
[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]
De verwijderbewerking
- Als 
pathverwijst naar een matrixelement: verwijdert het element. - Als 
pathverwijst naar een eigenschap:- Als de resource om te patchen een dynamisch object is, wordt de eigenschap verwijderd.
 - Als de resource om te patchen een statisch object is: 
- Als de eigenschap nullable is: stelt deze in op null.
 - Als de eigenschap niet nullbaar is, stelt u deze in op 
default<T>. 
 
 
De volgende voorbeeldpatch stelt document CustomerName in op null en verwijdert Orders[0]:
[
  {
    "op": "remove",
    "path": "/customerName"
  },
  {
    "op": "remove",
    "path": "/orders/0"
  }
]
De vervangingsbewerking
Deze bewerking is functioneel hetzelfde als een remove gevolgd door een add.
In het volgende voorbeeldpatchdocument wordt de waarde van CustomerName ingesteld en wordt Orders[0]vervangen door een nieuw Order-object:
[
  {
    "op": "replace",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "replace",
    "path": "/orders/0",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]
De verplaatsingsbewerking
- Als 
pathverwijst naar een matrixelement: kopieertfromelement naar de locatie vanpathelement en voert vervolgens eenremovebewerking uit op hetfromelement. - Als 
pathverwijst naar een eigenschap: kopieert de waarde vanfromeigenschap naarpatheigenschap en voert vervolgens eenremovebewerking uit op de eigenschapfrom. - Als 
pathverwijst naar een niet-bestaande eigenschap:- Als de resource om te patchen een statisch object is: de aanvraag mislukt.
 - Als de resource die moet worden gepatcht een dynamisch object is: kopieert 
fromeigenschap naar de locatie aangegeven doorpathen voert vervolgens eenremovebewerking uit op de eigenschapfrom. 
 
Het volgende voorbeeldpatchdocument:
- Kopieert de waarde van 
Orders[0].OrderNamenaarCustomerName. - Hiermee stelt u 
Orders[0].OrderNamein op null. - Hiermee verplaatst u 
Orders[1]naar vóórOrders[0]. 
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]
De kopieerbewerking
Deze bewerking is functioneel hetzelfde als een move bewerking zonder de laatste remove stap.
Het volgende voorbeeldpatchdocument:
- Kopieert de waarde van 
Orders[0].OrderNamenaarCustomerName. - Voegt een kopie van 
Orders[1]in vóórOrders[0]. 
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]
De testbewerking
Als de waarde op de locatie die wordt aangegeven door path verschilt van de waarde in value, mislukt de aanvraag. In dat geval mislukt de hele PATCH-aanvraag, zelfs als alle andere bewerkingen in het patchdocument anders zouden slagen.
De test bewerking wordt vaak gebruikt om een update te voorkomen wanneer er een gelijktijdigheidsconflict is.
Het volgende voorbeeldpatchdocument heeft geen effect als de initiële waarde van CustomerName 'John' is, omdat de test mislukt:
[
  {
    "op": "test",
    "path": "/customerName",
    "value": "Nancy"
  },
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  }
]
De code ophalen
voorbeeldcode weergeven of downloaden. (Hoe te downloaden).
Als u het voorbeeld wilt testen, voert u de app uit en verzendt u HTTP-aanvragen met de volgende instellingen:
- URL: 
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate - HTTP-methode: 
PATCH - Koptekst: 
Content-Type: application/json-patch+json - Hoofdtekst: Kopieer en plak een van de voorbeelden van JSON-patchdocumenten uit de JSON- projectmap.
 
Beveiligingsrisico's beperken
Wanneer u het Microsoft.AspNetCore.JsonPatch-pakket gebruikt met de op Newtonsoft.Json gebaseerde implementatie, is het essentieel om potentiële beveiligingsrisico's te begrijpen en te beperken. In de volgende secties worden de geïdentificeerde beveiligingsrisico's beschreven die zijn gekoppeld aan JSON Patch en worden aanbevolen oplossingen geboden om veilig gebruik van het pakket te garanderen.
Important
Dit is geen volledige lijst met bedreigingen. App-ontwikkelaars moeten hun eigen beoordelingen voor bedreigingsmodellen uitvoeren om een app-specifieke uitgebreide lijst te bepalen en zo nodig passende oplossingen te bedenken. Apps die verzamelingen beschikbaar maken voor patchbewerkingen, moeten bijvoorbeeld rekening houden met het potentieel voor algoritmen complexe aanvallen als deze bewerkingen elementen invoegen of verwijderen aan het begin van de verzameling.
Door uitgebreide bedreigingsmodellen voor hun eigen apps uit te voeren en geïdentificeerde bedreigingen aan te pakken terwijl ze de aanbevolen oplossingen hieronder volgen, kunnen consumenten van deze pakketten JSON Patch-functionaliteit integreren in hun apps en tegelijkertijd beveiligingsrisico's minimaliseren.
Denial of Service (DoS) via geheugenversterking
- 
              Scenario: Een kwaadwillende client verzendt een 
copybewerking waarmee grote objectgrafieken meerdere keren worden gedupliceerd, wat leidt tot overmatig geheugenverbruik. - Impact: Potentiële out-Of-Memory (OOM)-omstandigheden, waardoor serviceonderbrekingen ontstaan.
 - 
              Mitigation: 
- Valideer binnenkomende JSON Patch-documenten voor grootte en structuur voordat u aanroept 
ApplyTo. - De validatie moet specifiek zijn voor de app, maar een voorbeeldvalidatie kan er ongeveer als volgt uitzien:
 
 - Valideer binnenkomende JSON Patch-documenten voor grootte en structuur voordat u aanroept 
 
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();
    }
}
Subversie van bedrijfslogica
- Scenario: Patchbewerkingen kunnen velden bewerken met impliciete invarianten (bijvoorbeeld interne vlaggen, id's of berekende velden), die zakelijke beperkingen schenden.
 - Impact: Problemen met gegevensintegriteit en onbedoeld app-gedrag.
 - 
              Mitigation: 
- Gebruik POCO-objecten met expliciet gedefinieerde eigenschappen die veilig kunnen worden gewijzigd.
 - Vermijd het blootstellen van gevoelige of beveiligingskritieke eigenschappen in het doelobject.
 - Als er geen POCO-object wordt gebruikt, valideert u het gepatchte object na het toepassen van bewerkingen om ervoor te zorgen dat bedrijfsregels en invarianten niet worden geschonden.
 
 
Authenticatie en autorisatie
- Scenario: Niet-geverifieerde of niet-geautoriseerde clients verzenden schadelijke JSON Patch-aanvragen.
 - Impact: onbevoegde toegang om gevoelige gegevens te wijzigen of het gedrag van apps te verstoren.
 - 
              Mitigation: 
- Beveilig eindpunten die JSON Patch-aanvragen accepteren met de juiste verificatie- en autorisatiemechanismen.
 - Beperk de toegang tot vertrouwde clients of gebruikers met de juiste machtigingen.
 
 
Aanvullende bronnen
In dit artikel wordt uitgelegd hoe u JSON Patch-aanvragen kunt verwerken in een ASP.NET Core-web-API.
Important
De JSON Patch-standaard heeft inherente beveiligingsrisico's. Omdat deze risico's inherent zijn aan de JSON Patch-standaard, probeert deze implementatie geen inherente beveiligingsrisico's te beperken. Het is de verantwoordelijkheid van de ontwikkelaar om ervoor te zorgen dat het JSON Patch-document veilig kan worden toegepast op het doelobject. Zie de sectie Beveiligingsrisico's beperken voor meer informatie.
Pakketinstallatie
Voer de volgende stappen uit om ondersteuning voor JSON Patch in te schakelen in uw app:
Installeer het
Microsoft.AspNetCore.Mvc.NewtonsoftJsonNuGet-pakket.Werk de
Startup.ConfigureServices-methode van het project bij zodat deze AddNewtonsoftJsonaanroept. Voorbeeld:services .AddControllersWithViews() .AddNewtonsoftJson();
              AddNewtonsoftJson is compatibel met de registratiemethoden van de MVC-service:
JSON Patch, AddNewtonsoftJson en System.Text.Json
              AddNewtonsoftJson vervangt de op System.Text.Jsongebaseerde invoer- en uitvoerformatters die worden gebruikt voor het opmaken van alle JSON-content. Als u ondersteuning wilt toevoegen voor JSON Patch met behulp van Newtonsoft.Json, terwijl de andere formatters ongewijzigd blijven, werkt u de Startup.ConfigureServices methode van het project als volgt bij:
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();
}
Voor de voorgaande code zijn het Microsoft.AspNetCore.Mvc.NewtonsoftJson-pakket en de volgende using-instructies vereist:
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;
Gebruik de methode Newtonsoft.Json.JsonConvert.SerializeObject om een JsonPatchDocument te serialiseren.
HTTP-aanvraagmethode PATCH
De methoden PUT en PATCH worden gebruikt om een bestaande resource bij te werken. Het verschil is dat PUT de hele resource vervangt, terwijl PATCH alleen de wijzigingen opgeeft.
JSON-patch
JSON Patch is een indeling voor het opgeven van updates die moeten worden toegepast op een resource. Een JSON Patch-document bevat een matrix met bewerkingen. Elke bewerking identificeert een bepaald type wijziging. Voorbeelden van dergelijke wijzigingen zijn het toevoegen van een matrixelement of het vervangen van een eigenschapswaarde.
De volgende JSON-documenten vertegenwoordigen bijvoorbeeld een resource, een JSON Patch-document voor de resource en het resultaat van het toepassen van de patchbewerkingen.
Resourcevoorbeeld
{
  "customerName": "John",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    }
  ]
}
Voorbeeld van JSON-patch
[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]
In de voorgaande JSON:
- De eigenschap 
opgeeft het type bewerking aan. - De eigenschap 
pathgeeft het element aan dat moet worden bijgewerkt. - De eigenschap 
valuebiedt de nieuwe waarde. 
Resource na de patch
Dit is de resource na het toepassen van het voorgaande JSON Patch-document:
{
  "customerName": "Barry",
  "orders": [
    {
      "orderName": "Order0",
      "orderType": null
    },
    {
      "orderName": "Order1",
      "orderType": null
    },
    {
      "orderName": "Order2",
      "orderType": null
    }
  ]
}
De wijzigingen die zijn aangebracht door een JSON Patch-document toe te passen op een resource, zijn atomisch. Als een bewerking in de lijst mislukt, wordt er geen bewerking in de lijst toegepast.
Padsyntaxis
De eigenschap van het "/address/zipCode".
Indexen op basis van nul worden gebruikt om matrixelementen op te geven. Het eerste element van de addresses matrix bevindt zich op /addresses/0. Als u aan het einde van een matrix wilt add, gebruikt u een afbreekstreepje (-) in plaats van een indexnummer: /addresses/-.
Operations
In de volgende tabel ziet u ondersteunde bewerkingen zoals gedefinieerd in de JSON Patch-specificatie:
| Operation | Notes | 
|---|---|
add | 
Voeg een eigenschap of matrixelement toe. Voor bestaande eigenschap: waarde instellen. | 
remove | 
Een eigenschap of matrixelement verwijderen. | 
replace | 
Hetzelfde als remove gevolgd door add op dezelfde locatie. | 
move | 
Hetzelfde als remove van de bron, gevolgd door add naar de bestemming met behulp van de waarde uit de bron. | 
copy | 
Hetzelfde als add naar bestemming met behulp van waarde uit de bron. | 
test | 
Retourneer de success-statuscode als de waarde op path = opgegeven value. | 
JSON Patch in ASP.NET Core
De ASP.NET Core-implementatie van JSON Patch is beschikbaar in het Microsoft.AspNetCore.JsonPatch NuGet-pakket.
Code van actiemethode
In een API-controller, een actiemethode voor JSON Patch:
- Wordt geannoteerd met het kenmerk 
HttpPatch. - Accepteert een 
JsonPatchDocument<T>, meestal met[FromBody]. - Roept 
ApplyToaan in het patchdocument om de wijzigingen toe te passen. 
Hier volgt een voorbeeld:
[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);
    }
}
Deze code van de voorbeeld-app werkt met het volgende Customer model:
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; }
    }
}
De voorbeeldactiemethode:
- Maakt een 
Customer. - De patch wordt toegepast.
 - Retourneert het resultaat in de hoofdtekst van het antwoord.
 
In een echte app haalt de code de gegevens op uit een archief, zoals een database, en werkt de database bij nadat de patch is toegepast.
Modelstatus
In het voorgaande voorbeeld van de actiemethode wordt een overbelasting van ApplyTo aangeroepen die de modelstatus als een van de parameters gebruikt. Met deze optie kunt u foutberichten in antwoorden krijgen. In het volgende voorbeeld ziet u de body van een 400 Ongeldige Aanvraagreactie bij een test-bewerking:
{
    "Customer": [
        "The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
    ]
}
Dynamische objecten
In het volgende voorbeeld van een actiemethode ziet u hoe u een patch toepast op een dynamisch object:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
    dynamic obj = new ExpandoObject();
    patch.ApplyTo(obj);
    return Ok(obj);
}
De toevoegbewerking
- Als 
pathverwijst naar een matrixelement: voegt nieuw element in vóór het element dat is opgegeven doorpath. - Als 
pathverwijst naar een eigenschap: stelt de eigenschapswaarde in. - Als 
pathverwijst naar een niet-bestaande locatie:- Als de resource die moet worden gepatcht een dynamisch object is: voegt een eigenschap toe.
 - Als de resource om te patchen een statisch object is: de aanvraag mislukt.
 
 
In het volgende voorbeeldpatchdocument wordt de waarde van CustomerName ingesteld en wordt een Order-object toegevoegd aan het einde van de Orders-matrix.
[
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "add",
    "path": "/orders/-",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]
De verwijderbewerking
- Als 
pathverwijst naar een matrixelement: verwijdert het element. - Als 
pathverwijst naar een eigenschap:- Als de resource om te patchen een dynamisch object is, wordt de eigenschap verwijderd.
 - Als de resource om te patchen een statisch object is: 
- Als de eigenschap nullable is: stelt deze in op null.
 - Als de eigenschap niet nullbaar is, stelt u deze in op 
default<T>. 
 
 
De volgende voorbeeldpatch stelt document CustomerName in op null en verwijdert Orders[0]:
[
  {
    "op": "remove",
    "path": "/customerName"
  },
  {
    "op": "remove",
    "path": "/orders/0"
  }
]
De vervangingsbewerking
Deze bewerking is functioneel hetzelfde als een remove gevolgd door een add.
In het volgende voorbeeldpatchdocument wordt de waarde van CustomerName ingesteld en wordt Orders[0]vervangen door een nieuw Order-object:
[
  {
    "op": "replace",
    "path": "/customerName",
    "value": "Barry"
  },
  {
    "op": "replace",
    "path": "/orders/0",
    "value": {
      "orderName": "Order2",
      "orderType": null
    }
  }
]
De verplaatsingsbewerking
- Als 
pathverwijst naar een matrixelement: kopieertfromelement naar de locatie vanpathelement en voert vervolgens eenremovebewerking uit op hetfromelement. - Als 
pathverwijst naar een eigenschap: kopieert de waarde vanfromeigenschap naarpatheigenschap en voert vervolgens eenremovebewerking uit op de eigenschapfrom. - Als 
pathverwijst naar een niet-bestaande eigenschap:- Als de resource om te patchen een statisch object is: de aanvraag mislukt.
 - Als de resource die moet worden gepatcht een dynamisch object is: kopieert 
fromeigenschap naar de locatie aangegeven doorpathen voert vervolgens eenremovebewerking uit op de eigenschapfrom. 
 
Het volgende voorbeeldpatchdocument:
- Kopieert de waarde van 
Orders[0].OrderNamenaarCustomerName. - Hiermee stelt u 
Orders[0].OrderNamein op null. - Hiermee verplaatst u 
Orders[1]naar vóórOrders[0]. 
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]
De kopieerbewerking
Deze bewerking is functioneel hetzelfde als een move bewerking zonder de laatste remove stap.
Het volgende voorbeeldpatchdocument:
- Kopieert de waarde van 
Orders[0].OrderNamenaarCustomerName. - Voegt een kopie van 
Orders[1]in vóórOrders[0]. 
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]
De testbewerking
Als de waarde op de locatie die wordt aangegeven door path verschilt van de waarde in value, mislukt de aanvraag. In dat geval mislukt de hele PATCH-aanvraag, zelfs als alle andere bewerkingen in het patchdocument anders zouden slagen.
De test bewerking wordt vaak gebruikt om een update te voorkomen wanneer er een gelijktijdigheidsconflict is.
Het volgende voorbeeldpatchdocument heeft geen effect als de initiële waarde van CustomerName 'John' is, omdat de test mislukt:
[
  {
    "op": "test",
    "path": "/customerName",
    "value": "Nancy"
  },
  {
    "op": "add",
    "path": "/customerName",
    "value": "Barry"
  }
]
De code ophalen
voorbeeldcode weergeven of downloaden. (Hoe te downloaden).
Als u het voorbeeld wilt testen, voert u de app uit en verzendt u HTTP-aanvragen met de volgende instellingen:
- URL: 
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate - HTTP-methode: 
PATCH - Koptekst: 
Content-Type: application/json-patch+json - Hoofdtekst: Kopieer en plak een van de voorbeelden van JSON-patchdocumenten uit de JSON- projectmap.