Udostępnij za pośrednictwem


Obsługa poprawek JSON w internetowym interfejsie API platformy ASP.NET Core

W tym artykule wyjaśniono, jak obsługiwać żądania poprawek JSON w internetowym interfejsie API platformy ASP.NET Core.

Obsługa poprawek JSON w internetowym interfejsie API platformy ASP.NET Core jest oparta na System.Text.Json serializacji i wymaga Microsoft.AspNetCore.JsonPatch.SystemTextJson pakietu NuGet.

Co to jest standard poprawki JSON?

Standard poprawki JSON:

  • To standardowy format opisujący zmiany, które mają być stosowane do dokumentu JSON.

  • Jest definiowany w dokumencie RFC 6902 i jest powszechnie używany w interfejsach API RESTful do wykonywania częściowych aktualizacji zasobów JSON.

  • Opisuje sekwencję operacji modyfikujących dokument JSON, na przykład:

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

W aplikacjach internetowych poprawka JSON jest często używana w operacji PATCH do wykonywania częściowych aktualizacji zasobu. Zamiast wysyłać cały zasób aktualizacji, klienci mogą wysyłać dokument poprawki JSON zawierający tylko zmiany. Stosowanie poprawek zmniejsza rozmiar ładunku i zwiększa wydajność.

Aby zapoznać się z omówieniem standardu JSON Patch, zobacz jsonpatch.com.

Obsługa poprawek JSON w internetowym interfejsie API platformy ASP.NET Core

Obsługa poprawek JSON w webowym interfejsie API platformy ASP.NET Core jest oparta na System.Text.Json serializacji, począwszy od wersji .NET 10, implementacji Microsoft.AspNetCore.JsonPatch w oparciu o serializację System.Text.Json. Ta funkcja:

  • Microsoft.AspNetCore.JsonPatch.SystemTextJson Wymaga pakietu NuGet.
  • Jest zgodny z nowoczesnymi rozwiązaniami platformy .NET dzięki wykorzystaniu System.Text.Json biblioteki zoptymalizowanej pod kątem platformy .NET.
  • Zapewnia lepszą wydajność i mniejsze użycie pamięci w porównaniu ze starszą implementacją opartą na Newtonsoft.Json. Aby uzyskać więcej informacji na temat starszej implementacji opartej na Newtonsoft.Json, zobacz wersję artykułu z .NET 9.

Uwaga / Notatka

Implementacja Microsoft.AspNetCore.JsonPatch oparta na System.Text.Json serializacji nie jest zamiennikiem dla starszej implementacji opartej na Newtonsoft.Json. Nie obsługuje typów dynamicznych, na przykład ExpandoObject.

Ważne

Standard poprawki JSON ma nieodłączne zagrożenia bezpieczeństwa. Ponieważ te zagrożenia są związane ze standardem poprawki JSON, implementacja ASP.NET Core nie próbuje ograniczyć ryzyka związanego z zabezpieczeniami. Jest to odpowiedzialność dewelopera za zapewnienie, że dokument poprawki JSON jest bezpieczny do zastosowania do obiektu docelowego. Aby uzyskać więcej informacji, zobacz sekcję Ograniczanie ryzyka zabezpieczeń .

Włączanie obsługi poprawek JSON za pomocą polecenia System.Text.Json

Aby włączyć obsługę JSON Patch za pomocą System.Text.Json, zainstaluj pakiet NuGet Microsoft.AspNetCore.JsonPatch.SystemTextJson.

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

Ten pakiet zawiera klasę reprezentującą JsonPatchDocument<TModel> dokument poprawki JSON dla obiektów typu T oraz dedykowaną logikę do serializacji i deserializacji dokumentów poprawek JSON z użyciem System.Text.Json. Kluczową metodą JsonPatchDocument<TModel> klasy jest ApplyTo(Object), która stosuje operacje poprawek do obiektu docelowego typu T.

Kod metody akcji stosujący poprawkę JSON

W kontrolerze interfejsu API metoda akcji dla poprawki JSON:

Przykładowa metoda akcji kontrolera:

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

Kod z tej przykładowej aplikacji działa z następującymi modelami: Customer i Order.

namespace App.Models;

public class Customer
{
    public string Id { get; set; }
    public string? Name { get; set; }
    public string? Email { get; set; }
    public string? PhoneNumber { get; set; }
    public string? Address { get; set; }
    public List<Order>? Orders { get; set; }

    public Customer()
    {
        Id = Guid.NewGuid().ToString();
    }
}
namespace App.Models;

