RESTful web API’si tasarımı

Birçok modern web uygulaması, istemcilerin uygulama ile etkileşimde bulunmak için kullanabileceği API'leri kullanıma sunar. İyi tasarlanmış bir web API’si şunları hedeflemeyi destekler:

  • Platform bağımsızlığı. API’nin içeride nasıl uygulandığına bakılmaksızın API’yi herhangi bir istemci çağırabilmelidir. Bunun için standart protokollerin kullanılması ve istemci ile web hizmetinin değişimi yapılacak veriler üzerinde anlaşabileceği bir mekanizmaya sahip olunması gerekir.

  • Hizmet gelişimi. Web API’si gelişebilmeli ve istemci uygulamalardan bağımsız olarak işlevsellik ekleyebilmelidir. API geliştikçe mevcut istemci uygulamalar değişiklik olmadan çalışmaya devam etmelidir. İstemci uygulamalarının tam olarak kullanabilmesi için tüm işlevler bulunabilir olmalıdır.

Bu rehber bir web API’si tasarlarken dikkate almanız gereken sorunları açıklamaktadır.

REST nedir?

2000 yılında Roy Fielding, web hizmetleri tasarlamaya yönelik mimari bir yaklaşım olarak Temsili Durum Transferi’ni (REST) önerdi. REST, hiper medyayı temel alan dağıtılmış sistemler oluşturmaya yönelik bir mimari stilidir. REST, temel alınan herhangi bir protokolden bağımsızdır ve mutlaka HTTP’ye bağlı olması gerekmez. Ancak, en yaygın REST API uygulamaları uygulama protokolü olarak HTTP kullanır ve bu kılavuz HTTP için REST API'leri tasarlamaya odaklanır.

REST'in HTTP'ye göre birincil avantajı, açık standartlar kullanması ve API'nin veya istemci uygulamalarının uygulanmasını belirli bir uygulamaya bağlamamasıdır. Örneğin, bir REST web hizmeti ASP.NET ile yazılabilir ve istemci uygulamalar, HTTP istekleri oluşturabilen ve HTTP yanıtlarını ayrıştırabilen herhangi bir dil ya da araç takımı kullanabilir.

HTTP kullanan RESTful API’lerinin temel tasarım ilkelerinden bazıları şunlardır:

  • REST API'leri, istemci tarafından erişilebilen herhangi bir nesne, veri ya da hizmet olabilecek kaynaklar çevresinde tasarlanır.

  • Bir kaynak, kendisini benzersiz bir şekilde tanımlayan URI biçiminde bir tanımlayıcıya sahiptir. Örneğin, belirli bir müşterinin siparişine ait URI şu şekilde olabilir:

    https://adventure-works.com/orders/1
    
  • İstemciler, kaynakların gösterimlerini takas ederek bir hizmetle etkileşimde bulunur. Birçok web API’si takas biçimi olarak JSON kullanır. Örneğin, yukarıda listelenen URI’ya gönderilen bir GET isteği şu yanıt gövdesini döndürebilir:

    {"orderId":1,"orderValue":99.90,"productId":1,"quantity":1}
    
  • REST API’leri, istemci ve hizmet uygulamalarını birbirinden ayırmaya yardımcı olan tek tip arabirim kullanır. HTTP üzerinde oluşturulan REST API'ler için tekdüzen arabirim, kaynaklarda işlem gerçekleştirmek için standart HTTP fiilleri kullanmayı içerir. En yaygın işlemler GET, POST, PUT, PATCH ve DELETE işlemleridir.

  • REST API’leri durum bilgisiz istek modeli kullanır. HTTP istekleri bağımsız olmalıdır ve herhangi bir sırada gerçekleşebilir, bu nedenle istekler arasında geçici durum bilgilerini tutmak mümkün değildir. Bilgiler yalnızca kaynaklarda depolanır ve her istek atomik bir işlem olmalıdır. İstemciler ile belirli sunucular arasında herhangi bir benzeşim sürdürmek gerekmediğinden bu kısıtlama, web hizmetlerinin yüksek oranda ölçeklenebilir olmasını sağlar. Herhangi bir sunucu, herhangi bir istemciden gelen herhangi bir isteği işleyebilir. Bununla birlikte, ölçeklenebilirlik diğer faktörlerle sınırlanabilir. Örneğin, birçok web hizmeti arka uç veri deposuna yazılır ve bu da ölçeği genişletmek zor olabilir. Veri deposu ölçeğini genişletme stratejileri hakkında daha fazla bilgi için bkz . Yatay, dikey ve işlevsel veri bölümleme.

  • REST API'leri, gösterimde yer alan hiper medya bağlantıları ile yönlendirilir. Örneğin, aşağıda bir siparişin JSON gösterimi gösterilmektedir. Siparişle ilişkili müşteriyi alma veya güncelleştirme bağlantılarını içerir.

    {
      "orderID":3,
      "productID":2,
      "quantity":4,
      "orderValue":16.60,
      "links": [
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"PUT" }
      ]
    }
    

2008 yılında Leonard Richardson, web API’leri için aşağıdaki olgunluk modelini önerdi:

  • Düzey 0: Bir URI tanımlandığında tüm işlemler bu URI’ye yönelik POST istekleridir.
  • Düzey 1: Ayrı kaynaklar için ayrı URI’ler oluşturma.
  • Düzey 2: Kaynaklar üzerinde işlemleri tanımlamak için HTTP yöntemleri kullanma.
  • Düzey 3: Hiper medya kullanma (aşağıda açıklanan HATEOAS).

Düzey 3, Fielding'in tanımına göre gerçek bir RESTful API'sine karşılık gelir. Uygulamada, yayımlanan birçok web API’si yaklaşık olarak Düzey 2’ye denk gelir.

API tasarımını kaynaklar etrafında düzenleme

