Aracılığıyla paylaş


ASP.NET Core web API'de JSON Patch'in desteklenmesi

Bu makalede, ASP.NET Core web API'sinde JSON Düzeltme Eki isteklerinin nasıl işleneceğini açıklanmaktadır.

ASP.NET Core web API'de JSON Yaması desteği, System.Text.Json serileştirmesine dayanır ve Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet paketini gerektirir.

JSON Patch standardı nedir?

JSON Patch standardı:

  • JSON belgesine uygulanacak değişiklikleri açıklamaya yönelik standart bir biçimdir.

  • RFC 6902'de tanımlanır ve JSON kaynaklarında kısmi güncelleştirmeler gerçekleştirmek için RESTful API'lerinde yaygın olarak kullanılır.

  • Aşağıdakiler gibi bir JSON belgesini değiştiren işlem dizisini açıklar:

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

Web uygulamalarında JSON Patch genellikle bir kaynağın kısmi güncelleştirmelerini gerçekleştirmek için PATCH işleminde kullanılır. İstemciler, bir güncelleştirme için kaynağın tamamını göndermek yerine yalnızca değişiklikleri içeren bir JSON Düzeltme Eki belgesi gönderebilir. Yama yapmak, yük boyutunu azaltır ve verimliliği artırır.

JSON Düzeltme Eki standardına genel bakış için bkz. jsonpatch.com.

ASP.NET Core web API'de JSON Patch'in desteklenmesi

ASP.NET Core web API'sinde JSON Patch desteği, .NET 10'dan itibaren System.Text.Json serileştirmeye dayanır ve Microsoft.AspNetCore.JsonPatch serileştirme temelli System.Text.Json uygulamasıyla gerçekleştirilir. Bu özellik:

Uyarı

Microsoft.AspNetCore.JsonPatch serileştirmesine dayalı System.Text.Json uygulaması, eski Newtonsoft.Json tabanlı uygulamanın doğrudan bir ikamesi değildir. Dinamik türleri desteklemez, örneğin ExpandoObject.

Önemli

JSON Patch standardının doğal güvenlik riskleri vardır. Bu riskler JSON Patch standardına bağlı olduğundan, ASP.NET Core uygulaması doğal güvenlik risklerini azaltmaya çalışmaz. JSON Patch belgesinin hedef nesneye uygulanmasının güvenli olduğundan emin olmak geliştiricinin sorumluluğundadır. Daha fazla bilgi için Güvenlik Risklerini Azaltma bölümüne bakın.

JSON Düzeltme Eki desteğini System.Text.Json ile etkinleştirin

JSON Yaması desteğini System.Text.Json etkinleştirmek için Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet paketini yükleyin.

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

Bu paket, JsonPatchDocument<TModel> sınıfını, T türü nesneler için JSON Patch belgesini temsil etmek üzere ve System.Text.Json kullanılarak JSON Patch belgelerinin seri hale getirilmesi ve seri durumdan çıkarılması için özel mantığı sağlar. JsonPatchDocument<TModel> sınıfının anahtar yöntemi, düzeltme eki işlemlerini ApplyTo(Object) türündeki bir hedef nesneye uygulayan T yöntemidir.

JSON Patch uygulayan metot kodu

BIR API denetleyicisinde JSON Düzeltme Eki için bir eylem yöntemi:

Örnek Denetleyici Eylemi yöntemi:

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

Örnek uygulamadaki bu kod aşağıdakilerle Customer ve Order modelleriyle çalışır:

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

Örnek eylem yönteminin temel adımları:

  • Müşteriyi Alma:
    • yöntemi, sağlanan kimliği kullanarak veritabanından Customer bir AppDb nesne alır.
    • Hiçbir Customer nesne bulunmazsa, bir 404 Not Found yanıt döndürür.
  • JSON Düzeltme Eki Uygula:
    • ApplyTo(Object) yöntemi, patchDoc'tan JSON Patch işlemlerini alınan Customer nesneye uygular.
    • Düzeltme eki uygulaması sırasında geçersiz işlemler veya çakışmalar gibi hatalar oluşursa, bunlar bir hata işleme temsilcisi tarafından yakalanır. Bu temsilci, etkilenen nesnenin tür adını ve hata iletisini kullanarak ModelState öğesine hata iletileri ekler.
  • ModelState Doğrulama:
    • Düzeltme eki uygulandıktan sonra, yöntem ModelState üzerindeki hataları kontrol eder.
    • ModelState geçersizse, örneğin düzeltme eki hatalarından dolayı, doğrulama hatalarını içeren bir 400 Bad Request yanıt döndürür.
  • Güncelleştirilmiş Müşteriyi İade Edin:
    • Düzeltme eki başarıyla uygulanırsa ve ModelState geçerliyse, yöntem yanıttaki güncelleştirilmiş Customer nesneyi döndürür.

