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'sindeki JSON Düzeltme Eki desteği, .NET 10'dan başlayarak System.Text.Json serileştirmeyi temel alır. Microsoft.AspNetCore.JsonPatch, System.Text.Json serileştirmesine dayalı olarak uygulanır. Bu özellik:

Note

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

Important

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

Bu paket, JsonPatchDocument<TModel> sınıfını, TModel 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 TModel yöntemidir.

JSON Patch uygulayan Minimal API PATCH uç noktası

Minimal API'de JSON Patch için PATCH uç noktası:

Örnek Minimal API PATCH uç noktası

group.MapPatch("/{id}", async Task<Results<Ok<Customer>,ValidationProblem,NotFound<ProblemDetails>>> (AppDb db, string id,
    JsonPatchDocument<Customer> patchDoc) =>
{
    var customer = await db.Customers.Include(c => c.Orders).FirstOrDefaultAsync(c => c.Id == id);
    if (customer is null)
    {
        return TypedResults.NotFound<ProblemDetails>(new ());
    }
    if (patchDoc != null)
    {
        Dictionary<string, string[]>? errors = null;
        patchDoc.ApplyTo(customer, 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)
        {
            return TypedResults.ValidationProblem(errors);
        }

        // Only save if there are no errors
        await db.SaveChangesAsync();
    }

    return TypedResults.Ok(customer);
})
.Accepts<JsonPatchDocument<Customer>>("application/json-patch+json");

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

using System.ComponentModel.DataAnnotations;

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 PATCH uç noktasının temel adımları:

  • Müşteriyi Alma:
    • Uç nokta, sağlanan Customeröğesini kullanarak veritabanından AppDb bir id nesne alır.
    • Hiçbir Customer nesnesi bulunmazsa, TypedResults.NotFound() aracılığıyla 404 Not Found yanıtı döndürür.
  • JSON Düzeltme Eki Uygula:
    • ApplyTo(Object) yöntemi, patchDoc içindeki JSON Patch işlemlerini alınan Customer nesnesine uygular.
    • Düzeltme eki uygulaması sırasında geçersiz işlemler veya çakışmalar gibi hatalar oluşursa, hata işleme temsilcisi bunları yakalar. Bu temsilci, hata iletilerini etkilenen nesnenin tür adıyla anahtarlanmış bir sözlükte toplar.
  • Doğrulama hatalarını döndürme:
    • Hata işleme temsilcisi düzeltme eki uygulaması sırasında hataları yakalarsa, uç nokta aracılığıyla ValidationProblemhata ayrıntılarını içeren bir TypedResults.ValidationProblem(errors) yanıt döndürür.
  • Güncelleştirilmiş Müşteriyi kaydedin ve iade edin:
    • Düzeltme eki hata olmadan başarıyla uygulanırsa, değişiklikler veritabanına kaydedilir ve uç nokta aracılığıyla Customergüncelleştirilmiş TypedResults.Ok(customer) nesneyi döndürür.

Örnek hata yanıtı

Aşağıdaki örnek, belirtilen yol geçersiz olduğunda JSON Patch işlemi için doğrulama sorunu yanıtının gövdesini gösterir:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "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 çeşitli hatalar oluşabilir. Ö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.

Important

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.

Important

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ı.
  • Mitigation:
    • ** 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ığının kötüye kullanılması

  • 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ışı.
  • Mitigation:
    • 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.
  • Mitigation:
    • Uygun kimlik doğrulama ve yetkilendirme mekanizmalarını kullanarak 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 alın

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

Örneği test etmek için uygulamayı çalıştırın ve eklenen .http dosyayı kullanarak HTTP istekleri gönderin.

Ek kaynaklar

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

Important

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 Patch desteği, Newtonsoft.Json öğesine dayanır ve Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet paketini gerektirir.

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üm JSON içeriğini biçimlendirmek için kullanılan varsayılan System.Text.Json tabanlı giriş ve çıkış biçimlendiricilerinin yerini alır. Bu uzantı yöntemi aşağıdaki MVC hizmet kayıt yöntemleriyle uyumludur:

JsonPatch, Content-Type üst bilgisinin application/json-patch+json olarak ayarlanmasını gerektirir.