Web API’sinin kullanıma sunduğu iş varlıklarına odaklanın. Örneğin, bir e-ticaret sisteminde birincil varlıklar müşteriler ve siparişler olabilir. Sipariş bilgilerini içeren bir HTTP POST isteği gönderilerek sipariş oluşturulabilir. HTTP yanıtı, siparişin başarılı bir şekilde oluşturulup oluşturulmadığını gösterir. Mümkün olduğunda, kaynak URI'leri fiiller (kaynak üzerindeki işlemler) yerine isimleri (kaynak) temel almalıdır.

https://adventure-works.com/orders // Good

https://adventure-works.com/create-order // Avoid

Kaynağın tek bir fiziksel veri öğesini temel almış olması gerekmez. Örneğin, bir sipariş kaynağı, ilişkisel bir veritabanı içinde birkaç tablo halinde uygulanabilir ancak istemciye tek varlık olarak sunulur. Yalnızca bir veritabanının iç yapısını yansıtan API’ler oluşturmaktan kaçının. REST’in amacı, varlıkları ve bir uygulamanın bu varlıklar üzerinde gerçekleştirebileceği işlemleri modellemektir. İstemci bir iç uygulamanın kullanımına sunulmamalıdır.

Varlıklar genellikle koleksiyonlar (siparişler, müşteriler) halinde gruplandırılır. Koleksiyon, koleksiyon içindeki öğeden ayrı bir kaynaktır ve kendi URI’sine sahip olmalıdır. Örneğin, aşağıdaki URI, sipariş koleksiyonunu temsil edebilir:

https://adventure-works.com/orders

Koleksiyon URI’sine bir HTTP GET isteği gönderildiğinde koleksiyondaki öğelerin listesi alınır. Koleksiyondaki her bir öğe kendi benzersiz URI'sine de sahiptir. Öğenin URI'sine gönderilen bir HTTP GET isteği bu öğenin ayrıntılarını döndürür.

URI'de tutarlı bir adlandırma kuralı benimseyin. Genel olarak, koleksiyonlara başvuran URI’ler için çoğul isimler kullanmak faydalı olur. Koleksiyon ve öğeler için URI'leri bir hiyerarşiye göre düzenlemek iyi bir uygulamadır. Örneğin, /customers müşteri koleksiyonunun yolu, /customers/5 ise müşterinin yoludur ve kimlik 5'e eşittir. Bu yaklaşım, web API’sinin sezgiselliğini korumaya yardımcı olur. Ayrıca, birçok web API çerçevesi, /customers/{id} yolu için bir rota tanımlayabilmeniz amacıyla istekleri parametreli URI yollarına yönlendirebilir.

Farklı kaynak türleri arasındaki ilişkileri ve bu ilişkileri nasıl kullanıma sunabileceğinize göz önünde bulundurun. Örneğin /customers/5/orders, müşteri 5 için tüm siparişleri temsil edebilir. Ayrıca diğer yönde gidebilir ve bir siparişin /orders/99/customer gibi bir URI’si olan müşteri ile ilişkisini açıklayabilirsiniz. Ancak, bu modelin çok fazla genişletilmesi zahmetli bir uygulama olabilir. HTTP yanıt iletisinin gövdesindeki ilişkili kaynakların gezinilebilir bağlantılarını sağlamak daha iyi bir çözümdür. Bu mekanizma, ilgili kaynaklara gezintiyi etkinleştirmek için HATEOAS kullanma bölümünde daha ayrıntılı olarak açıklanmıştır.

Daha karmaşık sistemlerde bir istemcinin /customers/1/orders/99/products gibi birkaç ilişki düzeyinde gezinmesini sağlayan URI’ler sağlamak cazip olabilir. Ancak, bu karmaşıklık düzeyinin sürdürülmesi zor olabilir ve gelecekte kaynaklar arasındaki ilişkiler değişirse esnekliği olmaz. Bunun yerine, URI’leri oldukça basit tutmaya çalışın. Bir uygulama bir kaynağa başvurduğunda bu başvuruyu o kaynakla ilgili öğeleri bulmak için kullanmak mümkündür. Yukarıdaki sorgu, müşteri 1’in tüm siparişlerini bulmak için /customers/1/orders URI’si ile, sonra da siparişteki ürünleri bulmak için /orders/99/products ile değiştirilebilir.

İpucu

koleksiyon/öğe/koleksiyon biçiminden daha karmaşık kaynak URI’leri gerektirmemeye özen gösterin.

Tüm web isteklerinin web sunucusu üzerinde bir yük oluşturması ise başka bir faktördür. İstek sayısı arttıkça yük büyür. Bu nedenle, çok sayıda küçük kaynağı kullanıma sunan "geveze" web API’lerinden kaçınmaya çalışın. Böyle bir API, bir istemci uygulamasının gerekli olan tüm verileri bulmak için birden çok istek göndermesini gerektirebilir. Bunun yerine, verileri normalleştirilmişlikten çıkarmak ve ilgili bilgileri tek bir istekle alınabilen daha büyük kaynaklar halinde birleştirmek isteyebilirsiniz. Ancak, bu yaklaşımı istemcinin ihtiyaç duymadığı verileri getirme yükü ile dengelemeniz gerekir. Büyük nesnelerin alınması bir isteğin gecikme süresini artırabilir ve ek bant genişliği maliyetleri doğurabilir. Bu performans anti-desenleri hakkında daha fazla bilgi için bkz. Chatty G/Ç ve Fazlalık Getirme.

Web API’si ile temel alınan veri kaynakları arasında bağımlılıklar oluşturmaktan kaçının. Örneğin, verileriniz ilişkisel bir veritabanında depolanıyorsa, web API’sinin her bir tabloyu bir kaynak koleksiyonu olarak kullanıma sunması gerekmez. Aslında bu, büyük olasılıkla kötü bir tasarım olacaktır. Bunun yerine, web API’sini veritabanının bir özeti olarak düşünün. Gerekirse, veritabanı ile web API’si arasında bir eşleme katmanı oluşturun. Bu şekilde, istemci uygulamalar değişikliklerden temel alınan veritabanı düzenine doğru yalıtılır.