Örnek hata yanıtı:

Belirtilen yol geçersiz olduğunda bir JSON Patch işlemi için 400 Bad Request yanıtının gövdesini aşağıdaki örnek gösterir.

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

Nesneye JSON Düzeltme Eki belgesi uygulama

Aşağıdaki örnekler, JSON Patch belgesini bir nesneye uygulamak için ApplyTo(Object) yönteminin nasıl kullanılacağını göstermektedir.

Örnek: Bir JsonPatchDocument<TModel> nesneye uygulayın

Aşağıdaki örnekte gösterilmiştir:

  • add, replaceve remove işlemleri.
  • İç içe geçmiş özelliklerde işlemler.
  • Diziye yeni öğe ekleme.
  • JSON yama belgesinde JSON Dize Enum Dönüştürücüsü kullanma.
// 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));

Önceki örnekte güncelleştirilmiş nesnenin aşağıdaki çıkışı elde edilir:

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

ApplyTo(Object) yöntemi, işleme System.Text.Json kurallarını ve seçeneklerini, aşağıdaki seçenekler tarafından denetlenen davranış da dahil olmak üzere, JsonPatchDocument<TModel> için geçerli olan kurallar ve seçenekleri izler.

System.Text.Json ve yeni JsonPatchDocument<TModel> uygulaması arasındaki ana farklar:

  • Hedef nesnenin bildirilmiş türü değil, çalışma zamanındaki türü, hangi özellikleri ApplyTo(Object) yamanacağını belirler.
  • System.Text.Json seri durumdan çıkarma, uygun özellikleri tanımlamak için bildirilen türe dayanır.

Örnek: Hata işleme ile JsonPatchDocument'i uygulama

JSON Düzeltme Eki belgesi uygulanırken oluşabilecek çeşitli hatalar vardır. Örneğin, hedef nesne belirtilen özelliğe sahip olmayabilir veya belirtilen değer özellik türüyle uyumsuz olabilir.

JSON Patch , belirtilen bir değerin test hedef özelliğe eşit olup olmadığını denetleyen işlemi destekler. Aksi takdirde bir hata döndürür.

Aşağıdaki örnekte bu hataların düzgün bir şekilde nasıl işleneceğini gösterilmektedir.

Önemli

ApplyTo(Object) yöntemine aktarılan nesne doğrudan değiştirilir. İşlemlerden herhangi biri başarısız olursa, değişiklikleri atmak çağırıcının sorumluluğundadır.

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

Önceki örnekte aşağıdaki çıkış elde edilir:

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

Güvenlik risklerini azaltma

Paketi kullanırken Microsoft.AspNetCore.JsonPatch.SystemTextJson olası güvenlik risklerini anlamak ve azaltmak kritik önem taşır. Aşağıdaki bölümlerde JSON Düzeltme Eki ile ilişkili tanımlanan güvenlik riskleri özetlenmiştir ve paketin güvenli kullanımını sağlamak için önerilen risk azaltmaları sağlanır.

Önemli

Bu, kapsamlı bir tehdit listesi değildir. Uygulama geliştiricileri, uygulamaya özgü kapsamlı bir liste belirlemek ve gerektiğinde uygun risk azaltmaları bulmak için kendi tehdit modeli incelemelerini yapmalıdır. Örneğin, koleksiyonları düzeltme eki işlemlerine sunan uygulamalar, bu işlemlerin koleksiyonun başına öğe eklemesi veya kaldırması durumunda algoritmasal karmaşıklık saldırıları olasılığını göz önünde bulundurmalıdır.

JSON Patch işlevselliğini uygulamalarıyla tümleştirirken güvenlik risklerini en aza indirmek için geliştiriciler şunları yapmalıdır:

  • Kendi uygulamaları için kapsamlı tehdit modelleri çalıştırın.
  • Tanımlanan tehditleri ele alın.
  • Aşağıdaki bölümlerde önerilen azaltmaları izleyin.

