.NET 11 Preview 6: C# Union'ları Tüm Stack'te, CSRF Koruması Varsayılan Olarak Açık
C# union'ları artık ek iskelet kod gerektirmeden derleniyor ve System.Text.Json, ASP.NET Core ile OpenAPI boyunca destekleniyor. Otomatik CSRF koruması ise uygulamaların hangi cross-origin istekleri reddedeceğini değiştiriyor.
Umut Korkmaz
Yayın tarihi: 4 dk okuma
Bu sayfada6
Microsoft, 14 Temmuz 2026'da .NET 11 Preview 6 sürümünü yayınladı. Çok sayıda .NET API kodu review eden biri olarak iki başlık özellikle dikkatimi çekti. C# union'ları artık elle yazılmış derleyici iskeletine ihtiyaç duymuyor ve ASP.NET Core, cross-origin (CSRF) korumasını artık varsayılan olarak açıyor. İlki sonuçları nasıl modellediğimizi değiştiriyor. İkincisi ise uygulamanın kendi kodunda hiçbir değişiklik olmadan neyi reddettiğini değiştirebilir.
İskelet kod olmadan union'lar
Preview 5, union tanımlarını ve union pattern'lerini getirmişti ama derleyicinin ihtiyaç duyduğu destek tiplerini (UnionAttribute ve IUnion) kendiniz yazmanız gerekiyordu. C# sürüm notlarına göre Preview 6 bunları System.Runtime.CompilerServices içinde hazır getiriyor; yani public union Pet(Dog, Cat); hiçbir ek şey yazmadan derleniyor. Bilinmesi gereken bir kural değişikliği de var: not pattern'i artık içindeki case'e değil, union değerinin kendisine uygulanıyor.
Dikkat edilmesi gereken nokta: union'lar hâlâ <LangVersion>preview</LangVersion> arkasında duran bir preview özelliği ve notlar, API yüzeyinin değişmeye devam edeceğini beklemek gerektiğini söylüyor. Bu sürümle gelen ve .NET 10'daki extension members özelliğini tamamlayan extension indexer'lar da aynı preview bayrağının arkasında.
İmzadan okunabilen sonuçlar
Beklenen hata durumları için bugün kodun büyük kısmı ya exception fırlatıyor ya da nullable bir hata alanı taşıyan, elle yazılmış bir Result<T> döndürüyor. Union ise imzanın tam olarak neyin dönebileceğini söylemesini sağlıyor. Aşağıdaki örnek, sürüm notlarındaki söz dizimiyle yazılmış bir taslak, production kodu değil:
public record class OrderPlaced(int Id);
public record class OutOfStock(string Sku);
public record class InvalidOrder(string[] Errors);
public union PlaceOrderResult(OrderPlaced, OutOfStock, InvalidOrder);
static IResult ToHttp(PlaceOrderResult result) => result switch
{
OrderPlaced(var id) => TypedResults.Created($"/orders/{id}"),
OutOfStock(var sku) => TypedResults.Conflict(sku),
InvalidOrder(var errors) => TypedResults.BadRequest(errors)
};
Beni ikna eden şey, stack'in ne kadarının bunu şimdiden anlıyor olması. System.Text.Json aktif case'i doğrudan serialize ediyor; yani Dog("Rex") tutan bir Pet, {"Name":"Rex"} olarak çıkıyor. ASP.NET Core notları union desteğini şu alanlarda listeliyor:
- Minimal API body'leri ve dönüş tipleri;
Task<Union>,IAsyncEnumerable<Union>veResults<T1, T2>dahil - MVC ve Razor Pages JSON request ve response body'leri
- JSON hub protokolü üzerinde SignalR
- Blazor parametreleri, JS interop ve persisted state
Review sırasında en çok yavaşlayacağım yer wire format olurdu. Union case'leri herhangi bir $type discriminator taşımıyor ve OpenAPI, bir union dönüşünü case şemaları üzerinde anyOf olarak tanımlıyor. İki case aynı JSON şekline serialize edildiğinde notlar, bunları bir [JsonUnion] classifier ile ayırt etmeyi öneriyor. TypeScript tiplerinizi üreten araç ne olursa olsun daraltma yapabileceği bir etiket yok; bu yüzden case'lerin birbirinden ayırt edilebilir şekillere sahip olması gerekiyor. Yukarıdaki gibi status code'lara eşlemeyi yine de uç noktada yapmayı tercih etmemin sebebi de bu. Union doğrudan döndürüldüğünde yalnızca response body oluyor, dolayısıyla status code case'e göre değişmiyor.
Mevcut sınırlamalar:
- Yalnızca JSON body ve response'lar. Query, route, header veya form'dan binding yok.
- MessagePack ve Newtonsoft.Json hub protokolleri union'ları desteklemiyor.
- Swashbuckle ve NSwag henüz union'ları tanımıyor.
Kendiliğinden açılan CSRF koruması
WebApplication.CreateBuilder ile oluşturulan uygulamalar artık hiçbir yapılandırma gerektirmeden güvenli olmayan cross-origin istekleri reddediyor. Kontrol, tarayıcının Sec-Fetch-Site ve Origin header'larını kullanıyor ve Minimal API'ları, MVC'yi, Razor Pages'i ve Blazor'ı kapsıyor. Same-origin istekler, kullanıcının başlattığı navigasyonlar ve tarayıcı dışı client'lar geçiyor. Bir form'u tüketmeye çalışan cross-origin bir tarayıcı isteği ise reddediliyor.
İyi bir varsayılan ama bir şeyleri sessizce bozabilir. İlk bakacağım yer ödemeler olurdu: 3-D Secure dönüş URL'leri çoğu zaman bankanın ya da sağlayıcının sayfasından gönderilen bir form POST'u alıyor; bu da notlarda reddedileceği söylenen durumla örtüşüyor. Bunu herhangi bir sağlayıcıyla test etmedim, o yüzden bir sonuç olarak değil, kontrol edilmesi gereken bir madde olarak düşünün. Uygulamanıza form post eden partner siteler de aynı listeye giriyor.
Kaçış yolları:
- Minimal API'lar için
.DisableAntiforgery() - MVC için
[IgnoreAntiforgeryToken] - tüm uygulama için
DisableCsrfProtectionyapılandırma anahtarı - özel bir
ICsrfProtection
Uygulama genelindeki anahtar yerine, review edilmiş kısa bir endpoint bazlı opt-out listesi görmeyi tercih ederim.
OpenAPI 3.2 ve diğerleri
Üretilen dokümanlar artık varsayılan olarak OpenAPI 3.2'yi hedefliyor. Değişen yalnızca varsayılan; 3.2'ye henüz geçmemiş araçlar için daha eski bir sürümü açıkça belirtebilirsiniz. Dokümanı okuyan client generator'ları, gateway'leri ve contract testlerini kontrol edin.
SignalR bağlantıları artık token süresi dolduğunda kopmadan authentication'ı yenileyebiliyor. Bunu her hub için EnableAuthenticationRefresh ile açıyorsunuz. Bu preview'da yalnızca .NET client destekliyor. JavaScript/TypeScript ve Azure SignalR Service desteği üzerinde çalışılıyor, yani bir React Native client henüz bundan faydalanamıyor.
Sürümün geri kalanında: xUnit v3 ve NUnit için test şablonları, Podman ile multi-arch container yayınlama ve complex-type property'leri üzerinden gidebilen EF Core key ve index'leri var.
Ben ne yapıyorum
Production .NET 10'da kalıyor. Bir preview branch'inde şunları yapmayı planlıyorum:
- Dahili bir endpoint'in sonuçlarını union olarak modelleyip, fikir oluşturmadan önce üretilen OpenAPI ve TypeScript tiplerini incelemek.
- GA'dan önce Meridian'ın source generator'larının union tipli request ve response'ları doğru işlediğini kontrol etmek.
- Meşru olarak cross-origin tarayıcı form post'u alan tüm endpoint'leri, ödeme callback'lerinden başlayarak listelemek.
- Downstream araçların 3.2'de olmadığı her yerde OpenAPI sürümünü sabitlemek.
Union'lar bu döngüde en çok önemsediğim C# değişikliği ve framework'ün, serializer'ın ve web stack'inin onları tek bir tasarım olarak ele aldığı ilk preview bu. Hâlâ bir preview; pürüzleri bulmanın zamanı da tam olarak şimdi.
Kaynaklar
Bu yazıda yararlanılan birincil kaynaklar.