Son olarak, bir web API’si tarafından uygulanan her işlemin belirli bir kaynakla eşlenmesi mümkün olmayabilir. Bu tür kaynak dışı senaryoları bir işlev çağıran ve sonuçları bir HTTP yanıt iletisi olarak döndüren HTTP istekleri aracılığıyla işleyebilirsiniz. Örneğin, toplama ve çıkarma gibi basit hesap makinesi işlemleri uygulayan bir web API’si, bu işlemleri sahte kaynak olarak kullanıma sunan ve gerekli parametreleri belirtmek üzere sorgu dizesini kullanan URI’ler sağlayabilir. Örneğin, /add?operand1=99&operand2=1 URI'sine yönelik bir GET isteği, gövdesi 100 değerini içeren bir yanıt iletisi döndürür. Ancak, bu tür URI'leri tedbirli şekilde kullanın.

API işlemlerini HTTP yöntemleri açısından tanımlama

HTTP protokolü bir isteğe anlam atayan birkaç yöntemi tanımlar. Çoğu RESTful web API’si tarafından kullanılan yaygın HTTP yöntemleri şunlardır:

  • GET, belirtilen URI’de kaynağın bir gösterimini alır. Yanıt iletisinin gövdesinde, istenen kaynağın ayrıntıları bulunur.
  • POST, belirtilen URI’de yeni bir kaynak oluşturur. İstek iletisinin gövdesi, yeni kaynağın ayrıntılarını sağlar. POST işleminin gerçekte kaynak oluşturmayan işlemleri tetiklemek için de kullanılabileceğini unutmayın.
  • PUT, belirtilen URI’de kaynağı oluşturur veya yenisiyle değiştirir. İstek iletisinin gövdesi, oluşturulacak veya güncelleştirilecek kaynağı belirtir.
  • PATCH, kaynağın kısmi bir güncelleştirmesini yapar. İstek gövdesi, kaynağa uygulanacak değişiklik kümesini belirtir.
  • DELETE, belirtilen URI’deki kaynağı kaldırır.

Belirli bir isteğin etkisi, kaynağın bir koleksiyon ya da tek öğe olmasına bağlı olmalıdır. Aşağıdaki tabloda, e-ticaret örneği kullanılarak çoğu RESTful uygulaması tarafından benimsenen yaygın kurallar özetlenmiştir. Bu isteklerin tümü uygulanamayabilir; bu, belirli bir senaryoya bağlıdır.

Kaynak POST GET PUT DELETE
/customers Yeni müşteri oluşturma Tüm müşterileri alma Müşterileri toplu güncelleştirme Tüm müşterileri kaldırma
/customers/1 Hata Müşteri 1’in ayrıntılarını alma Varsa müşteri 1’in ayrıntılarını güncelleştirme Müşteri 1’i kaldırma
/customers/1/orders Müşteri 1 için yeni bir sipariş oluşturma Müşteri 1 için tüm siparişleri alma Müşteri 1 için siparişleri toplu güncelleştirme Müşteri 1 için tüm siparişleri kaldırma

POST, PUT ve PATCH işlemleri arasındaki farklılıklar kafa karıştırıcı olabilir.

  • POST isteği bir kaynak oluşturur. Sunucu yeni kaynak için bir URI atar ve bu URI’yi istemciye döndürür. REST modelinde sıklıkla koleksiyonlara POST istekleri uygularsınız. Yeni kaynak, koleksiyona eklenir. Yeni bir kaynak oluşturmadan verileri işlenmek üzere var olan bir kaynağa göndermek için de POST isteği kullanılabilir.

  • PUT isteği bir kaynak oluşturur veya var olan bir kaynağı güncelleştirir. İstemci, kaynağın URI’sini belirtir. İstek gövdesi, kaynağın tam gösterimini içerir. Bu URI’ye sahip bir kaynak zaten varsa, değiştirilir. Aksi takdirde, sunucu tarafından destekleniyorsa yeni bir kaynak oluşturulur. PUT istekleri, koleksiyonlar yerine en sık olarak belirli bir müşteri gibi tek öğeler halindeki kaynaklara uygulanır. Bir sunucu, PUT işlemiyle güncelleştirmeleri destekleyebilir ancak oluşturmayı desteklemez. PUT işlemiyle oluşturmanın desteklenip desteklenmediği, istemcinin bir kaynağa var olmadan önce anlamlı bir şekilde URI atayıp atayamayacağına bağlıdır. Bu durum geçerli değilse, kaynakları oluşturmak için POST, güncelleştirmek için PUT veya PATCH işlemini kullanın.

  • Bir PATCH isteği, var olan bir kaynak üzerinde kısmi güncelleştirme uygular. İstemci, kaynağın URI’sini belirtir. İstek gövdesi, kaynağa uygulanacak bir değişiklik kümesini belirtir. İstemci, kaynağın tüm gösterimini değil yalnızca değişiklikleri gönderdiği için, bu işlem PUT kullanmaktan daha verimli olabilir. Teknik olarak, sunucu tarafından destekleniyorsa PATCH ayrıca yeni bir kaynak oluşturabilir ("null" bir kaynağa bir güncelleştirme kümesi belirterek).

PUT istekleri bir kez etkili olmalıdır. Bir istemci aynı PUT isteğini birden çok kez gönderirse, sonuçlar her zaman aynı olur (aynı kaynak, aynı değerlerle değiştirilir). POST ve PATCH isteklerinin bir kez etkili olması garanti edilmez.

HTTP semantiğine uygunluk