System.Text.Json kullanırken JSON Patch desteği ekleyin

System.Text.Json tabanlı girdi biçimlendiricisi JSON Patch'i desteklemez. Newtonsoft.Json kullanarak JSON Patch 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 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 Patch isteklerini işler.
  • Mevcut System.Text.Jsontabanlı giriş ve biçimlendiriciler diğer tüm JSON isteklerini ve yanıtlarını işler.

JsonPatchDocument öğesini serileştirmek için Newtonsoft.Json.JsonConvert.SerializeObject 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 Patch

JSON Patch, bir kaynağa uygulanacak güncelleştirmeleri belirtmek için kullanılan bir biçimdir. 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.

Yama sonrası 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 Patch belgesi uygulanarak yapılan değişiklikler atomiktir. Listedeki herhangi bir işlem başarısız olursa, listedeki hiçbir işlem uygulanmaz.

Yol söz dizimi

İşlem nesnesinin path özelliğinde düzeyleri ayıran eğik çizgiler bulunur. Ö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/-.

Operations

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

Operation Notes
add Özellik veya dizi öğesi ekleyin. Mevcut özellik için: değeri ayarlayın.
remove Bir özelliği veya dizi öğesini kaldırın.
replace remove ile aynı, ardından aynı konumda add gelir.
move Kaynaktan remove ile aynı; ardından, kaynaktan alınan değer kullanılarak hedefe add.
copy Kaynakta bulunan değeri kullanarak hedefe add ile aynıdır.
test path konumundaki değer, verilen value değerine eşitse başarı durum kodunu döndürür.

ASP.NET Core’da JSON Patch

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

Eylem yöntemi kodu

Bir API denetleyicisinde JSON Patch için bir eylem metodu:

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.
  • Yamayı 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, parametrelerinden biri olarak model durumunu alan ApplyTo aşırı yüklemelerinden birini çağırır. Bu seçenekle yanıtlarda hata iletileri alabilirsiniz. Aşağıdaki örnek, test işlemi için 400 Hatalı İstek yanıt 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 :
    • Yama uygulanacak kaynak dinamik bir nesneyse: bir özellik ekler.
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse, istek başarısız olur.

Aşağıdaki örnek yama belgesi, CustomerName değerini ayarlar ve Orders dizisinin sonuna bir Order nesnesi 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 :
    • Yamalanacak kaynak dinamik bir nesneyse: özellik kaldırılır.
    • Yama uygulanacak kaynak statik bir nesneyse:
      • Özellik null olabilir durumdaysa: null olarak ayarlar.
      • Özellik null atanamaz ise, onu default<T> değerine ayarlar.

Aşağıdaki örnek yama belgesi, CustomerName öğesini null olarak ayarlar ve Orders[0] öğesini siler:

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

Değiştirme işlemi

Bu işlem, işlevsel olarak, bir remove işleminin ardından bir add işlemi gerçekleştirmekle aynıdır.

Aşağıdaki örnek yama belgesi, CustomerName değerini ayarlar ve Orders[0] öğesini yeni bir Order nesnesiyle değiştirir:

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

Taşıma işlemi

  • path bir dizi öğesine işaret ediyorsa: from öğesini path öğesinin konumuna kopyalar, ardından from öğesi üzerinde bir remove işlemi yürütür.
  • Eğer path bir özelliğe işaret ediyorsa: from özelliğinin değerini path özelliğine kopyalar, ardından from özelliği üzerinde remove işlemi ç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.
    • Yama uygulanacak kaynak dinamik bir nesneyse: from özelliğini, path tarafından belirtilen konuma kopyalar; ardından from özelliği üzerinde bir remove işlemi çalıştırır.

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

  • Orders[0].OrderName değerini CustomerName öğesine kopyalar.
  • Orders[0].OrderName'ı null olarak ayarlar.
  • Orders[1] öğesini Orders[0] öğesinden önce taşır.