Bellek amplifikasyonu yoluyla Hizmet Reddi (DoS)

  • Senaryo: Kötü amaçlı istemci, büyük nesne grafiklerini birden çok kez çoğaltan ve aşırı bellek tüketimine yol açan bir copy işlem gönderir.
  • Etki: Hizmet kesintilerine neden olan olası Out-Of-Memory (OOM) koşulları.
  • Hafifletme:
    • ** ApplyTo(Object) çağırmadan önce gelen JSON Patch belgelerini boyut ve yapı açısından doğrulayın.
    • Doğrulamanın uygulamaya özgü olması gerekir, ancak örnek bir doğrulama aşağıdakine benzer olabilir:
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();
    }
}

İş Mantığı Bozulması

  • Senaryo: Yama işlemleri, iş kısıtlamalarını ihlal ederek alanları örtük sabit değerlerle (örneğin, iç bayraklar, kimlikler veya hesaplanan alanlar) işleyebilir.
  • Etki: Veri bütünlüğü sorunları ve istenmeyen uygulama davranışı.
  • Hafifletme:
    • Değiştirilmesi güvenli olan açıkça tanımlanmış özelliklerle POCO'ları (Düz Eski CLR Nesneleri) kullanın.
      • Hedef nesnede hassas veya güvenlik açısından kritik özellikleri ortaya çıkarmaktan kaçının.
      • POCO nesnesi kullanılmıyorsa, işlemleri uyguladıktan sonra yaması yapılmış nesneyi doğrulayarak iş kurallarının ve değişmezlerin ihlal edilmediğinden emin olun.

Kimlik doğrulaması ve yetkilendirme

  • Senaryo: Kimliği doğrulanmamış veya yetkisiz istemciler kötü amaçlı JSON Düzeltme Eki istekleri gönderir.
  • Etki: Hassas verileri değiştirmek veya uygulama davranışını kesintiye uğratmak için yetkisiz erişim.
  • Hafifletme:
    • Uygun kimlik doğrulama ve yetkilendirme mekanizmalarıyla JSON Düzeltme Eki isteklerini kabul eden uç noktaları koruyun.
    • Erişimi güvenilen istemcilere veya uygun izinlere sahip kullanıcılara kısıtlayın.

Kodu alma

Örnek kodu görüntüleyin veya indirme. (Nasıl indirilir).

Örneği test etmek için uygulamayı çalıştırın ve aşağıdaki ayarlarla HTTP istekleri gönderin:

  • URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • HTTP yöntemi: PATCH
  • Üstbilgi: Content-Type: application/json-patch+json
  • Gövde: JSON proje klasöründeki JSON düzeltme eki belge örneklerinden birini kopyalayıp yapıştırın.

Ek kaynaklar

Bu makalede, ASP.NET Core web API'sinde JSON Düzeltme Eki isteklerinin nasıl işleneceğini açıklanmaktadır.

Önemli

JSON Patch standardının doğal güvenlik riskleri vardır. Bu uygulama , bu doğal güvenlik risklerini azaltmaya çalışmaz. JSON Patch belgesinin hedef nesneye uygulanmasının güvenli olduğundan emin olmak geliştiricinin sorumluluğundadır. Daha fazla bilgi için Güvenlik Risklerini Azaltma bölümüne bakın.

Paket yükleme

ASP.NET Core web API'sindeki JSON Düzeltme Eki desteği NuGet paketini temel alır Newtonsoft.Json ve gerektirir Microsoft.AspNetCore.Mvc.NewtonsoftJson .

JSON Düzeltme Eki desteğini etkinleştirmek için:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet paketini yükleyin.

  • AddNewtonsoftJson çağrısı yapın. Örneğin:

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

AddNewtonsoftJson, tümSystem.Text.Jsoniçin kullanılan varsayılan tabanlı giriş ve çıkış biçimlendiricilerinin yerini alır. Bu uzantı yöntemi aşağıdaki MVC hizmet kayıt yöntemleriyle uyumludur:

JsonPatch için üst bilginin Content-Typeolarak ayarlanması application/json-patch+json gerekir.

System.Text.Json kullanırken JSON Düzeltme Eki desteği ekleme

System.Text.JsonTabanlı giriş biçimlendirici JSON Düzeltme Eki'ni desteklemez. kullanarak Newtonsoft.JsonJSON Düzeltme Eki desteği eklemek ve diğer giriş ve çıkış biçimlendiricilerini değiştirmeden bırakmak için:

  • Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet paketini yükleyin.

  • Güncelleştirme 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();
        }
    }
    

