İçeriğe atla

ASP.NET Core 6/Web Servisleri/HTTP Patch Metodu Desteği

Vikikitap, özgür kütüphane

Daha önce güncelleme işlemlerini PUT metoduyla yaptık. Ancak PUT metodu esasında var olan nesneyi silme, yerine yeni bir nesne koyma mantığında çalışır. Ancak PUT metodu her zaman uygulanabilir değildir. Bazen bir nesnenin çok fazla özelliği olabilir. Nesneyi tamamen silip yeni baştan üretmek maliyetli olabilir. Veya istemci nesnenin sadece bazı özelliklerini almıştır, diğer özelliklerinden haberi yoktur, ve aldığı özelliklerden birini değiştirmiştir, ve değiştirdiği özelliği geri veritabanına yazmak istiyordur. Böyle bir senaryoda ciddi bir sorun oluşur. Böyle bir durumda istemcinin sağlamadığı özellikler mecburen null olacaktır veya güncelleme girişi doğrulama sürecine takılacak ve hiç güncelleme yapılmayacaktır. Çözüm sunucuya PATCH isteği göndermektir. PATCH isteğinde istemci bir nesnenin sadece değişen özelliklerini gönderir, ilgili nesnenin değişen özellikleri güncellenir, veritabanındaki diğer özelliklere dokunulmaz.

JSON'da PATCH'in verilerinin belirtilmesi

[değiştir]

JSON, PATCH işleminin nasıl yapılacağını belirtmek için standart bir sözdizimi sağlamaktadır. Örnek:

[
    { "op": "replace", "path": "Name", "value": "Surf Co"},
    { "op": "replace", "path": "City", "value": “Los Angeles”}
]

Bu JSON verisi Supplier sınıfının iki özelliği için yeni değer belirtmektedir. Özellikler Name ve City'dir, bu özellikler için verilecek yeni değerler ise "Surf Co" ve "Los Angeles" olarak belirtilmiştir. PATCH metoduyla talebi alacak olan action, ilgili Supplier'ın id'sini rota değişeninden alacak, ilgili id'li supplier'ın Name ve City özelliklerini belirtilen değerlerle değiştirecek, ilgili supplier'ın diğer özelliklerine dokunmayacaktır.

PATCH desteğini ekleme

[değiştir]

ASP.NET Core Empty şablonuyla yeni bir proje oluştuduğumuzda varsayılan durumda PATCH desteği olmaz. Uygulamamıza PATCH desteğini eklemek için NuGet Paket Yöneticinden Microsoft.AspNetCore.Mvc.NewtonsoftJson paketinin 6.0.0 versiyonunu kurmalıyız. İlgili paketi kurduktan sonra Program.cs dosyamızı şöyle güncelleyelim:

using Microsoft.EntityFrameworkCore;
using WebApp.Models;
using Microsoft.AspNetCore.Mvc;
//using System.Text.Json.Serialization;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<DataContext>(opts => {
    opts.UseSqlServer(builder.Configuration["ConnectionStrings:ProductConnection"]);
    opts.EnableSensitiveDataLogging(true);
});
builder.Services.AddControllers().AddNewtonsoftJson();
builder.Services.Configure<MvcNewtonsoftJsonOptions>(opts => {
    opts.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});
//builder.Services.Configure<JsonOptions>(opts => {
// opts.JsonSerializerOptions.DefaultIgnoreCondition
// = JsonIgnoreCondition.WhenWritingNull;
//});
var app = builder.Build();
app.MapControllers();
app.MapGet("/", () => "Hello World!");
var context = app.Services.CreateScope().ServiceProvider.GetRequiredService<DataContext>();
SeedData.SeedDatabase(context);
app.Run();

Bu program JSON'a serileştirme için standart kütüphane yerine JSON.NET serileştiricisini kullanmaktadır. Ayrıca ilgili serileştirici options pattern ile konfigüre edilmiş ve değeri null olan özelliklerin üretilen JSON'a dahil edilmemesi sağlanmıştır.

Action metodunun eklenmesi

[değiştir]

Şimdi SuppliersController sınıfını şöyle değiştirelim:

using Microsoft.AspNetCore.Mvc;
using WebApp.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.JsonPatch;
namespace WebApp.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class SuppliersController : ControllerBase
    {
        private DataContext context;
        public SuppliersController(DataContext ctx)
        {
            context = ctx;
        }
        [HttpGet("{id}")]
        public async Task<Supplier> GetSupplier(long id)
        {
            Supplier supplier = await context.Suppliers.Include(s => s.Products).FirstAsync(s => s.SupplierId == id);
            if (supplier.Products != null)
            {
                foreach (Product p in supplier.Products)
                {
                    p.Supplier = null;
                };
            }
            return supplier;
        }
        [HttpPatch("{id}")]
        public async Task<Supplier> PatchSupplier(long id, JsonPatchDocument<Supplier> patchDoc)
        {
            Supplier s = await context.Suppliers.FindAsync(id);
            if (s != null)
            {
                patchDoc.ApplyTo(s);
                await context.SaveChangesAsync();
            }
            return s;
        }
    }
}

Kod son derece basittir. PatchSupplier() action'ı ilgili supplier'ın id'sini rotadan almaktadır. Daha sonra bu id'li supplier'ı veritabanından getirmekte, gelen request'in gövdesinde JSON formatında dersin başında belirttiğimiz şekilde gönderilen değişiklikleri patchDoc nesnesi içinde tutmakta, daha sonra bu değişiklikleri ilgili Supplier nesnesine uygulamakta, sonra değiştirilmiş nesneyi geri veritabanına yazmaktadır. Ayrıca teyit amacıyla değiştirilmiş nesneyi olduğu gibi geri döndürmektedir.