public class Order
{
    public string Id { get; set; }
    public DateTime? OrderDate { get; set; }
    public DateTime? ShipDate { get; set; }
    public decimal TotalAmount { get; set; }

    public Order()
    {
        Id = Guid.NewGuid().ToString();
    }
}

Kluczowe kroki metody przykładowej akcji:

  • Pobierz klienta:
    • Metoda pobiera Customer obiekt z bazy danych AppDb przy użyciu podanego identyfikatora.
    • Jeśli nie zostanie znaleziony żaden obiekt Customer, zwraca odpowiedź 404 Not Found.
  • Zastosuj poprawkę JSON:
    • Metoda ApplyTo(Object) stosuje operacje JSON Patch z patchDoc do pobranego Customer obiektu.
    • Jeśli podczas aplikacji poprawek wystąpią błędy, takie jak nieprawidłowe operacje lub konflikty, są przechwytywane przez delegata obsługi błędów. Ten delegat dodaje komunikaty o błędach do ModelState obiektu, którego dotyczy problem, przy użyciu nazwy typu i komunikatu o błędzie.
  • Sprawdź poprawność stanu modelu:
    • Po zastosowaniu poprawki metoda sprawdza ModelState pod kątem błędów.
    • Jeśli element ModelState jest nieprawidłowy, na przykład z powodu błędów poprawek, zwraca 400 Bad Request odpowiedź z błędami walidacji.
  • Zwróć zaktualizowanego klienta:
    • Jeśli poprawka została pomyślnie zastosowana i ModelState jest prawidłowa, metoda zwraca zaktualizowany Customer obiekt w odpowiedzi.

Przykładowa odpowiedź na błąd:

Poniższy przykład przedstawia treść odpowiedzi 400 Bad Request dla operacji JSON Patch, gdy określona ścieżka jest nieprawidłowa:

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

Stosowanie dokumentu poprawki JSON do obiektu

W poniższych przykładach pokazano, jak za pomocą metody zastosować ApplyTo(Object) dokument poprawki JSON do obiektu.

Przykład: Zastosuj JsonPatchDocument<TModel> do obiektu

W poniższym przykładzie pokazano:

  • Operacje add, replacei remove .
  • Operacje na właściwościach zagnieżdżonych.
  • Dodawanie nowego elementu do tablicy.
  • Używanie konwertera typu wyliczeniowego dla ciągów JSON w dokumencie modyfikacji JSON.
// Original object
var person = new Person {
    FirstName = "John",
    LastName = "Doe",
    Email = "johndoe@gmail.com",
    PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
    Address = new Address
    {
        Street = "123 Main St",
        City = "Anytown",
        State = "TX"
    }
};

// Raw JSON patch document
string jsonPatch = """
[
    { "op": "replace", "path": "/FirstName", "value": "Jane" },
    { "op": "remove", "path": "/Email"},
    { "op": "add", "path": "/Address/ZipCode", "value": "90210" },
    { "op": "add", "path": "/PhoneNumbers/-", "value": { "Number": "987-654-3210",
                                                                "Type": "Work" } }
]
""";

// Deserialize the JSON patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);

// Apply the JSON patch document
patchDoc!.ApplyTo(person);

// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));

Poprzedni przykład powoduje wyświetlenie następujących danych wyjściowych zaktualizowanego obiektu:

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

Metoda ApplyTo(Object) zwykle jest zgodna z konwencjami i opcjami System.Text.Json przy przetwarzaniu JsonPatchDocument<TModel> elementu, w tym z zachowaniem kontrolowanym przez następujące opcje:

Kluczowe różnice między System.Text.Json a nową implementacją JsonPatchDocument<TModel>:

  • Typ wykonywania obiektu docelowego, a nie zadeklarowany typ, określa, które właściwości ApplyTo(Object) są poprawiane.
  • System.Text.Json deserializacja opiera się na zadeklarowanym typie w celu zidentyfikowania kwalifikujących się właściwości.

Przykład: stosowanie obiektu JsonPatchDocument z obsługą błędów

Podczas stosowania dokumentu poprawki JSON mogą wystąpić różne błędy. Na przykład obiekt docelowy może nie mieć określonej właściwości lub określona wartość może być niezgodna z typem właściwości.

Kod JSON Patch obsługuje operację test , która sprawdza, czy określona wartość jest równa właściwości docelowej. Jeśli tak nie jest, zwraca błąd.

W poniższym przykładzie pokazano, jak bezpiecznie obsługiwać te błędy.

Ważne

Obiekt przekazany do ApplyTo(Object) metody jest modyfikowany na miejscu. Obiekt wywołujący jest odpowiedzialny za odrzucanie zmian w przypadku niepowodzenia operacji.

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