Yukarıdaki kod bir örneği NewtonsoftJsonPatchInputFormatter oluşturur ve bunu koleksiyondaki MvcOptions.InputFormatters ilk girdi olarak ekler. Bu kayıt sırası aşağıdakilerin sağlanmasını sağlar:

  • NewtonsoftJsonPatchInputFormatter JSON Düzeltme Eki isteklerini işler.
  • Mevcut System.Text.Jsontabanlı giriş ve biçimlendiriciler diğer tüm JSON isteklerini ve yanıtlarını işler.

Newtonsoft.Json.JsonConvert.SerializeObject bir serileştirmek JsonPatchDocumentiçin yöntemini kullanın.

PATCH HTTP istek yöntemi

PUT ve PATCH yöntemleri, mevcut bir kaynağı güncelleştirmek için kullanılır. Aralarındaki fark, PUT'nin kaynağın tamamının yerini aldığı, PATCH'nin ise yalnızca değişiklikleri belirttiğidir.

JSON Düzeltme Eki

JSON Düzeltme Eki , bir kaynağa uygulanacak güncelleştirmeleri belirtme biçimidir. JSON Patch belgesinde bir dizi işlem vardır. Her işlem belirli bir değişiklik türünü tanımlar. Dizi öğesi ekleme veya özellik değerini değiştirme gibi değişikliklere örnek olarak verilebilir.

Örneğin, aşağıdaki JSON belgeleri bir kaynağı, kaynak için JSON Patch belgesini ve Patch işlemlerinin uygulanmasının sonucunu temsil eder.

Kaynak örneği

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

JSON düzeltme eki örneği

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

Yukarıdaki JSON kodunda:

  • op özelliği, işlemin türünü gösterir.
  • path özelliği, güncelleştirilecek öğeyi gösterir.
  • value özelliği yeni değeri sağlar.

Düzeltme eki sonrasındaki kaynak

Yukarıdaki JSON Düzeltme Eki belgesini uyguladıktan sonraki kaynak aşağıdadır:

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

Bir kaynağa JSON Düzeltme Eki belgesi uygulanarak yapılan değişiklikler atomik değerdir. Listedeki herhangi bir işlem başarısız olursa, listedeki hiçbir işlem uygulanmaz.

Yol söz dizimi

İşlem nesnesinin path özelliği düzeyler arasında eğik çizgi içerir. Örneğin, "/address/zipCode".

Dizi öğelerini belirtmek için sıfır tabanlı dizinler kullanılır. Dizinin ilk öğesi addresses konumunda /addresses/0olacaktır. Dizinin add sonuna kadar dizin numarası yerine kısa çizgi (-) kullanın: /addresses/-.

Operasyonlar

Aşağıdaki tabloda JSON Düzeltme Eki belirtiminde tanımlandığı gibi desteklenen işlemler gösterilmektedir:

İşlem Notlar
add Özellik veya dizi öğesi ekleyin. Mevcut özellik için: set value.
remove Bir özelliği veya dizi öğesini kaldırın.
replace Aynı konumda ve ardından remove aynı konumda olduğu gibiadd.
move Kaynaktan alınan değeri kullanarak kaynaktan remove hedefe kadar add olan değerle aynıdır.
copy add Kaynaktan değer kullanan hedefle aynıdır.
test = değeri sağlandıysa pathvaluebaşarı durum kodunu döndürür.

ASP.NET Core'da JSON Düzeltme Eki

JSON Patch'in ASP.NET Core uygulaması Microsoft.AspNetCore.JsonPatch NuGet paketinde sağlanır.

Eylem yöntemi kodu

BIR API denetleyicisinde JSON Düzeltme Eki için bir eylem yöntemi:

Bir örnek aşağıda verilmiştir:

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

Örnek uygulamadaki bu kod aşağıdaki Customer modelle çalışır:

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

Örnek eylem yöntemi:

  • bir Customeroluşturur.
  • Düzeltme ekini uygular.
  • Yanıtın gövdesindeki sonucu döndürür.

Gerçek bir uygulamada kod, verileri veritabanı gibi bir depodan alır ve düzeltme ekini uyguladıktan sonra veritabanını güncelleştirir.

Model durumu

Yukarıdaki eylem yöntemi örneği, model durumunu parametrelerinden biri olarak alan bir aşırı yüklemesini ApplyTo çağırır. Bu seçenekle yanıtlarda hata iletileri alabilirsiniz. Aşağıdaki örnek, bir işlem için test 400 Hatalı İstek yanıtının gövdesini gösterir:

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

