﻿
{"id":28015,"date":"2025-09-09T19:24:48","date_gmt":"2025-09-09T19:24:48","guid":{"rendered":"https:\/\/www.gencayyildiz.com\/blog\/?p=28015"},"modified":"2025-09-09T19:24:48","modified_gmt":"2025-09-09T19:24:48","slug":"asp-net-core-uygulamalarinda-idempotent-rest-api-uygulama","status":"publish","type":"post","link":"https:\/\/www.gencayyildiz.com\/blog\/asp-net-core-uygulamalarinda-idempotent-rest-api-uygulama\/","title":{"rendered":"Asp.NET Core Uygulamalar\u0131nda Idempotent REST API Uygulama"},"content":{"rendered":"<div id=\"fb-root\"><\/div>\n<p>Merhaba,<\/p>\n<p>Bu i\u00e7eri\u011fimizde \u00f6nceki yaz\u0131lar\u0131m\u0131zda yer yer vurgulad\u0131\u011f\u0131m\u0131z <em>Idempotent<\/em> kavram\u0131na bir de Asp.NET Core REST API a\u00e7\u0131s\u0131ndan bak\u0131\u015f at\u0131yor olacak ve client&#8217;lardan gelen yinelenen request&#8217;ler neticesinde yarat\u0131lan m\u00fckerrer durumlara kar\u015f\u0131n nas\u0131l \u00f6nlemler alabilece\u011fimizi ve bir yandan da distributed sistemlerde REST API \u00fczerinden haberle\u015fme s\u00fcre\u00e7lerinde g\u00fcvenli\u011fin daha da nas\u0131l art\u0131r\u0131labilece\u011fini de\u011ferlendiriyor olaca\u011f\u0131z. O halde buyurun ba\u015flayal\u0131m&#8230;<\/p>\n<div style=\"font-size:12px;\">\nIdempotent kavram\u0131na temas edilen \u00f6nceki yaz\u0131lar\u0131m\u0131z;<\/p>\n<ul>\n<li><a href=\"https:\/\/www.gencayyildiz.com\/blog\/event-driven-mimaride-yinelenen-mesajlar-idempotent-sorunsali-ve-idempotent-consumer-pattern\/\" target=\"_blank\">Event-Driven Mimaride Yinelenen Mesajlar \u2013 Idempotent Sorunsal\u0131 ve Idempotent Consumer Pattern<\/a><\/li>\n<li><a href=\"https:\/\/www.gencayyildiz.com\/blog\/outbox-design-pattern-nedir-nasil-uygulanir-inbox-pattern-esliginde\/\" target=\"_blank\">Outbox Design Pattern Nedir? Nas\u0131l Uygulan\u0131r? (Inbox Pattern E\u015fli\u011finde)<\/a><\/li>\n<\/ul>\n<\/div>\n<h4>Idempotency Nedir?<\/h4>\n<p>Yukar\u0131da referans etti\u011fim iki makalede idempotency kavram\u0131n\u0131 farkl\u0131 a\u00e7\u0131lardan de\u011ferlendirmi\u015ftik. Hadi \u015fimdi gelin farkl\u0131 bir dille tekrar de\u011ferlendirelim \ud83d\ude42 Idempotency, bir sistem i\u00e7in <u>g\u00fcvenilirli\u011fi<\/u> ve <u>tutarl\u0131l\u0131\u011f\u0131<\/u> garanti eden kritik bir kavramd\u0131r.<\/p>\n<p><u>G\u00fcvenilirli\u011fi garanti ediyor, \u00e7\u00fcnk\u00fc<\/u>; bir i\u015flem yanl\u0131\u015fl\u0131kla veyahut a\u011f sorunlar\u0131 gibi t\u00fcrl\u00fc nedenlerle birden fazla kez g\u00f6nderilir ya da yap\u0131lmaya \u00e7al\u0131\u015f\u0131l\u0131rsa(ki bu tarz durumlar kullan\u0131c\u0131 kaynakl\u0131 s\u00fcrekli ola\u011fan \u015feylerdir) sistemin bu etkiyi yaln\u0131zca bir kez i\u015flemesini sa\u011flar. B\u00f6ylece, sistemin \u00f6ng\u00f6r\u00fclemeyen hatalardan(\u00f6rne\u011fin; \u00e7ift \u00f6deme, ayn\u0131 i\u015flemin m\u00fckerrer tekrar edilmesi vs.) korunmas\u0131n\u0131 sa\u011flar.<\/p>\n<p>Bunun yan\u0131nda, a\u011f kesintileri veya client-server ileti\u015fim hatalar\u0131 gibi durumlarda i\u015flemin ya da ileti\u015fimin g\u00f6n\u00fcl rahatl\u0131\u011f\u0131nca tekrar tekrar denenebilmesinin g\u00fcvencesini sa\u011flar. \u00c7\u00fcnk\u00fc idempotency, bir i\u015flemin tekrar denenmesi durumunda bile beklenmeyen yan etkilerin \u00fcretilmeyece\u011finin garantisini vermektedir.<\/p>\n<p>Bu duruma rasyonel bir \u00f6rnek vermemiz gerekirse e\u011fer; bir veritaban\u0131nda kayd\u0131n g\u00fcncellenmesi idempotent bir \u015fekilde tasarland\u0131ysa, ayn\u0131 g\u00fcncelleme talebi tekrar g\u00f6nderildi\u011finde sistem ya ayn\u0131 sonucu d\u00f6nd\u00fcrecek ya da zaten g\u00fcncellenmi\u015f oldu\u011funu bildirerek i\u015flem ger\u00e7ekle\u015ftirmeyecektir. Bir ba\u015fka \u00f6rnek vermemiz gerekirse e\u011fer; herhangi bir e-ticaret sisteminde, sipari\u015f verildi\u011fi taktirde stok i\u015flemleri i\u00e7in message broker&#8217;a at\u0131lan mesaj\u0131n t\u00fcrl\u00fc nedenlerden kaynakl\u0131 stok servisi taraf\u0131ndan tekrar tekrar i\u015flenebilme ihtimali s\u00f6z konusudur. \u0130\u015fte buradaki tasar\u0131m idempotent bir yakla\u015f\u0131m sergiliyorsa bu tarz durumlara kar\u015f\u0131n stok servisinde bir yan etki olu\u015fmayacak ve \u00f6nceden ilgili sipari\u015fe dair stok g\u00fcncellemesi yap\u0131ld\u0131\u011f\u0131 bilinece\u011fi i\u00e7in tekrar bir i\u015flem yap\u0131lmayacakt\u0131r.<\/p>\n<p><u>Tutarl\u0131l\u0131\u011f\u0131 garanti ediyor, \u00e7\u00fcnk\u00fc<\/u>; ayn\u0131 i\u015flem birden fazla kez \u00e7al\u0131\u015ft\u0131r\u0131ld\u0131\u011f\u0131 durumlarda, sistemin durumu ilk \u00e7al\u0131\u015ft\u0131r\u0131lmaya kar\u015f\u0131n yaln\u0131zca bir kez de\u011fi\u015fecek, sonrakilerde ise de\u011fi\u015fmeyecektir. B\u00f6ylece veritaban\u0131nda veya sistemde \u00e7eli\u015fkili durumlar\u0131n(\u00f6rne\u011fin; ayn\u0131 kayd\u0131n birden fazla kopyas\u0131n\u0131n olu\u015fmas\u0131 vs. gibi) \u00f6nlenmesi sa\u011flanm\u0131\u015f olacakt\u0131r.<\/p>\n<p>Ayr\u0131ca, idempotency bir i\u015flemin sonucunun her tekrarda her daim ayn\u0131 olmas\u0131n\u0131 garanti edece\u011fi i\u00e7in hem sistemin hem de verisel anlamda i\u015fleyi\u015fin tutarl\u0131 ve \u00f6ng\u00f6r\u00fclebilir olmas\u0131n\u0131 sa\u011flayacakt\u0131r.<\/p>\n<p>Buna da bir \u00f6rnek vermemiz gerekirse; bir \u00f6deme i\u015flemi idempotent ise, ayn\u0131 \u00f6deme talebi tekrar tekrar g\u00f6nderilse bile, sistem tek bir \u00f6deme yapacak ve di\u011ferlerinde zaten \u00f6demenin yap\u0131ld\u0131\u011f\u0131n\u0131 bildirecektir.<\/p>\n<p>Yani anlayaca\u011f\u0131n\u0131z, idempotent olarak tasarlanm\u0131\u015f bir i\u015flem, \u00f6zellikle a\u011f hatalar\u0131 veya zaman a\u015f\u0131m\u0131 sorunlar\u0131ndan kaynakl\u0131 yinelenen isteklerin g\u00f6n\u00fcl rahatl\u0131\u011f\u0131yla yap\u0131labilece\u011fini ve b\u00f6ylece ilk istek d\u0131\u015f\u0131nda sonucu de\u011fi\u015ftirmeden birden \u00e7ok kez tekrarlanabilece\u011fini garanti etmektedir.<\/p>\n<h4>REST API Ba\u011flam\u0131nda Idempotent Nedir?<\/h4>\n<p>REST API&#8217;ler ba\u011flam\u0131nda idempotent kavram\u0131, yinelenen isteklerin(request) sistem \u00fczerinde tek bir istekle ayn\u0131 etkiyi yaratmas\u0131 anlam\u0131na gelmektedir. Bir ba\u015fka deyi\u015fle, bir client taraf\u0131ndan herhangi bir i\u015fleve kar\u015f\u0131n olan istek ka\u00e7 kez yinelenirse yinelensin, server taraf\u0131ndaki etkisinin yaln\u0131zca bir kez ger\u00e7ekle\u015fmesidir.<\/p>\n<p>HTTP Semantics(anlamsal kurallar\u0131) \u00fczerine olan RFC 9110 standard\u0131 dahi bu konuda baz al\u0131nabilecek g\u00fczel bir tan\u0131m sunmakta ve idempotent i\u015flevselli\u011fini \u015fu \u015fekilde a\u00e7\u0131klamaktad\u0131r;<\/p>\n<blockquote><p><em>Bir istek metodunun &#8216;idempotent&#8217; kabul edilebilmesi i\u00e7in, ayn\u0131 metotla ger\u00e7ekle\u015ftirilen birden \u00e7ok \u00f6zde\u015f  iste\u011fin sunucuda yaratt\u0131\u011f\u0131 etkinin tek bir iste\u011fin etkisiyle ayn\u0131 olmas\u0131 gerekir.<\/em><\/p><\/blockquote>\n<p>Bu a\u00e7\u0131klamadan yola \u00e7\u0131karak; <em>PUT<\/em>, <em>DELETE<\/em>, <em>GET<\/em>, <em>HEAD<\/em>, <em>OPTIONS<\/em> ve <em>TRACE<\/em> idempotent metotlar oldu\u011funu s\u00f6yleyebiliriz.<\/p>\n<p>Tabi burada ayriyeten \u015funu da vurgulamakta fayda vard\u0131r ki; idempotent, yaln\u0131zca kullan\u0131c\u0131n\u0131n bekledi\u011fi ana ya da bir ba\u015fka deyi\u015fle sistemin o anki i\u015fleyi\u015f ba\u011flam\u0131na etki i\u00e7in ge\u00e7erli olan bir yap\u0131lanmad\u0131r. Yani idempotent olan bir yap\u0131lanmada server, gelen request&#8217;i idempotent olarak i\u015flemek d\u0131\u015f\u0131nda her iste\u011fi ayr\u0131 ayr\u0131 loglayabilir, bir s\u00fcr\u00fcm kontrol ge\u00e7mi\u015fi tutabilir ya da metrik toplamak gibi t\u00fcrl\u00fc side effect&#8217;ler olu\u015fturarak sistemin gerektirdi\u011fi ba\u015fka i\u015flemleri rahatl\u0131kla y\u00fcr\u00fctebilir. Anlayaca\u011f\u0131n\u0131z, her istekte kullan\u0131c\u0131 ve sistem a\u00e7\u0131s\u0131ndan sonu\u00e7 de\u011fi\u015fmeyecek(yani idempotent olacak) ancak arka planda server teknik olarak ba\u015fka kay\u0131tlar veya i\u015flemler yaparak ya\u015fam fonksiyonlar\u0131na devam edebilecektir.<\/p>\n<p>Bu duruma g\u00fcnl\u00fck hayattan a\u015fa\u011f\u0131daki gibi g\u00fczel bir \u00f6rnek verebiliriz;<br \/>\nYap\u0131lan bir al\u0131\u015fveri\u015f neticesinde kasiyerden ayn\u0131 fi\u015fi ka\u00e7 kez istersek isteyelim ayn\u0131 i\u00e7eri\u011fe sahip fi\u015f verecektir, bu k\u0131s\u0131m idempotent&#8217;tir. Ama bu s\u00fcre\u00e7te kasiyer her fi\u015f iste\u011fini ayr\u0131ca deftere kaydedebilir ya da kendince not alabilir, i\u015fte bu da idempotent&#8217;a ayk\u0131r\u0131 olmayan side effect&#8217;lerdir.<\/p>\n<h4>Hangi HTTP Metotlar\u0131 Do\u011fas\u0131 Gere\u011fi Idempotent&#8217;tir?<\/h4>\n<p>HTTP metotlar\u0131n\u0131n idempotent olup olmamas\u0131, ayn\u0131 iste\u011fin birden fazla kez yap\u0131lmas\u0131n\u0131n sunucu \u00fczerinde ayn\u0131 sonucu \u00fcretip \u00fcretmedi\u011fine ba\u011fl\u0131d\u0131r. Bu a\u00e7\u0131dan olay\u0131 de\u011ferlendirdi\u011fimizde bir\u00e7ok HTTP metodu do\u011fas\u0131 gere\u011fi idempotent&#8217;t\u0131r;<\/p>\n<ul style=\"font-size:12px;\">\n<li><code>GET<\/code><br \/>\nHer GET iste\u011finde sunucu, state&#8217;ini de\u011fi\u015ftirmeyece\u011fi ve sadece ayn\u0131 veriyi getirece\u011fi i\u00e7in idempotent&#8217;dir. Burada dikkat edilmesi gereken bir husus vard\u0131r; o da, pratikte baz\u0131 uygulamalarda GET isteklerinin yanl\u0131\u015f \u015fekilde implemente edilmesinden kaynakl\u0131 sunucuda kullan\u0131c\u0131 ve sistem a\u00e7\u0131s\u0131ndan state&#8217;i de\u011fi\u015ftirebilecek saya\u00e7 art\u0131rma, log yazma gibi side effect&#8217;ler mevzu bahis olabilir. Bu durumlarda, GET metodu teknik olarak idempotent olmaktan \u00e7\u0131kabilir; \u015f\u00f6yle ki, yukar\u0131daki sat\u0131rlarda da bahsetti\u011fimiz gibi kullan\u0131c\u0131ya sunulan kaynak durumu de\u011fi\u015fmedi\u011fi s\u00fcrece bu metodun idempotent&#8217;li\u011fi etkilenmeyecektir. Aksi taktirde, bu side effect&#8217;ler neticesinde kullan\u0131c\u0131ya sunulan sonu\u00e7ta bir de\u011fi\u015fiklik olacaksa (ki bu de\u011fi\u015fiklik &#8216;bu profil (n) kere g\u00f6sterildi&#8217; \u015feklinde bir saya\u00e7 bilgisini etkiliyorsa), her istekte bu saya\u00e7 artacak ve kullan\u0131c\u0131ya farkl\u0131 sonu\u00e7 sunulacakt\u0131r. Ee haliyle bu durumda da idempotent&#8217;\u0131n do\u011fas\u0131na ayk\u0131r\u0131 bir durum s\u00f6z konusu olacakt\u0131r.<\/li>\n<li><code>HEAD<\/code><br \/>\nGET ile benzer davran\u0131\u015f sergileyen ancak sadece response headers&#8217;lar\u0131 d\u00f6nd\u00fcrerek, response body i\u00e7ermeyen bir metottur. GET&#8217;te bahsedilen kritik durumlar\u0131 ayn\u0131 \u015fekilde g\u00f6z \u00f6n\u00fcne alarak do\u011fas\u0131 gere\u011fi idempotent oldu\u011funu s\u00f6yleyebiliriz.\n<\/li>\n<li><code>PUT<\/code><br \/>\nGenellikle belirli kayna\u011f\u0131 g\u00fcncellemekte olan metot t\u00fcr\u00fcd\u00fcr. Haliyle ayn\u0131 PUT iste\u011fi her tekrarland\u0131\u011f\u0131nda, kaynak zaten ilkinde g\u00fcncellenmi\u015f olaca\u011f\u0131 i\u00e7in ayn\u0131 sonucu \u00fcretecektir. Bundan kaynakl\u0131 genellikle idempotent&#8217;t\u0131r. Amma velakin her PUT iste\u011finde kaynakta art\u0131rma, eksiltme ya da rastgelelik gibi s\u00fcrekli bir de\u011fi\u015fim s\u00f6z konusuysa bu taktirde idempotent&#8217;lik mevzu bahis olmayacakt\u0131r. Bunu \u015f\u00f6yle \u00f6rneklendirebiliriz; yap\u0131lan PUT iste\u011finde <em>{&#8216;adi&#8217; : &#8216;Gen\u00e7ay&#8217;}<\/em> \u015feklinde bir \u00e7al\u0131\u015fma yap\u0131l\u0131yorsa, evet, ilk istekten sonraki t\u00fcm istekler ayn\u0131 neticeyle nihayete erecek ve ilgili kayd\u0131n ad\u0131n\u0131 &#8216;Gen\u00e7ay&#8217; olarak de\u011fi\u015ftirecektirler. \u0130\u015fte bu durum idempotent&#8217;tir. Halbuki yap\u0131lan PUT iste\u011finde, <em>{&#8216;increment&#8217;: 5}<\/em> minvalinde de\u011fer art\u0131r\u0131c\u0131 ya da azalt\u0131c\u0131 bir i\u015flem yap\u0131lacaksa, her istekte bu i\u015flem farkl\u0131 bir netice do\u011furaca\u011f\u0131 i\u00e7in bu kullan\u0131m durumlar\u0131nda da idempotent&#8217;l\u0131k s\u00f6z konusu olmayacakt\u0131r.\n<\/li>\n<li><code>DELETE<\/code><br \/>\nDELETE ile belirli bir kayna\u011f\u0131(ya da kayd\u0131\/veriyi) silece\u011fimiz i\u00e7in ve zaten bu kaynak ilk istekte silinmi\u015f olaca\u011f\u0131 i\u00e7in iste\u011fin sonraki tekrarlar\u0131nda ayn\u0131 durum korunacak ve mutlak bir idempotent&#8217;l\u0131k durumu s\u00f6z konusu olacakt\u0131r.\n<\/li>\n<li><code>OPTIONS<\/code><br \/>\nBu istekte, sunucunun destekledi\u011fi metotlar\u0131 sorgulayaca\u011f\u0131 i\u00e7in idempotent bir davran\u0131\u015fa sahiptir.\n<\/li>\n<\/ul>\n<p>Ancak yap\u0131s\u0131 ve davran\u0131\u015f\u0131 gere\u011fi net idempotent olmayan tek bir HTTP metodumuz vard\u0131r, o da <code style=\"color:red;\">POST<\/code>&#8216;tur. POST, her istekte yeni bir kaynak olu\u015fturaca\u011f\u0131 i\u00e7in do\u011fas\u0131 gere\u011fi idempotent de\u011fildir! Tabi bizler \u00f6zel mant\u0131k geli\u015ftirerek, kayna\u011f\u0131 olu\u015fturmadan \u00f6nce mevcut mu de\u011fil mi kontrol edebilir ve b\u00f6ylece tekrarlanan POST isteklerinde idempotent&#8217;lik sa\u011flayabiliriz.<\/p>\n<h4>Asp.NET Core&#8217;da Idempotency&#8217;nin Uygulanmas\u0131<\/h4>\n<p>Asp.NET Core&#8217;da uygulamaya idempotency davran\u0131\u015f\u0131n\u0131 uygulayabilmek i\u00e7in request header&#8217;lar\u0131nda <em>Idempotence-Key<\/em> i\u00e7eren \u00f6zel bir strateji geli\u015ftirece\u011fiz. Bu stratejinin teknik detaylar\u0131 \u015fu \u015fekilde olacakt\u0131r;<\/p>\n<ol>\n<li>Client, her i\u015fleme \u00f6zel unique bir key \u00fcretecek ve bunu <em>Idempotence-Key<\/em> ba\u015fl\u0131\u011f\u0131nda request&#8217;in header&#8217;\u0131na ekleyerek g\u00f6nderecektir.<\/li>\n<li>Server ise bu key&#8217;i kontrol edecek ve belirli bir vadeye kar\u015f\u0131n \u00f6nceden i\u015flenmi\u015f bir key&#8217;in muadiliyse o i\u015flemin result&#8217;\u0131n\u0131 d\u00f6nd\u00fcrecek, yok e\u011fer de\u011filse de gelen istek do\u011frultusunda i\u015flevini ger\u00e7ekle\u015ftirecek ve bu key e\u015fli\u011finde sonucu saklayacakt\u0131r.<\/li>\n<\/ol>\n<p>Yani anlayaca\u011f\u0131n\u0131z, isteklerdeki idempotent de\u011ferlerini server&#8217;da adil bir s\u00fcreli\u011fine cache&#8217;leyerek store edece\u011fiz, ard\u0131ndan da bu s\u00fcre zarf\u0131nda a\u011f sorunlar\u0131 gibi durumlardan kaynakl\u0131 tekrar eden isteklerin yaln\u0131zca bir kez i\u015flenmesini sa\u011fl\u0131yor olaca\u011f\u0131z.<\/p>\n<p>Bunun i\u00e7in uygulamada \u00f6ncelikle bir cache mekanizmas\u0131 temellerinin at\u0131lm\u0131\u015f olmas\u0131 gerekmektedir. Bizler bu i\u00e7eri\u011fimizde, bu i\u015flem i\u00e7in Redis veritaban\u0131n\u0131 tercih edece\u011fiz. Haliyle <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Extensions.Caching.StackExchangeRedis\" target=\"_blank\">Microsoft.Extensions.Caching.StackExchangeRedis<\/a> k\u00fct\u00fcphanesi e\u015fli\u011finde a\u015fa\u011f\u0131daki yap\u0131land\u0131rmayla cache temellerini h\u0131zl\u0131ca atabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nbuilder.Services.AddStackExchangeRedisCache(options =&gt;\r\n{\r\n    options.Configuration = &quot;localhost:6379&quot;;\r\n});\r\n<\/pre>\n<\/div>\n<p>Bu kadar basit. Art\u0131k bu cache altyap\u0131s\u0131 \u00fczerinden idempotency \u00e7al\u0131\u015fmalar\u0131na ba\u015flayabiliriz.<\/p>\n<h5>Controller-Based Idempotency<\/h5>\n<p>Asp.NET Core&#8217;da geleneksel controller seviyesindeki \u00e7al\u0131\u015fmalarda idempotency i\u00e7in a\u015fa\u011f\u0131daki gibi bir <em>Attribute<\/em> ve <em>IAsyncActionFilter<\/em> olarak geli\u015ftirilmi\u015f s\u0131n\u0131f tasarlayabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    &#x5B;AttributeUsage(AttributeTargets.Method)]\r\n    public sealed class IdempotentAttribute(int CacheTimeInMinutes = 60) : Attribute, IAsyncActionFilter\r\n    {\r\n        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)\r\n        {\r\n            \/\/Gelen istektenten Idempotence-Key header'\u0131n\u0131 elde ediyoruz.\r\n            if (\r\n                !context.HttpContext.Request.Headers.TryGetValue(&quot;Idempotence-Key&quot;, out StringValues idempotenceKeyValue)\r\n                ||\r\n                !Guid.TryParse(idempotenceKeyValue, out Guid idempotenceKey)\r\n                )\r\n            {\r\n                context.Result = new BadRequestObjectResult(&quot;Idempotence-Key de\u011feri ge\u00e7ersiz veya eksiktir!&quot;);\r\n                return;\r\n            }\r\n\r\n            \/\/Bu iste\u011fin daha \u00f6nce i\u015flenip i\u015flenmedi\u011fini kontrol ediyoruz. E\u011fer i\u015flendiyse, \u00f6nbellekten sonucu al\u0131p d\u00f6nd\u00fcr\u00fcyoruz.\r\n            IDistributedCache cache = context.HttpContext.RequestServices.GetRequiredService&lt;IDistributedCache&gt;();\r\n            string cacheKey = $&quot;Idempotence_{idempotenceKey}&quot;;\r\n            string? cachedResult = await cache.GetStringAsync(cacheKey);\r\n\r\n            if (cachedResult is not null)\r\n            {\r\n                IdempotentResponse response = JsonSerializer.Deserialize&lt;IdempotentResponse&gt;(cachedResult)!;\r\n\r\n                var result = new ObjectResult(response.Value)\r\n                {\r\n                    StatusCode = response.StatusCode\r\n                };\r\n\r\n                context.Result = result;\r\n\r\n                return;\r\n            }\r\n\r\n            \/\/ \u0130stek daha \u00f6nce i\u015flenmemi\u015fse, i\u015flemi ger\u00e7ekle\u015ftiriyoruz. Ayr\u0131ca sonucu belirtilen s\u00fcre boyunca \u00f6nbelle\u011fe kaydediyoruz.\r\n            ActionExecutedContext executedContext = await next();\r\n\r\n            if (executedContext.Result is ObjectResult { StatusCode: &gt;= 200 and &lt; 300 } objectResult)\r\n            {\r\n                int statusCode = objectResult.StatusCode ?? StatusCodes.Status200OK;\r\n                IdempotentResponse response = new(statusCode, objectResult.Value);\r\n\r\n                await cache.SetStringAsync(\r\n                    cacheKey,\r\n                    JsonSerializer.Serialize(response),\r\n                    new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheTimeInMinutes) }\r\n                    );\r\n            }\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Burada;<br \/>\n<em>6<\/em> ile <em>15.<\/em> sat\u0131r aral\u0131\u011f\u0131na g\u00f6z atarsak e\u011fer client&#8217;tan gelen istekteki <em>Idempotence-Key<\/em> header&#8217;\u0131 kontrol edilmekte ve b\u00f6yle bir header yoksa yahut olsa dahi i\u00e7erisindeki veri uygun formatta de\u011filse, i\u015fte bu taktirde ilgili istek <em>BadRequestObjectResult<\/em> olarak g\u00fcncellenip, geri d\u00f6nd\u00fcr\u00fclmektedir.<\/p>\n<p><em>17<\/em> ile <em>34.<\/em> sat\u0131r aral\u0131\u011f\u0131na bakarsak e\u011fer, ge\u00e7erli bir <em>Idempotence-Key<\/em> de\u011ferine kar\u015f\u0131l\u0131k bu iste\u011fin daha \u00f6nce i\u015flenip i\u015flenmedi\u011fi kontrol edilmekte ve e\u011fer i\u015flendiyse cache&#8217;den \u00f6nceki sonucu al\u0131n\u0131p, geri g\u00f6nderilmektedir.<\/p>\n<p>Son olarak <em>36<\/em> ile <em>50.<\/em> sat\u0131r aral\u0131\u011f\u0131na g\u00f6z atarsak, ilgili iste\u011fin daha \u00f6nce(ya da adil cache s\u00fcreci i\u00e7erisinde) i\u015flenmedi\u011fine kanaat getirildi\u011fi taktirde gerekli i\u015flevsellik g\u00f6sterilmekte, ayr\u0131ca yeni de\u011fer cache&#8217;lenerek s\u00fcre\u00e7 tamamlanmaktad\u0131r.<\/p>\n<p>Tabi bu s\u00fcre\u00e7te kullan\u0131lan <code>IdempotentResponse<\/code> t\u00fcr\u00fcn\u00fc de a\u015fa\u011f\u0131daki gibi olu\u015fturabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\ninternal sealed class IdempotentResponse\r\n{\r\n    &#x5B;JsonConstructor]\r\n    public IdempotentResponse(int statusCode, object? value)\r\n    {\r\n        StatusCode = statusCode;\r\n        Value = value;\r\n    }\r\n\r\n    public int StatusCode { get; }\r\n    public object? Value { get; }\r\n}\r\n<\/pre>\n<\/div>\n<p>Ve bu \u00e7al\u0131\u015fmadan sonra a\u015fa\u011f\u0131daki gibi herhangi bir controller&#8217;da bu filter&#8217;\u0131 devreye sokup, kullanabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    &#x5B;Route(&quot;api\/&#x5B;controller]&quot;)]\r\n    &#x5B;ApiController]\r\n    public class OrdersController : ControllerBase\r\n    {\r\n        &#x5B;HttpPost]\r\n        &#x5B;Idempotent(CacheTimeInMinutes: 60)]\r\n        public IActionResult CreateOrder(&#x5B;FromBody] CreateOrderRequest createOrderRequest)\r\n        {\r\n            return Ok(new Random().NextDouble());\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Burada dikkat ederseniz &#8216;m\u0131\u015f&#8217; gibi bir sipari\u015f olu\u015fturma i\u015flemi ger\u00e7ekle\u015ftirmekte ve esas\u0131nda 0 ile 1 aras\u0131nda rastgele bir de\u011fer \u00fcreterek geriye d\u00f6nd\u00fcrmekteyiz. Birazdan idempotent olan bu sistemde, gelen tekrarl\u0131 istekler neticesinde client&#8217;a g\u00f6nderilen verinin yeni \u00fcretilip \u00fcretilmedi\u011fini bu de\u011ferlerden kontrol edip, test edece\u011fiz. Ama tabi sanki bir sipari\u015f kayd\u0131 olu\u015fturuluyormu\u015f gibi varsayaca\u011f\u0131z \ud83d\ude42<\/p>\n<p>Esas\u0131nda controller tabanl\u0131 isteklerle ilgili \u00e7al\u0131\u015fmam\u0131z bu noktada tamamland\u0131\u011f\u0131 i\u00e7in testlere ge\u00e7ip gerekli kontrollerde bulunabiliriz. Ancak ben minimal api&#8217;ler i\u00e7in de bu yap\u0131land\u0131rman\u0131n nas\u0131l olaca\u011f\u0131na de\u011finip, g\u00fcn sonunda her iki yakla\u015f\u0131m\u0131nda testini tek elden yapmay\u0131 tercih etmekteyim.<\/p>\n<h5>Minimal API Idempotency<\/h5>\n<p>Evet, minimal API&#8217;lerde de idempotency&#8217;i uygulayabilmek i\u00e7in a\u015fa\u011f\u0131daki gibi bir <em>IEndpointFilter<\/em> tasarlayabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n    public sealed class IdempotencyFilter(int CacheTimeInMinutes = 60) : IEndpointFilter\r\n    {\r\n        public async ValueTask&lt;object?&gt; InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)\r\n        {\r\n            \/\/Gelen istektenten Idempotence-Key header'\u0131n\u0131 elde ediyoruz.\r\n            if (\r\n                !context.HttpContext.Request.Headers.TryGetValue(&quot;Idempotence-Key&quot;, out StringValues idempotenceKeyValue)\r\n                ||\r\n                !Guid.TryParse(idempotenceKeyValue, out Guid idempotenceKey)\r\n                )\r\n            {\r\n                return Results.BadRequest(&quot;Idempotence-Key de\u011feri ge\u00e7ersiz veya eksiktir!&quot;);\r\n            }\r\n\r\n            \/\/Bu iste\u011fin daha \u00f6nce i\u015flenip i\u015flenmedi\u011fini kontrol ediyoruz. E\u011fer i\u015flendiyse, \u00f6nbellekten sonucu al\u0131p d\u00f6nd\u00fcr\u00fcyoruz.\r\n            IDistributedCache cache = context.HttpContext.RequestServices.GetRequiredService&lt;IDistributedCache&gt;();\r\n            string cacheKey = $&quot;Idempotence_{idempotenceKey}&quot;;\r\n            string? cachedResult = await cache.GetStringAsync(cacheKey);\r\n\r\n            if (cachedResult is not null)\r\n            {\r\n                IdempotentResponse response = JsonSerializer.Deserialize&lt;IdempotentResponse&gt;(cachedResult)!;\r\n                return response.Value;\r\n            }\r\n\r\n            \/\/ \u0130stek daha \u00f6nce i\u015flenmemi\u015fse, i\u015flemi ger\u00e7ekle\u015ftiriyoruz. Ayr\u0131ca sonucu belirtilen s\u00fcre boyunca \u00f6nbelle\u011fe kaydediyoruz.\r\n            var result = await next(context);\r\n\r\n            if (result is IStatusCodeHttpResult { StatusCode: &gt;= 200 and &lt; 300 } statusCodeResult and IValueHttpResult valueResult)\r\n            {\r\n                int statusCode = statusCodeResult.StatusCode ?? StatusCodes.Status200OK;\r\n                IdempotentResponse response = new(statusCode, valueResult.Value);\r\n\r\n                await cache.SetStringAsync(\r\n                    cacheKey,\r\n                    JsonSerializer.Serialize(response),\r\n                    new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(CacheTimeInMinutes) }\r\n                    );\r\n            }\r\n\r\n            return result;\r\n        }\r\n    }\r\n<\/pre>\n<\/div>\n<p>Burada da benzer mant\u0131kla, <em>5<\/em> ile <em>13.<\/em> sat\u0131r aral\u0131\u011f\u0131nda request&#8217;teki <em>Idempotence-Key<\/em> de\u011feri kontrol edilmekte, <em>15<\/em> ile <em>24.<\/em> sat\u0131r aral\u0131\u011f\u0131nda bu iste\u011fin \u00f6nceden yap\u0131l\u0131p yap\u0131lmad\u0131\u011f\u0131 de\u011ferlendirilmekte ve <em>26<\/em> ile <em>39.<\/em> sat\u0131r aral\u0131\u011f\u0131nda ise request i\u015flenerek sonucu cache&#8217;lenmektedir.<\/p>\n<p>Bu yapt\u0131\u011f\u0131m\u0131z \u00e7al\u0131\u015fmay\u0131 da a\u015fa\u011f\u0131daki gibi herhangi bir endpoint&#8217;te <code>AddEndpointFilter<\/code> metodu ile \u00e7a\u011f\u0131rabiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n.\r\n.\r\n.\r\napp.MapPost(&quot;\/api\/orders_mini&quot;, (CreateOrderRequest createOrderRequest) =&gt;\r\n{\r\n    return Results.Ok(new Random().NextDouble());\r\n})\r\n    .AddEndpointFilter&lt;IdempotencyFilter&gt;();\r\n<\/pre>\n<\/div>\n<p>Evet, burada da yine sanki bir sipari\u015f olu\u015fturuluyormu\u015f minvalinde olan, halbuki i\u00e7erisinde 0 ile 1 aras\u0131nda de\u011fer d\u00f6nd\u00fcren bir i\u015flevselli\u011fe sahip bir endpoint tasarlam\u0131\u015f bulunmaktay\u0131z. <\/p>\n<p>Her neyse&#8230; Ne de olsa yapmam\u0131z gerekenler esas\u0131nda bunlardan ibarettir diyebiliriz.<\/p>\n<p>\u015eimdi gelin test s\u00fcrecine ge\u00e7ebiliriz;<\/p>\n<h5>Test Edelim<\/h5>\n<p>Yukar\u0131da yapt\u0131\u011f\u0131m\u0131z Controller-Based ve Minimal API odakl\u0131 iki idempotency \u00e7al\u0131\u015fmas\u0131n\u0131 a\u015fa\u011f\u0131daki gibi bir <code>.http<\/code> dosyas\u0131yla rahatl\u0131kla test edebiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n### Create Order Controller-Based Test Request\r\nPOST https:\/\/localhost:7152\/api\/orders\r\nContent-Type: application\/json\r\nAccept: application\/json\r\nIdempotence-Key: {{$guid}}\r\n\r\n{\r\n  &quot;BuyerId&quot;: 123\r\n}\r\n\r\n### Create Order Minimal API Test Request\r\nPOST https:\/\/localhost:7152\/api\/orders_mini\r\nContent-Type: application\/json\r\nAccept: application\/json\r\nIdempotence-Key: {{$guid}}\r\n\r\n{\r\n  &quot;BuyerId&quot;: 123\r\n}\r\n<\/pre>\n<\/div>\n<p>Burada tasarlanan isteklerin yap\u0131s\u0131na g\u00f6z atarsak e\u011fer <em>Guid<\/em> t\u00fcr\u00fcnden olan <em>Idempotence-Key<\/em> verisi otomatik \u00fcretilerek-<span style=\"font-size:12px;\">{{$guid}}<\/span>&#8211; request olu\u015fturulmakta ve b\u00f6ylece, client&#8217;\u0131n her bir iste\u011fe kar\u015f\u0131n yeni bir key \u00fcretebildi\u011fi bir durumu rahatl\u0131kla sim\u00fcle edebilmekteyiz.<\/p>\n<p>\u015eimdi a\u015fa\u011f\u0131daki g\u00f6rselde oldu\u011fu gibi i\u00e7erik s\u00fcrecinde olu\u015fturdu\u011fumuz her iki endpoint&#8217;e \u00fc\u00e7erli istekte bulundu\u011fumuzu varsayal\u0131m;<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama.gif\" alt=\"Asp.NET Core Uygulamalar\u0131nda Idempotent REST API Uygulama\" width=\"800\" height=\"409\" class=\"aligncenter size-full wp-image-28026\" \/><\/a>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere t\u00fcm istekler yeni bir <em>Idempotence-Key<\/em> de\u011feriyle yap\u0131laca\u011f\u0131 i\u00e7in sistem gere\u011fi bu isteklere kar\u015f\u0131n \u00fcretilen de\u011ferlerle(result) birlikte <em>Idempotence-Key<\/em> verileri Redis&#8217;te cache&#8217;lenecektir. Bu durum, ister controller-based olsun, ister minimal api olsun&#8230; hangi yakla\u015f\u0131m olursa olsun her ikisi i\u00e7in de ge\u00e7erli olacakt\u0131r.<\/p>\n<p>Tabi s\u00fcrecin i\u015fleyi\u015finin daha iyi sindirilebilmesi i\u00e7in herhangi bir request&#8217;in detay\u0131n\u0131 screenshot olarak a\u015fa\u011f\u0131ya alarak incelemekte fayda g\u00f6rmekteyim;<a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama.png\" alt=\"Asp.NET Core Uygulamalar\u0131nda Idempotent REST API Uygulama\" width=\"1916\" height=\"1030\" class=\"aligncenter size-full wp-image-28027\" srcset=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama.png 1916w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama-300x161.png 300w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama-1024x550.png 1024w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama-768x413.png 768w, https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama-1536x826.png 1536w\" sizes=\"auto, (max-width: 1916px) 100vw, 1916px\" \/><\/a>Evet, bu ekran al\u0131nt\u0131s\u0131ndan da g\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere bir request e\u011fer \u00f6zg\u00fcn bir <em>Idempotence-Key<\/em> de\u011ferine sahipse hem bu request&#8217;in i\u015flenmesine m\u00fcsade edilmekte hem de bir yandan Redis&#8217;e cache&#8217;lenerek tekrarl\u0131 bir istek geldi\u011fi taktirde cache&#8217;lenmi\u015f veriyi d\u00f6nd\u00fcrmektedir.<\/p>\n<p><em><strong>Ee hoca! tekrarl\u0131 istekleri de test ederek bi g\u00f6ster la!<\/strong><\/em> dedi\u011finizi duyar gibiyim&#8230; Tamam, hi\u00e7 merak etmeyin \ud83d\ude42 Misal olarak, yukar\u0131da yapt\u0131\u011f\u0131m\u0131z son iste\u011fin <em>Idempotence-Key<\/em> de\u011ferini al\u0131p, a\u015fa\u011f\u0131daki gibi kullanarak yinelenen istekleri h\u0131zl\u0131ca \u00f6rneklendirebiliriz;<\/p>\n<div style=\"font-size:12px;\">\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n### Create Order Controller-Based Test Request\r\nPOST https:\/\/localhost:7152\/api\/orders\r\nContent-Type: application\/json\r\nAccept: application\/json\r\nIdempotence-Key: 1817d553-4e2f-4d26-ac45-c9bc3fd54646\r\n\r\n{\r\n  &quot;BuyerId&quot;: 123\r\n}\r\n\r\n### Create Order Minimal API Test Request\r\nPOST https:\/\/localhost:7152\/api\/orders_mini\r\nContent-Type: application\/json\r\nAccept: application\/json\r\nIdempotence-Key: 1817d553-4e2f-4d26-ac45-c9bc3fd54646\r\n\r\n{\r\n  &quot;BuyerId&quot;: 123\r\n}\r\n<\/pre>\n<\/div>\n<p><a href=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama-1.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.gencayyildiz.com\/blog\/wp-content\/uploads\/2025\/09\/Asp.NET-Core-Uygulamalarinda-Idempotent-REST-API-Uygulama-1.gif\" alt=\"Asp.NET Core Uygulamalar\u0131nda Idempotent REST API Uygulama\" width=\"800\" height=\"409\" class=\"aligncenter size-full wp-image-28028\" \/><\/a>G\u00f6r\u00fcld\u00fc\u011f\u00fc \u00fczere cache&#8217;de ilgili <em>Idempotence-Key<\/em> de\u011ferine kar\u015f\u0131n bir request bilgisi tutuldu\u011fu i\u00e7in bu istek i\u015flenmemekte, mevcut de\u011fer d\u00f6nd\u00fcr\u00fclmektedir.<\/p>\n<p>\u0130\u015fte bu kadar \ud83d\ude42<\/p>\n<p>\u0130\u00e7eri\u011fimizi sonland\u0131rmadan, yaz\u0131 boyunca olas\u0131 akl\u0131n\u0131za tak\u0131lma riski olan bir ka\u00e7 hususa de\u011finmek istiyorum. Misal olarak bunlardan birinin cache&#8217;lenecek verilerin expiration s\u00fcreleri oldu\u011funu s\u00f6yleyebilirim. Evet, cache expiration olduk\u00e7a hassas bir parametredir diyebiliriz. Makul olmal\u0131 ama bir yandan da yinelenecek isteklerin i\u015flevselliklerindeki kritik duruma g\u00f6re zamansal uzunlu\u011fa sahip olmal\u0131d\u0131r. Bunun d\u0131\u015f\u0131nda distributed \u00e7al\u0131\u015fmalar ve daha da \u00f6nemlisi distributed locking durumlar\u0131 i\u00e7in Redis olduk\u00e7a idealdir diyebiliriz. O y\u00fczden bu tarz \u00e7al\u0131\u015fmalarda ben Redis&#8217;i mutlak tercih ediyorum diyebilirim. Sizlerin de bu konuda \u00e7ok fazla alternatif araman\u0131za gerek olmad\u0131\u011f\u0131n\u0131 d\u00fc\u015f\u00fcn\u00fcyorum.<\/p>\n<p>Nihai olarak;<br \/>\nBu i\u00e7eri\u011fimizde, REST API&#8217;lerimizin kalitesini, hizmet g\u00fcvenilirli\u011fini ve tutarl\u0131l\u0131\u011f\u0131n\u0131 art\u0131rabilmek i\u00e7in Idempotency yakla\u015f\u0131m\u0131n\u0131n nas\u0131l uygulanabilece\u011fini Asp.NET Core \u00e7er\u00e7evesinde de\u011ferlendirmi\u015f ve bir yandan da API&#8217;lerimizdeki kritik i\u015flemlere ve \u00f6zellikle sistem durumunu de\u011fi\u015ftiren veya \u00f6nemli i\u015f s\u00fcre\u00e7lerini tetikleyen operasyonlara kar\u015f\u0131n a\u011f kesintileri yahut kullan\u0131c\u0131 deneyimlerindeki problemlerden kaynakl\u0131 istemsiz yinelenebilen isteklere kar\u015f\u0131 nas\u0131l \u00f6nlem al\u0131nabilece\u011fini idempotency&#8217;i pratik bir \u015fekilde uygulayarak deneyimlemi\u015f bulunuyoruz.<\/p>\n<p>\u0130lgilenenlerin faydalanmas\u0131 dile\u011fiyle&#8230;<br \/>\nSonraki yaz\u0131lar\u0131mda g\u00f6r\u00fc\u015fmek \u00fczere&#8230;<br \/>\n\u0130yi \u00e7al\u0131\u015fmalar&#8230;<\/p>\n<p>Not : \u00d6rnek \u00e7al\u0131\u015fmaya a\u015fa\u011f\u0131daki GitHub adresinden eri\u015febilirsiniz.<br \/>\n<a href=\"https:\/\/github.com\/gncyyldz\/AspNetCore.Idempotency.Implementation\" target=\"_blank\">https:\/\/github.com\/gncyyldz\/AspNetCore.Idempotency.Implementation<\/a><\/p>\n<!-- AddThis Advanced Settings generic via filter on the_content --><!-- AddThis Share Buttons generic via filter on the_content -->","protected":false},"excerpt":{"rendered":"<p>Merhaba, Bu i\u00e7eri\u011fimizde \u00f6nceki yaz\u0131lar\u0131m\u0131zda yer yer vurgulad\u0131\u011f\u0131m\u0131z Idempotent kavram\u0131na bir de Asp.NET Core REST API a\u00e7\u0131s\u0131ndan bak\u0131\u015f at\u0131yor olacak ve client&#8217;lardan gelen yinelenen request&#8217;ler neticesinde yarat\u0131lan m\u00fckerrer durumlara kar\u015f\u0131n nas\u0131l \u00f6nlemler alabilece\u011fimizi ve&#46;&#46;&#46;<!-- AddThis Advanced Settings generic via filter on get_the_excerpt --><!-- AddThis Share Buttons generic via filter on get_the_excerpt --><\/p>\n","protected":false},"author":1,"featured_media":28031,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2739,3102],"tags":[5437,5438,5442,5435,5436,5441,5439,5440],"class_list":["post-28015","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-asp-net-core","tag-asp-net-core-idempotency","tag-asp-net-core-idempotent","tag-controller-based-idempotency","tag-idempotency","tag-idempotent","tag-minimal-api-idempotency","tag-rest-api-idempotency","tag-rest-api-idempotent"],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/28015","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/comments?post=28015"}],"version-history":[{"count":9,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/28015\/revisions"}],"predecessor-version":[{"id":28030,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/posts\/28015\/revisions\/28030"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media\/28031"}],"wp:attachment":[{"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/media?parent=28015"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/categories?post=28015"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.gencayyildiz.com\/blog\/wp-json\/wp\/v2\/tags?post=28015"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}