W poprzednim przykładzie są wyświetlane następujące dane wyjściowe:

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

Ograniczanie ryzyka zabezpieczeń

W przypadku korzystania z Microsoft.AspNetCore.JsonPatch.SystemTextJson pakietu kluczowe znaczenie ma zrozumienie i ograniczenie potencjalnych zagrożeń bezpieczeństwa. W poniższych sekcjach opisano zidentyfikowane zagrożenia bezpieczeństwa związane z poprawką JSON i przedstawiono zalecane środki zaradcze w celu zapewnienia bezpiecznego użycia pakietu.

Ważne

Nie jest to wyczerpująca lista zagrożeń. Deweloperzy aplikacji muszą przeprowadzać własne przeglądy modeli zagrożeń, aby określić kompleksową listę specyficzną dla aplikacji i w razie potrzeby wymyślić odpowiednie środki zaradcze. Na przykład aplikacje, które uwidaczniają kolekcje w operacjach poprawek, powinny rozważyć potencjalne ataki związane z złożonością algorytmiczną, jeśli te operacje wstawiają lub usuwają elementy na początku kolekcji.

Aby zminimalizować zagrożenia bezpieczeństwa podczas integrowania funkcji poprawek JSON z aplikacjami, deweloperzy powinni:

  • Uruchamianie kompleksowych modeli zagrożeń dla własnych aplikacji.
  • Rozwiązywanie zidentyfikowanych zagrożeń.
  • Postępuj zgodnie z zalecanymi środkami zaradczymi w poniższych sekcjach.

Odmowa usługi (DoS) za pośrednictwem wzmacniania pamięci

  • Scenariusz: Złośliwy klient przesyła operację copy , która duplikuje duże grafy obiektów wiele razy, co prowadzi do nadmiernego użycia pamięci.
  • Wpływ: Potencjalne warunkiOf-Memory out-Of-Memory (OOM), powodując zakłócenia usług.
  • Łagodzenie:
    • Przed wywołaniem metody ApplyTo(Object)zweryfikuj przychodzące dokumenty poprawek JSON pod kątem rozmiaru i struktury.
    • Walidacja musi być specyficzna dla aplikacji, ale przykładowa weryfikacja może wyglądać podobnie do następującego:
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();
    }
}

Podwersja logiki biznesowej

  • Scenariusz: Operacje poprawek mogą manipulować polami z niejawnymi zmiennymi (na przykład flagami wewnętrznymi, identyfikatorami lub polami obliczeniowymi), naruszając ograniczenia biznesowe.
  • Wpływ: problemy z integralnością danych i niezamierzone zachowanie aplikacji.
  • Łagodzenie:
    • Użyj obiektów POC (zwykłych starych obiektów CLR) z jawnie zdefiniowanymi właściwościami, które można bezpiecznie zmodyfikować.
      • Unikaj uwidaczniania poufnych lub krytycznych właściwości w obiekcie docelowym.
      • Jeśli obiekt POCO nie jest używany, zweryfikuj załatany obiekt po zastosowaniu operacji, aby upewnić się, że reguły biznesowe i zasady niezmienności nie są naruszone.

Uwierzytelnianie i autoryzacja

  • Scenariusz: Nieuwierzytelniony lub nieautoryzowani klienci wysyłają złośliwe żądania poprawek JSON.
  • Wpływ: Nieautoryzowany dostęp do modyfikowania poufnych danych lub zakłócania działania aplikacji.
  • Łagodzenie:
    • Ochrona punktów końcowych akceptujących żądania poprawek JSON przy użyciu odpowiednich mechanizmów uwierzytelniania i autoryzacji.
    • Ogranicz dostęp do zaufanych klientów lub użytkowników z odpowiednimi uprawnieniami.

Uzyskiwanie kodu

Wyświetl lub pobierz kod przykładowy. (Jak pobrać).

Aby przetestować przykład, uruchom aplikację i wyślij żądania HTTP przy użyciu następujących ustawień:

  • Adres URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • Metoda HTTP: PATCH
  • Nagłówek: Content-Type: application/json-patch+json
  • Treść: skopiuj i wklej jeden z przykładów dokumentów poprawek JSON z folderu projektu JSON .

Dodatkowe zasoby

W tym artykule wyjaśniono, jak obsługiwać żądania poprawek JSON w internetowym interfejsie API platformy ASP.NET Core.

Ważne