Dinamik nesneler

Aşağıdaki eylem yöntemi örneği, bir dinamik nesneye düzeltme ekinin nasıl uygulanacağını gösterir:

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

    return Ok(obj);
}

Ekleme işlemi

  • Bir dizi öğesine işaret ederse path : tarafından pathbelirtilen öğeden önce yeni öğe ekler.
  • Bir özelliğe işaret ederse path : özellik değerini ayarlar.
  • Var olmayan bir konuma işaret ederse path :
    • Düzeltme eki uygulama kaynağı dinamik bir nesneyse: bir özellik ekler.
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse, istek başarısız olur.

Aşağıdaki örnek düzeltme eki belgesi değerini CustomerName ayarlar ve dizinin sonuna Order bir Orders nesne ekler.

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

Kaldırma işlemi

  • Bir dizi öğesine işaret ederse path : öğesini kaldırır.
  • Bir özelliğe işaret ederse path :
    • Düzeltme eki uygulanacak kaynak dinamik bir nesneyse: özelliği kaldırır.
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse:
      • Özellik null atanabilirse: null olarak ayarlar.
      • Özelliği null atanamazsa olarak ayarlar default<T>.

Aşağıdaki örnek düzeltme eki belge null olarak ayarlanır CustomerName ve silinir Orders[0]:

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

Değiştirme işlemi

Bu işlem işlevsel olarak bir remove ile aynıdır ve ardından bir ile aynıdır add.

Aşağıdaki örnek düzeltme eki belgesi değerini CustomerName ayarlar ve değerini yeni Orders[0] bir nesneyle değiştirirOrder:

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

Taşıma işlemi

  • Bir dizi öğesine işaret edersepath: öğeyi öğenin konumuna from kopyalarpath, ardından öğesinde remove bir from işlem çalıştırır.
  • Bir özelliğe işaret edersepath: özelliğin frompath değerini özelliğe kopyalar, ardından özelliğinde remove bir from işlem çalıştırır.
  • Var olmayan bir özelliğe işaret ederse path :
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse, istek başarısız olur.
    • Düzeltme eki uygulanacak kaynak dinamik bir nesneyse: özelliği tarafından frombelirtilen konuma kopyalar path ve özelliği üzerinde remove bir from işlem çalıştırır.

Aşağıdaki örnek düzeltme eki belgesi:

  • değerini Orders[0].OrderName olarak CustomerNamekopyalar.
  • Null olarak ayarlanır Orders[0].OrderName .
  • öncesine Orders[1]giderOrders[0].
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Kopyalama işlemi

Bu işlem işlevsel olarak son move adım olmadan bir remove işlemle aynıdır.

Aşağıdaki örnek düzeltme eki belgesi:

  • değerini Orders[0].OrderName olarak CustomerNamekopyalar.
  • öncesinin Orders[1]Orders[0]bir kopyasını ekler.
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Test işlemi

tarafından path belirtilen konumdaki değer, içinde valuesağlanan değerden farklıysa istek başarısız olur. Bu durumda düzeltme eki belgesindeki diğer tüm işlemler başarılı olsa bile PATCH isteğinin tamamı başarısız olur.

İşlem test genellikle eşzamanlılık çakışması olduğunda güncelleştirme yapılmasını önlemek için kullanılır.

Aşağıdaki örnek düzeltme eki belgesinin ilk değeri CustomerName "John" ise hiçbir etkisi yoktur, çünkü test başarısız olur:

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

Kodu alma

Örnek kodu görüntüleyin veya indirme. (Nasıl indirilir).

Örneği test etmek için uygulamayı çalıştırın ve aşağıdaki ayarlarla HTTP istekleri gönderin:

  • URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • HTTP yöntemi: PATCH
  • Üstbilgi: Content-Type: application/json-patch+json
  • Gövde: JSON proje klasöründeki JSON düzeltme eki belge örneklerinden birini kopyalayıp yapıştırın.

Güvenlik risklerini azaltma

Microsoft.AspNetCore.JsonPatch paketini Newtonsoft.Json tabanlı uygulamayla kullanırken olası güvenlik risklerini anlamak ve azaltmak kritik önem taşır. Aşağıdaki bölümlerde JSON Düzeltme Eki ile ilişkili tanımlanan güvenlik riskleri özetlenmiştir ve paketin güvenli kullanımını sağlamak için önerilen risk azaltmaları sağlanır.