Bu bölümde, HTTP belirtimine uygun bir API tasarlamaya yönelik bazı genel konular açıklanmaktadır. Ancak, her olası ayrıntı veya senaryo ele alınmamıştır. Şüpheye düştüğünüzde HTTP belirtimlerine başvurun.

Medya türleri

Daha önce belirtildiği gibi istemciler ve sunucular, kaynakların gösterimlerini takas eder. Örneğin, bir POST isteğinde istek gövdesi, oluşturulacak kaynağın bir gösterimini içerir. Bir GET isteğinde ise yanıt gövdesi, getirilen kaynağın gösterimini içerir.

HTTP protokolünde biçimler, aynı zamanda MIME türleri olarak bilinen medya türleri kullanılarak belirtilir. İkili olmayan veriler için web API'lerinin çoğu JSON (medya türü = application/json) ve muhtemelen XML 'yi (medya türü = application/xml) destekler.

Bir istek veya yanıttaki Content-Type üst bilgisi, gösterimin biçimini belirtir. JSON verilerini içeren bir POST isteğinin örneği aşağıda verilmiştir:

POST https://adventure-works.com/orders HTTP/1.1
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

Sunucu medya türünü desteklemiyorsa, 415 (Desteklenmeyen Medya Türü) HTTP durum kodunu döndürmelidir.

Bir istemci isteği, istemcinin yanıt iletisinde sunucudan kabul edeceği medya türlerinin listesini içeren bir Accept üst bilgisi içerebilir. Örneğin:

GET https://adventure-works.com/orders/2 HTTP/1.1
Accept: application/json

Sunucu listelenen medya türlerinden herhangi biriyle eşleşemiyorsa HTTP durum kodu 406 (Kabul Edilemez) döndürmelidir.

GET yöntemleri

Başarılı bir GET yöntemi genellikle 200 (Tamam) HTTP durum kodunu döndürür. Kaynak bulunamazsa yöntem, 404 (Bulunamadı) kodunu döndürmelidir.

İstek yerine getirildiyse ancak HTTP yanıtına dahil edilmiş bir yanıt gövdesi yoksa, 204 (İçerik Yok) HTTP durum kodunu döndürmelidir; örneğin, eşleşme içermeyen bir arama işlemi bu davranışla uygulanabilir.

POST yöntemleri

POST yöntemi yeni bir kaynak oluşturursa 201 (Oluşturuldu) HTTP durum kodunu döndürür. Yeni kaynağın URI'si, yanıtın Location üst bilgisine dahil edilir. Yanıt gövdesi, kaynağın bir gösterimini içerir.

Yöntem aynı işlemi yapmasına rağmen yeni bir kaynak oluşturmazsa, 200 HTTP durum kodunu döndürebilir ve yanıt gövdesinde işlemin sonucunu içerebilir. Alternatif olarak, döndürülecek sonuç yoksa yöntem herhangi bir yanıt gövdesi olmadan 204 (İçerik Yok) HTTP durum kodunu döndürebilir.

İstemci isteğe geçersiz veriler yerleştirirse, sunucu 400 (Hatalı İstek) HTTP durum kodunu döndürmelidir. Yanıt gövdesi, hata hakkında ek bilgiler veya daha fazla ayrıntı sağlayan bir URI bağlantısı içerebilir.

PUT yöntemleri

PUT yöntemi yeni bir kaynak oluşturursa POST yönteminde olduğu gibi 201 (Oluşturuldu) HTTP durum kodunu döndürür. Yöntem var olan bir kaynağı güncelleştirirse 200 (Tamam) ya da 204 (İçerik Yok) durum kodunu döndürür. Bazı durumlarda, var olan bir kaynağı güncelleştirmek mümkün olmayabilir. Bu durumda 409 (Çakışma) HTTP durum kodunu döndürmeyi düşünün.

Bir koleksiyondaki birden fazla kaynağı toplu olarak güncelleştirebilen toplu HTTP PUT işlemleri uygulamayı düşünün. PUT isteği, koleksiyonun URI’sini belirtmelidir ve istek gövdesi, değiştirilecek kaynakların ayrıntılarını belirtmelidir. Bu yaklaşım, sık iletişim kurma sorununu azaltmaya ve performansı artırmaya yardımcı olabilir.

PATCH yöntemleri

İstemci PATCH isteğini kullanarak var olan bir kaynağa yama belgesi biçiminde bir güncelleştirme kümesi gönderir. Sunucu, güncelleştirmeyi gerçekleştirmek için yama belgesini işler. Yama belgesi kaynağın tamamını değil, yalnızca uygulanacak değişiklikler kümesini açıklar. PATCH yönteminin belirtimi (RFC 5789), yama belgeleri için belirli bir biçim tanımlamaz. Biçim, istekteki medya türünden anlaşılmalıdır.

Web API’leri için en sık kullanılan veri biçimi büyük olasılıkla JSON biçimidir. JSON yaması ve JSON birleştirme yaması adlı iki ana JSON temelli yama biçimi vardır.

JSON birleştirme yaması biraz daha basittir. Yama belgesi, özgün JSON kaynağı ile aynı yapıda olmasına karşın yalnızca değiştirilmesi veya eklenmesi gereken alanların alt kümesini içerir. Ayrıca, yama belgesindeki alan değeri için null belirtilerek bir alan silinebilir. (Diğer bir deyişle, özgün kaynak açıkça null değerler alabiliyorsa birleştirme yaması uygun değildir.)

Örneğin, özgün kaynağın aşağıdaki JSON gösterimine sahip olduğunu varsayın:

{
    "name":"gizmo",
    "category":"widgets",
    "color":"blue",
    "price":10
}

Bu kaynak için olası bir JSON birleştirme yaması şu şekildedir:

{
    "price":12,
    "color":null,
    "size":"small"
}