Standard poprawki JSON ma nieodłączne zagrożenia bezpieczeństwa. Ta implementacja nie próbuje ograniczyć tych nieodłącznych zagrożeń bezpieczeństwa. Jest to odpowiedzialność dewelopera za zapewnienie, że dokument poprawki JSON jest bezpieczny do zastosowania do obiektu docelowego. Aby uzyskać więcej informacji, zobacz sekcję Ograniczanie ryzyka zabezpieczeń .

Instalacja pakietu

Obsługa poprawek JSON w internetowym interfejsie API platformy ASP.NET Core jest oparta i Newtonsoft.Json wymaga Microsoft.AspNetCore.Mvc.NewtonsoftJson pakietu NuGet.

Aby włączyć obsługę poprawek JSON:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson Zainstaluj pakiet NuGet.

  • Wywołaj polecenie AddNewtonsoftJson. Na przykład:

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

AddNewtonsoftJson Zastępuje domyślne System.Text.Jsonformatery danych wejściowych i wyjściowych używane do formatowania całej zawartości JSON. Ta metoda rozszerzenia jest zgodna z następującymi metodami rejestracji usługi MVC:

JsonPatch wymaga ustawienia nagłówka Content-Type na application/json-patch+json.

Dodawanie obsługi poprawki JSON w przypadku korzystania z pliku System.Text.Json

Formater System.Text.Jsondanych wejściowych oparty na protokole nie obsługuje poprawki JSON. Aby dodać obsługę poprawek JSON przy użyciu polecenia Newtonsoft.Json, pozostawiając inne formatery danych wejściowych i wyjściowych bez zmian:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson Zainstaluj pakiet NuGet.

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

Powyższy kod tworzy wystąpienie NewtonsoftJsonPatchInputFormatter obiektu i wstawia je jako pierwszy wpis w kolekcji MvcOptions.InputFormatters . Ta kolejność rejestracji gwarantuje, że:

  • NewtonsoftJsonPatchInputFormatter przetwarza żądania poprawek JSON.
  • Istniejące System.Text.Jsonelementy wejściowe i formatujące przetwarzają wszystkie inne żądania i odpowiedzi w formacie JSON.

Newtonsoft.Json.JsonConvert.SerializeObject Użyj metody , aby serializować element JsonPatchDocument.

PATCH HTTP request method (Metoda żądania HTTP PATCH)

Metody PUT i PATCH służą do aktualizowania istniejącego zasobu. Różnica między nimi polega na tym, że PUT zastępuje cały zasób, podczas gdy PATCH określa tylko zmiany.

Poprawka JSON

Poprawka JSON to format określający aktualizacje, które mają być stosowane do zasobu. Dokument poprawki JSON zawiera tablicę operacji. Każda operacja identyfikuje określony typ zmiany. Przykłady takich zmian obejmują dodanie elementu tablicy lub zastąpienie wartości właściwości.

Na przykład następujące dokumenty JSON reprezentują zasób, dokument poprawki JSON dla zasobu i wynik zastosowania operacji patch.

Przykład zasobu

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

Przykład poprawki JSON

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

W powyższym kodzie JSON:

  • Właściwość op wskazuje typ operacji.
  • Właściwość path wskazuje element do zaktualizowania.
  • Właściwość value udostępnia nową wartość.

Zasób po poprawce

Oto zasób po zastosowaniu poprzedniego dokumentu poprawki JSON:

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

Zmiany wprowadzone przez zastosowanie dokumentu poprawki JSON do zasobu są niepodzielne. Jeśli jakakolwiek operacja na liście nie powiedzie się, nie zostanie zastosowana żadna operacja na liście.

Składnia XPath

Właściwość path obiektu operacji ma ukośniki między poziomami. Na przykład "/address/zipCode".

Indeksy oparte na zerach służą do określania elementów tablicy. Pierwszym elementem addresses tablicy będzie wartość /addresses/0. Na add końcu tablicy użyj łącznika (-), a nie numeru indeksu: /addresses/-.

Operacje

W poniższej tabeli przedstawiono obsługiwane operacje zdefiniowane w specyfikacji poprawki JSON:

Operacja Uwagi
add Dodaj właściwość lub element tablicy. Dla istniejącej właściwości: ustaw wartość.
remove Usuń właściwość lub element tablicy.
replace To samo, co remove następuje add w tej samej lokalizacji.
move Tak samo jak remove ze źródła, a następnie add do miejsca docelowego przy użyciu wartości ze źródła.
copy Tak samo jak add w przypadku lokalizacji docelowej przy użyciu wartości ze źródła.
test Zwraca kod stanu powodzenia, jeśli wartość na path = podano value.