Önemli

Bu, kapsamlı bir tehdit listesi değildir. Uygulama geliştiricileri, uygulamaya özgü kapsamlı bir liste belirlemek ve gerektiğinde uygun risk azaltmaları bulmak için kendi tehdit modeli incelemelerini yapmalıdır. Örneğin, koleksiyonları düzeltme eki işlemlerine sunan uygulamalar, bu işlemlerin koleksiyonun başına öğe eklemesi veya kaldırması durumunda algoritmasal karmaşıklık saldırıları olasılığını göz önünde bulundurmalıdır.

Bu paketlerin tüketicileri, kendi uygulamaları için kapsamlı tehdit modelleri çalıştırarak ve tanımlanan tehditleri ele alırken aşağıdaki önerilen azaltmaları izleyerek, güvenlik risklerini en aza indirirken JSON Patch işlevselliğini uygulamalarıyla tümleştirebilir.

Bellek amplifikasyonu yoluyla Hizmet Reddi (DoS)

  • Senaryo: Kötü amaçlı istemci, büyük nesne grafiklerini birden çok kez çoğaltan ve aşırı bellek tüketimine yol açan bir copy işlem gönderir.
  • Etki: Hizmet kesintilerine neden olan olası Out-Of-Memory (OOM) koşulları.
  • Hafifletme:
    • ** ApplyTo çağırmadan önce gelen JSON Patch belgelerini boyut ve yapı açısından doğrulayın.
    • Doğrulamanın uygulamaya özgü olması gerekir, ancak örnek bir doğrulama aşağıdakine benzer olabilir:
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();
    }
}

İş Mantığı Bozulması

  • Senaryo: Yama işlemleri, iş kısıtlamalarını ihlal ederek alanları örtük sabit değerlerle (örneğin, iç bayraklar, kimlikler veya hesaplanan alanlar) işleyebilir.
  • Etki: Veri bütünlüğü sorunları ve istenmeyen uygulama davranışı.
  • Hafifletme:
    • Değiştirilmesi güvenli olan açıkça tanımlanmış özelliklere sahip POCO nesnelerini kullanın.
    • Hedef nesnede hassas veya güvenlik açısından kritik özellikleri ortaya çıkarmaktan kaçının.
    • PoCO nesnesi kullanılmazsa, iş kurallarının ve sabitlerin ihlal edilmediğinden emin olmak için işlemler uygulandıktan sonra değişiklik yapılan nesneyi doğrulayın.

Kimlik doğrulaması ve yetkilendirme

  • Senaryo: Kimliği doğrulanmamış veya yetkisiz istemciler kötü amaçlı JSON Düzeltme Eki istekleri gönderir.
  • Etki: Hassas verileri değiştirmek veya uygulama davranışını kesintiye uğratmak için yetkisiz erişim.
  • Hafifletme:
    • Uygun kimlik doğrulama ve yetkilendirme mekanizmalarıyla JSON Düzeltme Eki isteklerini kabul eden uç noktaları koruyun.
    • Erişimi güvenilen istemcilere veya uygun izinlere sahip kullanıcılara kısıtlayın.

Ek kaynaklar

Bu makalede, ASP.NET Core web API'sinde JSON Düzeltme Eki isteklerinin nasıl işleneceğini açıklanmaktadır.

Önemli

JSON Patch standardının doğal güvenlik riskleri vardır. Bu riskler JSON Düzeltme Eki standardına bağlı olduğundan, bu uygulama doğal güvenlik risklerini azaltmaya çalışmaz. JSON Patch belgesinin hedef nesneye uygulanmasının güvenli olduğundan emin olmak geliştiricinin sorumluluğundadır. Daha fazla bilgi için Güvenlik Risklerini Azaltma bölümüne bakın.

Paket yükleme

Uygulamanızda JSON Düzeltme Eki desteğini etkinleştirmek için aşağıdaki adımları tamamlayın:

  1. Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet paketini yükleyin.

  2. projenin Startup.ConfigureServices yöntemini çağıracak AddNewtonsoftJsonşekilde güncelleştirin. Örneğin:

    services
        .AddControllersWithViews()
        .AddNewtonsoftJson();
    

AddNewtonsoftJson MVC hizmet kayıt yöntemleriyle uyumludur:

JSON Patch, AddNewtonsoftJson ve System.Text.Json