Bu, sunucuya güncelleştirmesini, silmesini pricecolorve eklemesini sizebildirir, ancak namecategory değiştirilmez. JSON birleştirme yamasının tam ayrıntıları için bkz. RFC 7396. JSON birleştirme düzeltme eki için medya türü şeklindedir application/merge-patch+json.

Özgün kaynak açıkça null değerler içerebiliyorsa, null değerinin yama belgesindeki özel anlamı nedeniyle birleştirme yaması uygun değildir. Ayrıca yama belgesi, sunucunun güncelleştirmeleri uygulaması gereken sırayı belirtmez. Bu durum, verilere ve etki alanına bağlı olarak önemli veya önemsiz olabilir. RFC 6902 içinde tanımlanan JSON yaması daha esnektir. Değişiklikleri, uygulanacak işlemlerin sırası olarak belirtir. Ekleme, kaldırma, değiştirme, kopyalama ve test etme (değerleri doğrulamak için) işlemleri mevcuttur. JSON düzeltme ekinin medya türü şeklindedir application/json-patch+json.

Bir PATCH isteği işlenirken karşılaşılabilecek tipik hata koşullarından bazıları, uygun HTTP durum kodu ile birlikte aşağıda verilmiştir.

Hata koşulu HTTP durum kodu
Yama belgesinin biçimi desteklenmiyor. 415 (Desteklenmeyen Medya Türü)
Hatalı biçimlendirilmiş yama belgesi. 400 (Hatalı İstek)
Yama belgesi geçerli, ancak değişiklikler mevcut durumunda kaynağa uygulanamıyor. 409 (Çakışma)

DELETE yöntemleri

Silme işlemi başarılı olursa, web sunucusunun http durum kodu 204 (İçerik Yok) ile yanıt vermesi gerekir; bu da işlemin başarıyla işlendiğini, ancak yanıt gövdesinin başka bilgi içermediğini belirtir. Kaynak mevcut değilse, web sunucusu HTTP 404 (Bulunamadı) kodunu döndürebilir.

Zaman uyumsuz işlemler

Bazen POST, PUT, PATCH veya DELETE işlemlerinin tamamlanması biraz zaman alabilir. İstemciye yanıt göndermeden önce tamamlanmasını beklerseniz, bu kabul edilemez bir gecikmeye neden olabilir. Bu durumda, işlemi zaman uyumsuz hale getirmeyi düşünün. İsteğin işleme alındığını ancak tamamlanmadığını belirtmek üzere 202 (Kabul Edildi) HTTP durum kodunu döndürün.

İstemcinin durum uç noktasını yoklayarak durumu izleyebilmesi için, zaman uyumsuz bir isteğin durumunu döndüren bir uç noktayı kullanıma sunmanız gerekir. Durum uç noktasının URI’sini 202 yanıtının Location üst bilgisine ekleyin. Örneğin:

HTTP/1.1 202 Accepted
Location: /api/status/12345

İstemci bu uç noktaya bir GET isteği gönderirse yanıt, isteğin geçerli durumunu içermelidir. İsteğe bağlı olarak, tahmini bir tamamlama süresi veya işlemi iptal etme bağlantısı da içerebilir.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}

Zaman uyumsuz işlem yeni bir kaynak oluşturursa, işlem tamamlandıktan sonra durum uç noktası 303 (Diğerine Bakın) durum kodunu döndürmelidir. 303 yanıtına, yeni kaynağın URI’sini veren bir Location üst bilgisi ekleyin:

HTTP/1.1 303 See Other
Location: /api/orders/12345

Bu yaklaşımı uygulama hakkında daha fazla bilgi için bkz . Uzun süre çalışan istekler için zaman uyumsuz destek sağlama ve Zaman Uyumsuz İstek-Yanıt düzeni.

İleti gövdelerinde boş kümeler

Başarılı bir yanıtın gövdesi boş olduğunda, durum kodu 204 (İçerik Yok) olmalıdır. Öğe içermeyen filtrelenmiş bir isteğe yanıt gibi boş kümeler için durum kodu 200 değil 204 (İçerik Yok) olmalıdır (Tamam).

Verileri filtreleme ve sayfalandırma

Bir kaynak koleksiyonunu tek bir URI aracılığıyla kullanıma sunmak, uygulamaların yalnızca bir bilgi alt kümesi gerekliyken büyük miktarlarda veri getirmesine neden olabilir. Örneğin, bir istemci uygulamanın, maliyeti belirli bir değerin üzerinde olan tüm siparişleri bulması gerektiğini varsayalım. Tüm siparişleri /orders URI’sinden alıp sonra bu siparişleri istemci tarafında fitreleyebilir. Açıkçası bu işlem son derece verimsizdir. Web API’sini barındıran sunucuda gereksiz ağ bant genişliği ve işlem gücü harcar.

Bunun yerine API, /orders?minCost=n gibi URI’nin sorgu dizesine bir filtre geçirmeye izin verebilir. Web API'si daha sonra sorgu dizesindeki parametreyi minCost ayrıştırıp işlemek ve filtrelenmiş sonuçları sunucu tarafında döndürmekten sorumludur.

Koleksiyon kaynakları üzerindeki GET istekleri, çok sayıda öğe döndürebilir. Tek bir istek tarafından döndürülen veri miktarını sınırlamak için bir web API’si tasarlamanız gerekir. Alınacak en fazla öğe sayısını ve koleksiyon ile başlangıç uzaklığını belirten sorgu dizelerini desteklemeyi düşünün. Örneğin:

/orders?limit=25&offset=50

Ayrıca, Hizmet Reddi saldırılarını önlemeye yardımcı olmak amacıyla, döndürülen öğe sayısına bir üst sınır getirmeyi düşünün. İstemci uygulamalara yardımcı olmak için, sayfalandırılmış veriler döndüren GET istekleri koleksiyondaki kullanılabilir kaynakların toplam sayısını belirten bir tür meta veri içermelidir.