Poprawka JSON w ASP.NET Core

Implementacja ASP.NET Core poprawki JSON jest dostępna w pakiecie NuGet Microsoft.AspNetCore.JsonPatch .

Kod metody akcji

W kontrolerze interfejsu API metoda akcji dla poprawki JSON:

Oto przykład:

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

Ten kod z przykładowej aplikacji działa z następującym 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; }
}

Przykładowa metoda akcji:

  • Tworzy element Customer.
  • Stosuje poprawkę.
  • Zwraca wynik w treści odpowiedzi.

W rzeczywistej aplikacji kod pobiera dane z magazynu, takiego jak baza danych, i aktualizuje bazę danych po zastosowaniu poprawki.

Stan modelu

Powyższy przykład metody akcji wywołuje przeciążenie ApplyTo , które przyjmuje stan modelu jako jeden z jego parametrów. Dzięki tej opcji możesz otrzymywać komunikaty o błędach w odpowiedziach. W poniższym przykładzie przedstawiono treść odpowiedzi 400 Nieprawidłowe żądanie dla test operacji:

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

Obiekty dynamiczne

Poniższy przykład metody akcji pokazuje, jak zastosować poprawkę do obiektu dynamicznego:

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

    return Ok(obj);
}

Operacja dodawania

  • Jeśli path wskazuje element tablicy: wstawia nowy element przed elementem określonym przez path.
  • Jeśli path wskazuje właściwość: ustawia wartość właściwości.
  • Jeśli path wskazuje na nieistnieną lokalizację:
    • Jeśli zasób do stosowania poprawek jest obiektem dynamicznym: dodaje właściwość.
    • Jeśli zasób do stosowania poprawek jest obiektem statycznym: żądanie kończy się niepowodzeniem.

Poniższy przykładowy dokument poprawki ustawia wartość CustomerName i dodaje Order obiekt na końcu Orders tablicy.

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

Operacja usuwania

  • Jeśli path wskazuje element tablicy: usuwa element.
  • Jeśli path wskazuje właściwość:
    • Jeśli zasób do poprawki jest obiektem dynamicznym: usuwa właściwość .
    • Jeśli zasób do poprawki jest obiektem statycznym:
      • Jeśli właściwość ma wartość null: ustawia ją na wartość null.
      • Jeśli właściwość jest niemożliwa do wartości null, ustawia ją na default<T>wartość .

Następujące przykładowe poprawki dokumentów ustawiają wartość CustomerName null i usuwają Orders[0]element :

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

Operacja zastępowania

Ta operacja jest funkcjonalnie taka sama jak remove po nim .add

Poniższy przykładowy dokument poprawki ustawia wartość CustomerName i zastępuje Orders[0]element nowym Order obiektem:

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

Operacja przenoszenia

  • Jeśli path wskazuje element tablicy: kopiuje from element do lokalizacji path elementu, a następnie uruchamia operację remove na elemecie from .
  • Jeśli path wskazuje właściwość: kopiuje wartość from właściwości do path właściwości, a następnie uruchamia operację remove na from właściwości.
  • Jeśli path wskazuje na nieistnieną właściwość:
    • Jeśli zasób do stosowania poprawek jest obiektem statycznym: żądanie kończy się niepowodzeniem.
    • Jeśli zasób do stosowania poprawek jest obiektem dynamicznym: kopiuje from właściwość do lokalizacji wskazanej przez pathelement , uruchamia operację remove na from właściwości .

Następujący przykładowy dokument poprawki:

  • Kopiuje wartość Orders[0].OrderName do CustomerName.
  • Ustawia Orders[0].OrderName wartość null.
  • Przechodzi Orders[1] do przed Orders[0].
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Operacja kopiowania

Ta operacja jest funkcjonalnie taka sama jak move operacja bez ostatniego remove kroku.

Następujący przykładowy dokument poprawki:

  • Kopiuje wartość Orders[0].OrderName do CustomerName.
  • Wstawia kopię Orders[1] przed elementem Orders[0].
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Operacja testowa

Jeśli wartość w lokalizacji wskazanej przez path jest inna niż wartość podana w valueelemecie , żądanie zakończy się niepowodzeniem. W takim przypadku całe żądanie PATCH kończy się niepowodzeniem, nawet jeśli wszystkie inne operacje w dokumencie poprawki zakończyłyby się powodzeniem.

Operacja test jest często używana do zapobiegania aktualizacji w przypadku konfliktu współbieżności.

Następujący przykładowy dokument poprawki nie ma wpływu, jeśli początkowa wartość CustomerName to "Jan", ponieważ test kończy się niepowodzeniem:

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