AddNewtonsoftJson System.Text.Json, tüm JSON içeriğini biçimlendirmek için kullanılan tabanlı giriş ve çıkış biçimlendiricilerinin yerini alır. kullanarak Newtonsoft.JsonJSON Düzeltme Eki desteği eklemek ve diğer biçimlendiricileri değiştirmeden bırakmak için projenin Startup.ConfigureServices yöntemini aşağıdaki gibi güncelleştirin:

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

Yukarıdaki kod için paket ve aşağıdaki Microsoft.AspNetCore.Mvc.NewtonsoftJson deyimler gerekirusing:

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 JsonPatchDocument'ı seri hale getirmek için yöntemini kullanın.

PATCH HTTP istek yöntemi

PUT ve PATCH yöntemleri, mevcut bir kaynağı güncelleştirmek için kullanılır. Aralarındaki fark, PUT'nin kaynağın tamamının yerini aldığı, PATCH'nin ise yalnızca değişiklikleri belirttiğidir.

JSON Düzeltme Eki

JSON Düzeltme Eki , bir kaynağa uygulanacak güncelleştirmeleri belirtme biçimidir. JSON Patch belgesinde bir dizi işlem vardır. Her işlem belirli bir değişiklik türünü tanımlar. Dizi öğesi ekleme veya özellik değerini değiştirme gibi değişikliklere örnek olarak verilebilir.

Örneğin, aşağıdaki JSON belgeleri bir kaynağı, kaynak için JSON Patch belgesini ve Patch işlemlerinin uygulanmasının sonucunu temsil eder.

Kaynak örneği

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

JSON düzeltme eki örneği

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

Yukarıdaki JSON kodunda:

  • op özelliği, işlemin türünü gösterir.
  • path özelliği, güncelleştirilecek öğeyi gösterir.
  • value özelliği yeni değeri sağlar.

Düzeltme eki sonrasındaki kaynak

Yukarıdaki JSON Düzeltme Eki belgesini uyguladıktan sonraki kaynak aşağıdadır:

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

Bir kaynağa JSON Düzeltme Eki belgesi uygulanarak yapılan değişiklikler atomik değerdir. Listedeki herhangi bir işlem başarısız olursa, listedeki hiçbir işlem uygulanmaz.

Yol söz dizimi

İşlem nesnesinin path özelliği düzeyler arasında eğik çizgi içerir. Örneğin, "/address/zipCode".

Dizi öğelerini belirtmek için sıfır tabanlı dizinler kullanılır. Dizinin ilk öğesi addresses konumunda /addresses/0olacaktır. Dizinin add sonuna kadar dizin numarası yerine kısa çizgi (-) kullanın: /addresses/-.

Operasyonlar

Aşağıdaki tabloda JSON Düzeltme Eki belirtiminde tanımlandığı gibi desteklenen işlemler gösterilmektedir:

İşlem Notlar
add Özellik veya dizi öğesi ekleyin. Mevcut özellik için: set value.
remove Bir özelliği veya dizi öğesini kaldırın.
replace Aynı konumda ve ardından remove aynı konumda olduğu gibiadd.
move Kaynaktan alınan değeri kullanarak kaynaktan remove hedefe kadar add olan değerle aynıdır.
copy add Kaynaktan değer kullanan hedefle aynıdır.
test = değeri sağlandıysa pathvaluebaşarı durum kodunu döndürür.

ASP.NET Core'da JSON Düzeltme Eki

JSON Patch'in ASP.NET Core uygulaması Microsoft.AspNetCore.JsonPatch NuGet paketinde sağlanır.

Eylem yöntemi kodu

BIR API denetleyicisinde JSON Düzeltme Eki için bir eylem yöntemi:

  • özniteliğiyle HttpPatch ek açıklama eklenir.
  • genellikle ile bir JsonPatchDocument<T>[FromBody]kabul eder.
  • Değişiklikleri uygulamak için düzeltme eki belgesine çağrılar ApplyTo .

Bir örnek aşağıda verilmiştir:

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

Örnek uygulamadaki bu kod aşağıdaki Customer modelle çalışır:

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

Örnek eylem yöntemi:

  • bir Customeroluşturur.
  • Düzeltme ekini uygular.
  • Yanıtın gövdesindeki sonucu döndürür.

Gerçek bir uygulamada kod, verileri veritabanı gibi bir depodan alır ve düzeltme ekini uyguladıktan sonra veritabanını güncelleştirir.

Model durumu