Değer olarak /orders?sort=ProductID gibi bir alan adı alan sıralama parametresi sağlayarak, verileri getirildiği gibi sıralamak için benzer bir strateji kullanabilirsiniz. Ancak, sorgu dizesi parametreleri önbelleğe alınmış verilerin anahtarı olarak çok sayıda önbellek uygulaması tarafından kullanılan kaynak tanımlayıcısının bir kısmını oluşturduğundan, bu yaklaşım önbelleğe almayı olumsuz yönde etkileyebilir.

Her öğe büyük miktarda veri içeriyorsa, bu yaklaşımı her bir öğe için döndürülen alanları sınırlayacak şekilde genişletebilirsiniz. Örneğin, /orders?fields=ProductID,Quantity gibi alanların virgülle ayrılmış listesini kabul eden bir sorgu dizesi parametresini kullanabilirsiniz.

Sorgu dizelerindeki tüm isteğe bağlı parametrelere anlamlı varsayılanlar atayın. Örneğin, sayfalama uyguluyorsanız limit parametresini 10 ve offset parametresini 0 olarak ayarlayın, sıralama uyguluyorsanız sıralama parametresini kaynağın anahtarına ayarlayın ve projeksiyonları destekliyorsanız fields parametresini kaynağın tüm alanlarına ayarlayın.

Büyük ikili kaynaklar için kısmi yanıtları destekleme

Bir kaynak, dosya ya da görüntü gibi büyük ikili alanlar içerebilir. Güvenilmez ve aralıklı bağlantıların neden olduğu sorunları aşmak ve yanıt sürelerini iyileştirmek için, bu tür kaynakların öbek halinde alınmasını sağlayın. Bunu yapmak için, web API’si büyük kaynaklara yönelik GET istekleri için Accept-Ranges üst bilgisini desteklemelidir. Bu üst bilgi, GET işleminin kısmi istekleri desteklediğini gösterir. İstemci uygulama, bayt aralığı cinsinden belirtilen bir kaynak alt kümesi döndüren GET istekleri gönderebilir.

Ayrıca, bu kaynaklar için HTTP HEAD isteklerini uygulamayı düşünün. HEAD isteği, boş bir ileti gövdesi ile yalnızca kaynağı tanımlayan HTTP üst bilgileri döndürmesi dışında bir GET isteği ile benzerdir. İstemci uygulama, kısmi GET istekleri kullanarak bir kaynağın getirilip getirilmeyeceğini belirlemek üzere HEAD isteği gönderebilir. Örneğin:

HEAD https://adventure-works.com/products/10?fields=productImage HTTP/1.1

Örnek bir yanıt iletisi aşağıda verilmiştir:

HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580

Content-Length üst bilgisi, kaynağın toplam boyutunu verirken Accept-Ranges üst bilgisi, ilgili GET işleminin kısmi sonuçları desteklediğini gösterir. İstemci uygulama bu bilgileri kullanarak görüntüyü daha küçük öbekler halinde alabilir. İlk istek, Range üst bilgisini kullanarak ilk 2500 baytı getirir:

GET https://adventure-works.com/products/10?fields=productImage HTTP/1.1
Range: bytes=0-2499

Yanıt iletisi, 206 HTTP durum kodunu döndürerek bunun kısmi bir yanıt olduğunu gösterir. Content-Length üst bilgisi, ileti gövdesinde döndürülen gerçek bayt sayısını (kaynağın boyutunu değil) belirtirken Content-Range üst bilgisi, bunun kaynağın hangi kısmı olduğunu gösterir (4580 bayt içinden 0-2499 bayt):

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580

[...]

İstemci uygulamadan alınan sonraki bir istek, kaynağın geri kalanını alabilir.

REST’in ardında yatan başlıca motivasyonlardan biri, URI şemasının önceden bilinmesine gerek olmadan tüm kaynak kümesinde gezinmenin mümkün olması gerekliliğidir. Her HTTP GET isteği, yanıta eklenen köprü bağlantılarla istenen nesne ile doğrudan ilgili kaynakları bulmak için gereken bilgileri döndürmeli ve ayrıca bu kaynakların her birinde kullanılabilen işlemleri açıklayan bilgiler her isteğe sağlanmalıdır. Bu ilke, HATEOAS ya da Uygulama Durumu Altyapısı Olarak Köprü Metni olarak bilinir. Sistem aslında sınırlı bir durum makinesidir ve her bir isteğe verilen yanıt, bir durumdan diğerine geçmek için gereken bilgileri içerir; başka bir bilgi gerekli olmamalıdır.

Not

Şu anda HATEOAS ilkesinin nasıl modellendiğini tanımlayan genel amaçlı standartlar yoktur. Bu bölümde gösterilen örneklerde olası, özel bir çözüm gösterilmektedir.

Örneğin, bir sipariş ile müşteri arasındaki ilişkiyi işlemek için sipariş gösterimine siparişin müşterisi için kullanılabilir işlemleri tanımlayan bağlantılar eklenebilir. Olası bir gösterim şu şekildedir:

{
  "orderID":3,
  "productID":2,
  "quantity":4,
  "orderValue":16.60,
  "links":[
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"DELETE",
      "types":[]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"DELETE",
      "types":[]
    }]
}