Uzyskiwanie kodu

Wyświetl lub pobierz kod przykładowy. (Jak pobrać).

Aby przetestować przykład, uruchom aplikację i wyślij żądania HTTP przy użyciu następujących ustawień:

  • Adres URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • Metoda HTTP: PATCH
  • Nagłówek: Content-Type: application/json-patch+json
  • Treść: skopiuj i wklej jeden z przykładów dokumentów poprawek JSON z folderu projektu JSON .

Ograniczanie ryzyka zabezpieczeń

W przypadku korzystania z pakietu Microsoft.AspNetCore.JsonPatch z implementacją opartą na Newtonsoft.Json, kluczowe jest zrozumienie i ograniczenie potencjalnych zagrożeń bezpieczeństwa. W poniższych sekcjach opisano zidentyfikowane zagrożenia bezpieczeństwa związane z poprawką JSON i przedstawiono zalecane środki zaradcze w celu zapewnienia bezpiecznego użycia pakietu.

Ważne

Nie jest to wyczerpująca lista zagrożeń. Deweloperzy aplikacji muszą przeprowadzać własne przeglądy modeli zagrożeń, aby określić kompleksową listę specyficzną dla aplikacji i w razie potrzeby wymyślić odpowiednie środki zaradcze. Na przykład aplikacje, które uwidaczniają kolekcje w operacjach poprawek, powinny rozważyć potencjalne ataki związane z złożonością algorytmiczną, jeśli te operacje wstawiają lub usuwają elementy na początku kolekcji.

Uruchamiając kompleksowe modele zagrożeń dla własnych aplikacji i zwracając się do zidentyfikowanych zagrożeń, stosując poniższe zalecane środki zaradcze, konsumenci tych pakietów mogą zintegrować funkcje poprawki JSON z aplikacjami, jednocześnie minimalizując zagrożenia bezpieczeństwa.

Odmowa usługi (DoS) za pośrednictwem wzmacniania pamięci

  • Scenariusz: Złośliwy klient przesyła operację copy , która duplikuje duże grafy obiektów wiele razy, co prowadzi do nadmiernego użycia pamięci.
  • Wpływ: Potencjalne warunkiOf-Memory out-Of-Memory (OOM), powodując zakłócenia usług.
  • Łagodzenie:
    • Przed wywołaniem metody ApplyTozweryfikuj przychodzące dokumenty poprawek JSON pod kątem rozmiaru i struktury.
    • Walidacja musi być specyficzna dla aplikacji, ale przykładowa weryfikacja może wyglądać podobnie do następującego:
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();
    }
}

Podwersja logiki biznesowej

  • Scenariusz: Operacje poprawek mogą manipulować polami z niejawnymi zmiennymi (na przykład flagami wewnętrznymi, identyfikatorami lub polami obliczeniowymi), naruszając ograniczenia biznesowe.
  • Wpływ: problemy z integralnością danych i niezamierzone zachowanie aplikacji.
  • Łagodzenie:
    • Użyj obiektów POCO z jawnie zdefiniowanymi właściwościami, które można bezpiecznie modyfikować.
    • Unikaj uwidaczniania poufnych lub krytycznych właściwości w obiekcie docelowym.
    • Jeśli nie jest używany żaden obiekt POCO, zweryfikuj poprawiony obiekt po zastosowaniu operacji, aby upewnić się, że reguły biznesowe i niezmienne nie są naruszone.

Uwierzytelnianie i autoryzacja

  • Scenariusz: Nieuwierzytelniony lub nieautoryzowani klienci wysyłają złośliwe żądania poprawek JSON.
  • Wpływ: Nieautoryzowany dostęp do modyfikowania poufnych danych lub zakłócania działania aplikacji.
  • Łagodzenie:
    • Ochrona punktów końcowych akceptujących żądania poprawek JSON przy użyciu odpowiednich mechanizmów uwierzytelniania i autoryzacji.
    • Ogranicz dostęp do zaufanych klientów lub użytkowników z odpowiednimi uprawnieniami.

Dodatkowe zasoby

W tym artykule wyjaśniono, jak obsługiwać żądania poprawek JSON w internetowym interfejsie API platformy ASP.NET Core.

Ważne

Standard poprawki JSON ma nieodłączne zagrożenia bezpieczeństwa. Ponieważ te zagrożenia są związane ze standardem poprawki JSON, ta implementacja nie próbuje ograniczyć ryzyka związanego z zabezpieczeniami. Jest to odpowiedzialność dewelopera za zapewnienie, że dokument poprawki JSON jest bezpieczny do zastosowania do obiektu docelowego. Aby uzyskać więcej informacji, zobacz sekcję Ograniczanie ryzyka zabezpieczeń .