Yukarıdaki eylem yöntemi örneği, model durumunu parametrelerinden biri olarak alan bir aşırı yüklemesini ApplyTo çağırır. Bu seçenekle yanıtlarda hata iletileri alabilirsiniz. Aşağıdaki örnek, bir işlem için test 400 Hatalı İstek yanıtının gövdesini gösterir:

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

Dinamik nesneler

Aşağıdaki eylem yöntemi örneği, bir dinamik nesneye düzeltme ekinin nasıl uygulanacağını gösterir:

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

    return Ok(obj);
}

Ekleme işlemi

  • Bir dizi öğesine işaret ederse path : tarafından pathbelirtilen öğeden önce yeni öğe ekler.
  • Bir özelliğe işaret ederse path : özellik değerini ayarlar.
  • Var olmayan bir konuma işaret ederse path :
    • Düzeltme eki uygulama kaynağı dinamik bir nesneyse: bir özellik ekler.
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse, istek başarısız olur.

Aşağıdaki örnek düzeltme eki belgesi değerini CustomerName ayarlar ve dizinin sonuna Order bir Orders nesne ekler.

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

Kaldırma işlemi

  • Bir dizi öğesine işaret ederse path : öğesini kaldırır.
  • Bir özelliğe işaret ederse path :
    • Düzeltme eki uygulanacak kaynak dinamik bir nesneyse: özelliği kaldırır.
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse:
      • Özellik null atanabilirse: null olarak ayarlar.
      • Özelliği null atanamazsa olarak ayarlar default<T>.

Aşağıdaki örnek düzeltme eki belge null olarak ayarlanır CustomerName ve silinir Orders[0]:

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

Değiştirme işlemi

Bu işlem işlevsel olarak bir remove ile aynıdır ve ardından bir ile aynıdır add.

Aşağıdaki örnek düzeltme eki belgesi değerini CustomerName ayarlar ve değerini yeni Orders[0] bir nesneyle değiştirirOrder:

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

Taşıma işlemi

  • Bir dizi öğesine işaret edersepath: öğeyi öğenin konumuna from kopyalarpath, ardından öğesinde remove bir from işlem çalıştırır.
  • Bir özelliğe işaret edersepath: özelliğin frompath değerini özelliğe kopyalar, ardından özelliğinde remove bir from işlem çalıştırır.
  • Var olmayan bir özelliğe işaret ederse path :
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse, istek başarısız olur.
    • Düzeltme eki uygulanacak kaynak dinamik bir nesneyse: özelliği tarafından frombelirtilen konuma kopyalar path ve özelliği üzerinde remove bir from işlem çalıştırır.

Aşağıdaki örnek düzeltme eki belgesi:

  • değerini Orders[0].OrderName olarak CustomerNamekopyalar.
  • Null olarak ayarlanır Orders[0].OrderName .
  • öncesine Orders[1]giderOrders[0].
[
  {
    "op": "move",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "move",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Kopyalama işlemi

Bu işlem işlevsel olarak son move adım olmadan bir remove işlemle aynıdır.

Aşağıdaki örnek düzeltme eki belgesi:

  • değerini Orders[0].OrderName olarak CustomerNamekopyalar.
  • öncesinin Orders[1]Orders[0]bir kopyasını ekler.
[
  {
    "op": "copy",
    "from": "/orders/0/orderName",
    "path": "/customerName"
  },
  {
    "op": "copy",
    "from": "/orders/1",
    "path": "/orders/0"
  }
]

Test işlemi

tarafından path belirtilen konumdaki değer, içinde valuesağlanan değerden farklıysa istek başarısız olur. Bu durumda düzeltme eki belgesindeki diğer tüm işlemler başarılı olsa bile PATCH isteğinin tamamı başarısız olur.

İşlem test genellikle eşzamanlılık çakışması olduğunda güncelleştirme yapılmasını önlemek için kullanılır.

Aşağıdaki örnek düzeltme eki belgesinin ilk değeri CustomerName "John" ise hiçbir etkisi yoktur, çünkü test başarısız olur:

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

Kodu alma

Örnek kodu görüntüleyin veya indirme. (Nasıl indirilir).

Örneği test etmek için uygulamayı çalıştırın ve aşağıdaki ayarlarla HTTP istekleri gönderin:

  • URL: http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
  • HTTP yöntemi: PATCH
  • Üstbilgi: Content-Type: application/json-patch+json
  • Gövde: JSON proje klasöründeki JSON düzeltme eki belge örneklerinden birini kopyalayıp yapıştırın.