Bu örnekte links dizisi bir bağlantı kümesi içerir. Her bağlantı, ilgili bir varlık üzerindeki bir işlemi temsil eder. Her bağlantının verileri ilişki ("müşteri"), URI (https://adventure-works.com/customers/3), HTTP yöntemi ve desteklenen MIME türlerini içerir. Bir istemci uygulamanın işlemi çağırabilmesi için gereken tüm bilgiler bunlardır.

links dizisi ayrıca alınmış kaynak hakkında kendi kendine başvuran bilgiler içerir. Bunlar kendi ilişkisine sahiptir.

Döndürülen bağlantı kümesi, kaynağın durumuna bağlı olarak değişebilir. Köprü metninin "uygulama durumu altyapısı" olması bu anlama gelir.

RESTful web API'si sürümü oluşturma

Bir web API'sinin statik kalma olasılığı düşüktür. İş gereksinimleri değiştikçe yeni kaynak koleksiyonları eklenebilir, kaynaklar arasındaki ilişkiler değişebilir ve kaynaklardaki verilerin yapısı değiştirilebilir. Bir web API'sini yeni ya da farklı gereksinimleri karşılayacak şekilde güncelleştirmek oldukça basit bir işlem olsa da, bu tür değişikliklerin web API'sini kullanan istemci uygulamalar üzerinde bırakacağı etkileri göz önünde bulundurmanız gerekir. Sorun, web API'sini tasarlayan ve uygulayan geliştiricinin bu API üzerinde tam denetime sahip olmasına rağmen, geliştiricinin uzaktan çalışan üçüncü taraf kuruluşlar tarafından oluşturulabilecek istemci uygulamaları üzerinde aynı düzeyde denetime sahip olmamasıdır. Mevcut istemci uygulamaların değişmeden çalışmaya devam etmesini sağlarken yeni istemci uygulamaların yeni özellik ve kaynaklardan yararlanmasına olanak tanımak birincil zorunluluktur.

Sürüm oluşturma, bir web API’sinin kullanıma sunduğu özellik ve kaynakları göstermesini sağlar ve bir istemci uygulama, bir özellik ya da kaynağın belirli bir sürümüne yöneltilmiş istekler gönderebilir. Aşağıdaki bölümlerde, her biri kendi avantaj ve dezavantajlarına sahip birkaç farklı yaklaşım açıklanmaktadır.

Sürüm oluşturma yok

En basit yaklaşımdır ve bazı iç API'ler için kabul edilebilir. Önemli değişiklikler yeni kaynaklar veya yeni bağlantılar olarak gösterilebilir. Mevcut kaynaklara içerik eklemek, bu içeriği görmeyi beklemeyen istemci uygulamaları bunu yoksayacağı için hataya neden olan bir değişiklik sunmayabilir.

Örneğin, URI'ye https://adventure-works.com/customers/3 yönelik bir istek, istemci uygulaması tarafından beklenen , nameve address alanlarını içeren idtek bir müşterinin ayrıntılarını döndürmelidir:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Not

Kolaylık olması için bu bölümde gösterilen örnek yanıtlar HATEOAS bağlantılarını içermez.

DateCreated alanı, müşteri kaynağının şemasına eklenirse yanıt şu şekilde görünür:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

Mevcut istemci uygulamalar tanınmayan alanları yok sayma becerisine sahipse doğru şekilde çalışmaya devam edebilir, yeni istemci uygulamalar ise bu yeni alanı işleyecek şekilde tasarlanabilir. Ancak, kaynak şemasında çok daha köklü değişiklikler (alanları kaldırma veya yeniden adlandırma gibi) oluşur ya da kaynaklar arasındaki ilişki değişirse, bunlar mevcut istemci uygulamaların doğru şekilde çalışmasını engelleyen önemli değişiklikler olabilir. Bu gibi durumlarda, aşağıdaki yaklaşımlardan birini göz önünde bulundurmalısınız.

URI sürümü oluşturma

Web API’sini veya kaynak şemasını değiştirdiğiniz her durumda, her bir kaynak için URI’ye bir sürüm numarası eklersiniz. Önceden var olan URI'ler önceki gibi çalışmaya devam etmeli ve özgün şemalarına uygun kaynakları döndürmelidir.

Önceki örneği genişletmek için, alan adresin address her bir kurucu bölümünü (, , stateve zipCodegibistreetAddresscity) içeren alt alanlara yeniden yapılandırılmışsa, kaynağın bu sürümü gibi https://adventure-works.com/v2/customers/3bir sürüm numarası içeren bir URI aracılığıyla gösterilebilir:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Bu sürüm oluşturma mekanizması çok basittir, ancak isteği uygun uç noktaya yönlendiren sunucuya bağlıdır. Ancak, web API’si birkaç yineleme ile olgunlaştığından ve sunucunun birkaç farklı sürümü desteklemesi gerektiğinden bu mekanizmanın uygulanması giderek zorlaşabilir. Ayrıca, bir puristin bakış açısından, her durumda istemci uygulamaları aynı verileri (müşteri 3) getirir, bu nedenle URI sürüme bağlı olarak gerçekten farklı olmamalıdır. Tüm bağlantıların URI’lerine sürüm numarasını eklemesi gerekeceğinden, bu düzen HATEOAS uygulamasını da karmaşık hale getirir.

Sorgu dizesi sürümü oluşturma

Birden çok URI sağlamak yerine, HTTP isteğine eklenen sorgu dizesi içinde bir parametre kullanarak kaynağın sürümünü belirtebilirsiniz; örneğin https://adventure-works.com/customers/3?version=2. Sürüm parametresi, eski istemci uygulamalar tarafından çıkarılması durumunda varsayılan olarak 1 gibi anlamlı bir değerde olmalıdır.

Bu yaklaşım, aynı kaynağın her zaman aynı URI’den alınmasını sağlayan semantik avantaja sahiptir, ancak sorgu dizesini ayrıştırmak ve uygun HTTP yanıtını geri göndermek için isteği işleyen koda bağımlıdır. Bu yaklaşım ayrıca URI sürümü oluşturma mekanizması ile aynı HATEOAS uygulama zorluklarını yaşar.

Not

Bazı eski web tarayıcıları ve web ara sunucuları, URI’ye sorgu dizesi ekleyen isteklere yönelik yanıtları önbelleğe almaz. Bu, web API'sini kullanan ve böyle bir web tarayıcısının içinden çalışan web uygulamalarının performansını düşürebilir.

Üst bilgi sürümü oluşturma

Sürüm numarasını bir sorgu dizesi parametresi olarak ekleme yerine, kaynağın sürümünü belirten özel bir üst bilgi uygulayabilirsiniz. Bu yaklaşım, sürüm üst bilgisi kullanılmazsa istemci isteğini işleyen kodun varsayılan bir değer (sürüm 1) kullanabilmesine karşın, istemci uygulamanın herhangi bir isteğe uygun üst bilgiyi eklemesini gerektirir. Aşağıdaki örneklerde Custom-Header adlı özel bir üst bilgi kullanılır. Bu üst bilginin değeri, web API’sinin sürümünü gösterir.

Sürüm 1:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Sürüm 2:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Önceki iki yaklaşımda olduğu gibi HATEOAS'ın uygulanması için tüm bağlantılara uygun özel üst bilgi dahil edilmesi gerekir.

Medya türü sürümü oluşturma

İstemci uygulama bir web sunucusuna HTTP GET isteği gönderdiğinde, bu rehberin önceki kısımlarında anlatıldığı gibi bir Accept üst bilgisi kullanarak, işleyebileceği içeriğin biçimini belirtmelidir. Accept üst bilgisinin amacı genellikle yanıt gövdesinin XML, JSON ya da istemci tarafından ayrıştırılabilen başka bir yaygın biçim olup olmadığının istemci uygulama tarafından belirtilmesine olanak tanımaktır. Ancak, istemci uygulamanın hangi kaynak türünü beklediğini belirtmesini sağlayan bilgiler içeren özel medya türleri tanımlamak mümkündür.

Aşağıdaki örnekte, application/vnd.adventure-works.v1+json değeri ile Accept üst bilgisi belirten bir istek gösterilmiştir. vnd.adventure-works.v1 öğesi, web sunucusuna kaynağın sürüm 1’ini döndürmesi gerektiğini, json öğesi ise yanıt gövdesi biçiminin JSON olması gerektiğini belirtir:

GET https://adventure-works.com/customers/3 HTTP/1.1
Accept: application/vnd.adventure-works.v1+json

İsteği işleyen kod, Accept üst bilgisini işlemekten ve mümkün olduğunca uzak onaylamaktan sorumludur (istemci uygulama, Accept üst bilgisinde birden fazla biçim belirtebilir ve bu durumda web sunucusu, yanıt gövdesi için en uygun biçimi seçebilir). Web sunucusu, Content-Type üst bilgisini kullanarak yanıt gövdesindeki verilerin biçimi onaylar:

HTTP/1.1 200 OK
Content-Type: application/vnd.adventure-works.v1+json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Accept üst bilgisi bilinen bir medya türü belirtmezse, web sunucusu bir HTTP 406 (Kabul Edilemez) yanıt iletisi oluşturur ya da varsayılan medya türünü içeren bir ileti döndürür.

Bu yaklaşım tartışmaya açık bir şekilde sürüm oluşturma mekanizmalarının en sade olanıdır ve kendisini, kaynak bağlantılarında ilgili verilerin MIME türünü içerebilen HATEOAS seçeneğine borçludur.

Not

Bir sürüm oluşturma stratejisi seçerken, başta web sunucusunda önbelleğe alma olmak üzere performansa etkilerini de göz önünde bulundurmanız gerekir. URI sürümü oluşturma ve Sorgu Dizesi sürümü oluşturma düzenleri, URI/sorgu dizesi birleşimi her seferinde aynı verilere başvurduğu sürece önbelleğe almaya uygundur.

Üst Bilgi sürümü oluşturma ve Medya Türü sürümü oluşturma mekanizmaları genellikle özel üst bilgi veya Accept üst bilgisi içindeki değerleri incelemek için ilave bir mantık gerektirir. Büyük ölçekli bir ortamda, bir web API'sinin farklı sürümlerini kullanan çok sayıda istemci, sunucu tarafı önbelleğinde önemli miktarda yinelenen veri ile sonuçlanabilir. Bir istemci uygulama, önbelleğe alma işlemini uygulayan bir ara sunucu üzerinden bir web sunucusu ile iletişim kurarsa ve o anda önbelleğinde istenen verilerin bir kopyasını tutmaması durumunda bu iletişim yalnızca web sunucusuna bir istek iletiyorsa, bu sorun ağırlaşabilir.

Open API Initiative

Open API Initiative, farklı satıcıların REST API açıklamalarını standart hale getirmek için bir sektör konsorsiyumu tarafından oluşturulmuştur. Bu girişimin bir parçası olarak Swagger 2.0 belirtimi, OpenAPI Belirtimi (OAS) olarak yeniden adlandırılmış ve Open API Initiative kapsamına alınmıştır.

Web API'leriniz için OpenAPI'yi benimsemek isteyebilirsiniz. Dikkat edilmesi gereken bazı noktalar:

  • OpenAPI Belirtimi, bir REST API’nin nasıl tasarlanması gerektiğine ilişkin görüşler içeren bazı yönergelerle birlikte gönderilir. Bu seçenek, birlikte çalışabilirlik için avantajlar sunmasına karşın API’nizi tasarlarken belirtime uymak için daha fazla dikkat gerektirir.

  • OpenAPI, uygulama öncelikli yaklaşım yerine sözleşme öncelikli bir yaklaşımı destekler. Sözleşme öncelikli yaklaşım, önce API sözleşmesini (arabirim) tasarlamanız ve sonra sözleşmeyi uygulayan kodu yazmanız anlamına gelir.

  • Swagger gibi araçlar, API sözleşmelerinden istemci kitaplıkları veya belgeler oluşturabilir. Örneğin, bkz . Swagger kullanarak web API'si yardım sayfalarını ASP.NET.

Sonraki adımlar