Instalacja pakietu

Aby włączyć obsługę poprawek JSON w aplikacji, wykonaj następujące kroki:

  1. Microsoft.AspNetCore.Mvc.NewtonsoftJson Zainstaluj pakiet NuGet.

  2. Zaktualizuj metodę projektu Startup.ConfigureServices , aby wywołać AddNewtonsoftJsonmetodę . Na przykład:

    services
        .AddControllersWithViews()
        .AddNewtonsoftJson();
    

AddNewtonsoftJson jest zgodny z metodami rejestracji usługi MVC:

Poprawka JSON, AddNewtonsoftJson i System.Text.Json

AddNewtonsoftJson System.Text.JsonZastępuje - oparte formatery danych wejściowych i wyjściowych używane do formatowania całej zawartości JSON. Aby dodać obsługę poprawki JSON przy użyciu polecenia Newtonsoft.Json, pozostawiając inne formatery bez zmian, zaktualizuj metodę projektu Startup.ConfigureServices w następujący sposób:

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

Powyższy kod wymaga Microsoft.AspNetCore.Mvc.NewtonsoftJson pakietu i następujących using instrukcji:

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;

Newtonsoft.Json.JsonConvert.SerializeObject Użyj metody , aby serializować kod JsonPatchDocument.

PATCH HTTP request method (Metoda żądania HTTP PATCH)

Metody PUT i PATCH służą do aktualizowania istniejącego zasobu. Różnica między nimi polega na tym, że PUT zastępuje cały zasób, podczas gdy PATCH określa tylko zmiany.

Poprawka JSON

Poprawka JSON to format określający aktualizacje, które mają być stosowane do zasobu. Dokument poprawki JSON zawiera tablicę operacji. Każda operacja identyfikuje określony typ zmiany. Przykłady takich zmian obejmują dodanie elementu tablicy lub zastąpienie wartości właściwości.

Na przykład następujące dokumenty JSON reprezentują zasób, dokument poprawki JSON dla zasobu i wynik zastosowania operacji patch.

Przykład zasobu

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

Przykład poprawki JSON

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

W powyższym kodzie JSON:

  • Właściwość op wskazuje typ operacji.
  • Właściwość path wskazuje element do zaktualizowania.
  • Właściwość value udostępnia nową wartość.

Zasób po poprawce

Oto zasób po zastosowaniu poprzedniego dokumentu poprawki JSON:

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

Zmiany wprowadzone przez zastosowanie dokumentu poprawki JSON do zasobu są niepodzielne. Jeśli jakakolwiek operacja na liście nie powiedzie się, nie zostanie zastosowana żadna operacja na liście.

Składnia XPath

Właściwość path obiektu operacji ma ukośniki między poziomami. Na przykład "/address/zipCode".

Indeksy oparte na zerach służą do określania elementów tablicy. Pierwszym elementem addresses tablicy będzie wartość /addresses/0. Na add końcu tablicy użyj łącznika (-), a nie numeru indeksu: /addresses/-.

Operacje

W poniższej tabeli przedstawiono obsługiwane operacje zdefiniowane w specyfikacji poprawki JSON:

Operacja Uwagi
add Dodaj właściwość lub element tablicy. Dla istniejącej właściwości: ustaw wartość.
remove Usuń właściwość lub element tablicy.
replace To samo, co remove następuje add w tej samej lokalizacji.
move Tak samo jak remove ze źródła, a następnie add do miejsca docelowego przy użyciu wartości ze źródła.
copy Tak samo jak add w przypadku lokalizacji docelowej przy użyciu wartości ze źródła.
test Zwraca kod stanu powodzenia, jeśli wartość na path = podano value.

Poprawka JSON w ASP.NET Core

Implementacja ASP.NET Core poprawki JSON jest dostępna w pakiecie NuGet Microsoft.AspNetCore.JsonPatch .

Kod metody akcji

W kontrolerze interfejsu API metoda akcji dla poprawki JSON:

  • Jest adnotacją z atrybutem HttpPatch .
  • Akceptuje element JsonPatchDocument<T>, zazwyczaj z elementem [FromBody].
  • Wywołuje ApplyTo dokument poprawki, aby zastosować zmiany.

Oto przykład:

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

Ten kod z przykładowej aplikacji działa z następującym 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; }
    }
}

Przykładowa metoda akcji:

  • Tworzy element Customer.
  • Stosuje poprawkę.
  • Zwraca wynik w treści odpowiedzi.