[
  {
    "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:

  • Orders[0].OrderName değerini CustomerName konumuna kopyalar.
  • Orders[1] öğesinin bir kopyasını Orders[0] öğesinden önce 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 alın

Ö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.

Important

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ı.
  • Mitigation:
    • ** 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ışı.
  • Mitigation:
    • 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.
  • Mitigation:
    • 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.

Important

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, AddNewtonsoftJson çağıracak ş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. Newtonsoft.Json kullanarak JSON Patch 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, Microsoft.AspNetCore.Mvc.NewtonsoftJson paketini ve aşağıdaki using deyimlerini gerektirir:

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 Patch

JSON Patch, bir kaynağa uygulanacak güncelleştirmeleri belirtmek için kullanılan bir biçimdir. 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.

Yama sonrası 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 Patch belgesi uygulanarak yapılan değişiklikler atomiktir. Listedeki herhangi bir işlem başarısız olursa, listedeki hiçbir işlem uygulanmaz.

Yol söz dizimi

İşlem nesnesinin path özelliğinde düzeyleri ayıran eğik çizgiler bulunur. Ö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/-.

Operations

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

Operation Notes
add Özellik veya dizi öğesi ekleyin. Mevcut özellik için: değeri ayarlayın.
remove Bir özelliği veya dizi öğesini kaldırın.
replace remove ile aynı, ardından aynı konumda add gelir.
move Kaynaktan remove ile aynı; ardından, kaynaktan alınan değer kullanılarak hedefe add.
copy Kaynakta bulunan değeri kullanarak hedefe add ile aynıdır.
test path konumundaki değer, verilen value değerine eşitse başarı durum kodunu döndürür.

ASP.NET Core’da JSON Patch

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

Eylem yöntemi kodu

Bir API denetleyicisinde JSON Patch için bir eylem metodu:

  • HttpPatch özniteliği ile ek açıklama eklenir.
  • Genellikle [FromBody] ile birlikte bir JsonPatchDocument<T> kabul eder.
  • Değişiklikleri uygulamak için yama belgesinde ApplyTo çağırır.

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.
  • Yamayı 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, parametrelerinden biri olarak model durumunu alan ApplyTo aşırı yüklemelerinden birini çağırır. Bu seçenekle yanıtlarda hata iletileri alabilirsiniz. Aşağıdaki örnek, test işlemi için 400 Hatalı İstek yanıt 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 :
    • Yama uygulanacak kaynak dinamik bir nesneyse: bir özellik ekler.
    • Düzeltme eki uygulanacak kaynak statik bir nesneyse, istek başarısız olur.

Aşağıdaki örnek yama belgesi, CustomerName değerini ayarlar ve Orders dizisinin sonuna bir Order nesnesi 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 :
    • Yamalanacak kaynak dinamik bir nesneyse: özellik kaldırılır.
    • Yama uygulanacak kaynak statik bir nesneyse:
      • Özellik null olabilir durumdaysa: null olarak ayarlar.
      • Özellik null atanamaz ise, onu default<T> değerine ayarlar.

Aşağıdaki örnek yama belgesi, CustomerName öğesini null olarak ayarlar ve Orders[0] öğesini siler:

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

Değiştirme işlemi

Bu işlem, işlevsel olarak, bir remove işleminin ardından bir add işlemi gerçekleştirmekle aynıdır.

Aşağıdaki örnek yama belgesi, CustomerName değerini ayarlar ve Orders[0] öğesini yeni bir Order nesnesiyle değiştirir:

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

Taşıma işlemi

  • path bir dizi öğesine işaret ediyorsa: from öğesini path öğesinin konumuna kopyalar, ardından from öğesi üzerinde bir remove işlemi yürütür.
  • Eğer path bir özelliğe işaret ediyorsa: from özelliğinin değerini path özelliğine kopyalar, ardından from özelliği üzerinde remove işlemi ç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.
    • Yama uygulanacak kaynak dinamik bir nesneyse: from özelliğini, path tarafından belirtilen konuma kopyalar; ardından from özelliği üzerinde bir remove işlemi çalıştırır.

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

  • Orders[0].OrderName değerini CustomerName konumuna kopyalar.
  • Orders[0].OrderName'ı null olarak ayarlar.
  • Orders[1] öğesini Orders[0] öğesinden önce taşır.
[
  {
    "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:

  • Orders[0].OrderName değerini CustomerName konumuna kopyalar.
  • Orders[1] öğesinin bir kopyasını Orders[0] öğesinden önce 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 alın

Ö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.