W rzeczywistej aplikacji kod pobiera dane z magazynu, takiego jak baza danych, i aktualizuje bazę danych po zastosowaniu poprawki.

Stan modelu

Powyższy przykład metody akcji wywołuje przeciążenie ApplyTo , które przyjmuje stan modelu jako jeden z jego parametrów. Dzięki tej opcji możesz otrzymywać komunikaty o błędach w odpowiedziach. W poniższym przykładzie przedstawiono treść odpowiedzi 400 Nieprawidłowe żądanie dla test operacji:

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

Obiekty dynamiczne

Poniższy przykład metody akcji pokazuje, jak zastosować poprawkę do obiektu dynamicznego:

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

    return Ok(obj);
}

Operacja dodawania

  • Jeśli path wskazuje element tablicy: wstawia nowy element przed elementem określonym przez path.
  • Jeśli path wskazuje właściwość: ustawia wartość właściwości.
  • Jeśli path wskazuje na nieistnieną lokalizację:
    • Jeśli zasób do stosowania poprawek jest obiektem dynamicznym: dodaje właściwość.
    • Jeśli zasób do stosowania poprawek jest obiektem statycznym: żądanie kończy się niepowodzeniem.

Poniższy przykładowy dokument poprawki ustawia wartość CustomerName i dodaje Order obiekt na końcu Orders tablicy.

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

Operacja usuwania

  • Jeśli path wskazuje element tablicy: usuwa element.
  • Jeśli path wskazuje właściwość:
    • Jeśli zasób do poprawki jest obiektem dynamicznym: usuwa właściwość .
    • Jeśli zasób do poprawki jest obiektem statycznym:
      • Jeśli właściwość ma wartość null: ustawia ją na wartość null.
      • Jeśli właściwość jest niemożliwa do wartości null, ustawia ją na default<T>wartość .

Następujące przykładowe poprawki dokumentów ustawiają wartość CustomerName null i usuwają Orders[0]element :

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

Operacja zastępowania

Ta operacja jest funkcjonalnie taka sama jak remove po nim .add

Poniższy przykładowy dokument poprawki ustawia wartość CustomerName i zastępuje Orders[0]element nowym Order obiektem:

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

Operacja przenoszenia

  • Jeśli path wskazuje element tablicy: kopiuje from element do lokalizacji path elementu, a następnie uruchamia operację remove na elemecie from .
  • Jeśli path wskazuje właściwość: kopiuje wartość from właściwości do path właściwości, a następnie uruchamia operację remove na from właściwości.
  • Jeśli path wskazuje na nieistnieną właściwość:
    • Jeśli zasób do stosowania poprawek jest obiektem statycznym: żądanie kończy się niepowodzeniem.
    • Jeśli zasób do stosowania poprawek jest obiektem dynamicznym: kopiuje from właściwość do lokalizacji wskazanej przez pathelement , uruchamia operację remove na from właściwości .

Następujący przykładowy dokument poprawki:

  • Kopiuje wartość Orders[0].OrderName do CustomerName.
  • Ustawia Orders[0].OrderName wartość null.
  • Przechodzi Orders[1] do przed Orders[0].
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Operacja kopiowania

Ta operacja jest funkcjonalnie taka sama jak move operacja bez ostatniego remove kroku.

Następujący przykładowy dokument poprawki:

  • Kopiuje wartość Orders[0].OrderName do CustomerName.
  • Wstawia kopię Orders[1] przed elementem Orders[0].
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Operacja testowa

Jeśli wartość w lokalizacji wskazanej przez path jest inna niż wartość podana w valueelemecie , żądanie zakończy się niepowodzeniem. W takim przypadku całe żądanie PATCH kończy się niepowodzeniem, nawet jeśli wszystkie inne operacje w dokumencie poprawki zakończyłyby się powodzeniem.

Operacja test jest często używana do zapobiegania aktualizacji w przypadku konfliktu współbieżności.

Następujący przykładowy dokument poprawki nie ma wpływu, jeśli początkowa wartość CustomerName to "Jan", ponieważ test kończy się niepowodzeniem:

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

Uzyskiwanie kodu

Wyświetl lub pobierz kod przykładowy. (Jak pobrać).

Aby przetestować przykład, uruchom aplikację i wyślij żądania HTTP przy użyciu następujących ustawień:

  • Adres URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • Metoda HTTP: PATCH
  • Nagłówek: Content-Type: application/json-patch+json
  • Treść: skopiuj i wklej jeden z przykładów dokumentów poprawek JSON z folderu projektu JSON .