JSON API Standartları
REST API yazarken, her defasında oluşturulacak veri kontraktı üzerine oturup, tartışılır. JSON API ise tam olarak bu soruna ışık tutar. Ekibinizin JSON isteklerinin ve cevaplarının hangi formatta ve ne şekilde olduğuyla ilgilenmek yerine asıl işiniz olan uygulama geliştirimine odaklanmanızı sağlar. Verimliliğiniz artar, ve genel bir servis formatı elde edersiniz. JSON API kullanan client’larınız ise etkin cache mekanizmalarına sahip olur, ve gerektiğinde yapılan birçok isteği tek bir isteğe indirgeyerek çoklu network taleplerini ortadan kaldırmış olurlar.
Nedir?
JSON API, ilgili veri kaynaklarının getirilmesi ve işlenmesi için istemci tarafından oluşturulacak isteklerin nasıl yapılması gerektiğini, ve sunucunun bu isteklere hangi şekilde yanıt vermesi gerektiğini belirler.
JSON API, istemciler ve sunucular arasındaki istek sayısının ve giden/gelen veri boyutunun azaltılması için tasarlanmıştır. JSON API, oluşturulacak veri değiş-tokuşu için JSON API medya türünün (application/vnd.api+json) kullanımını gerektirir.
JSON API kolaydır ve kendi kendini betimler
Sunucudan dönen cevaba baktığınızda yapılan isteğin ne olduğunu ve ne işe yaradığını kolayca anlayabilirsiniz. Örneğin aşağıdaki JSON API isteği ve cevabına bakalım:
İstek:
GET /articles/1 HTTP/1.1 Accept: application/vnd.api+json
Cevap:
HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "data":{ "type":"articles", "id":"1", "attributes":{ "title":"WebAssembly: Web'in Geleceği" }, "links":{ "self":"http://devnot.com/articles/1" } } }
Gelen cevapta ilk olarak data
elemanı gözümüze çarpıyor. Temel bir JSON API cevap ağacında yer alan ve veriyi tutan kök eleman data
elemanıdır ve veri ile ilgili diğer bilgiler bu eleman altında barındırılır. Eğer herhangi bir hata oluşursa, hata ile ilgili bilgiler errors
kök elemanı oluşturularak bu eleman altında belirlenmesi gerekir. İlgili gerekli üst bilgiler ise meta
kök elemanı altında yer alabilir.
Sonraki eleman olan type
, dönen isteğin tipi belirtilir. type
‘ı, yazdığımız model sınıfları gibi düşünebiliriz.
Not: Burada type
elemanını çoğul halde ele aldık fakat bu bağlamda belirli bir kısıtlama yoktur ve isteğinize göre tekil olarak da alabilirsiniz. Ancak yazdığınız servisin tamamında type
elemanını tutarlılık açısından her zaman tekil veya her zaman çoğul olarak kullanmalısınız.
id
elemanı gelen makalenin id’sini belirler. Her cevapta type
ve id
elemanları string tipinde bulunmalıdır.
attributes
elemanını, makale modelinin değişkenleri olarak düşünebiliriz. İçerisinde model sınıfımızda yer alan özellikler barınabilir (Örneğimizde makalenin başlığını tutan “title” değişkenini kullandık).
links
elemanında istenen kaynak ile ilgili linkler bulunur. self
elemanı ise mevcut isteğin linkini betimler.
Üstteki bilgiler kapsamında oluşturulan JSON dokümanı/nesnesi ile daha anlamlı istek ve cevapların oluşturulması sağlanmış olur.
Bileşik Cevaplar Oluşturulabilir
Belirli bir istek ile ilişkili diğer veriler için ayrı ayrı HTTP isteklerinin yapılması yerine include
sorgu parametresi kullanarak ilişkili kaynak tipleri URL sonuna eklenir. Örneğin makale ve ilgili makalenin yazarını getiren bir istek oluşturalım:
İstek:
GET /articles/1?include=author HTTP/1.1 Accept: application/vnd.api+json
Sunucudan geri dönen cevap verisi ise aşağıdaki şekilde olur:
HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "data":{ "type":"articles", "id":"1", "attributes":{ "title":"WebAssembly: Web'in Geleceği" }, "links":{ "self":"http://devnot.com/articles/1?include=author" }, "relationships":{ "author":{ "data":{ "id":"42", "type":"people" } } } }, "included":[ { "type":"people", "id":"42", "attributes":{ "name":"Zafer AYAN", "gender":"Male" } } ] }
Dönen Cevaptan Sadece İstenen Alanlar Alınabilir
Sunucu tarafından dönen cevapta, o an istenenden daha fazla bilginin yer aldığı durumlar oluşabilir. Bu gibi durumlarda HTTP isteklerinde gereksiz bilgilerin geri döndürülmemesi ve verimli bir şekilde ağın etkin olarak kullanılması açısından JSON API, sadece ilgili alanların alınması sağlayan bir standart sunar. Örneğin aşağıdaki şekilde fields
parametresi kullanılarak servisten sadece makalenin başlığı ve makale metni ile yazarın adının alınması sağlanabilir:
?fields[articles]=title,body&fields[people]=name
Kayıt Ekleme İşlemleri
Kayıt ekleme işlemlerinde ilgili kaynağı betimleyen URL’e POST isteği gönderilir. Yapılan istekte temel veri olarak sadece bir adet data
objesi içermelidir. data
objesi de en az bir adet type
elemanına sahip olmalıdır. Aşağıdaki örnekte fotoğraf ekleme isteği mevcuttur:
POST /photos HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data":{ "type":"photos", "attributes":{ "title":"Web Assembly", "src":"http://devnot.com/photos/wasm.jpg" }, "relationships":{ "photographer":{ "data":{ "type":"people", "id":"9" } } } } }
İstemci Tarafında ID Oluşturma
Sunucu, ilgili kaydın oluşturulmasında istemci tarafında üretilen bir ID kullanabilir. Oluşturulacak ID benzersiz olmalı, id
alanı ile belirtilmeli ve RFC 4122 dokümanında yer aldığı gibi UUID türünde formatlandırılmış olmalıdır.
Örnek istek:
POST /photos HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data":{ "type":"photos", "id":"550e8400-e29b-41d4-a716-446655440000", "attributes":{ "title":"Web Assembly", "src":"http://devnot.com/photos/wasm.jpg" } } }
Not: Eğer halihazırda veritabanında aynı UUID’ye sahip kayıt varsa sunucu 403 Forbidden cevabını dönmelidir.
Sunucu cevapları
201 Created
Eğer bir POST isteği istemci tarafında oluşturulmuş ID içermiyorsa ve istenilen kayıt başarıyla oluşturulmuşsa sunucu 201 Created
durum kodunu dönmek zorundadır.
Oluşturulan cevap, yeni oluşturulan kaydın linkini belirten HTTP Location
başlığını içermelidir.
Dönen data
nesnesi oluşturulan kaydın bilgilerini içermek zorundadır.
Eğer sunucu tarafından döndürülecek cevaptaki “links” elemanı, “self” anahtarını içeriyorsa ve cevapta Location başlığı yer almışsa, self elemanı Location başlığındaki link ile aynı değere sahip olmalıdır. Örnek:
HTTP/1.1 201 Created Location: http://devnot.com/photos/550e8400-e29b-41d4-a716-446655440000 Content-Type: application/vnd.api+json { "data":{ "type":"photos", "id":"550e8400-e29b-41d4-a716-446655440000", "attributes":{ "title":"Web Assembly", "src":"http://devnot.com/photos/wasm.jpg" }, "links":{ "self":"http://devnot.com/photos/550e8400-e29b-41d4-a716-446655440000" } } }
202 Accepted
Eğer bir kayıt, ekleme isteği sunucu tarafından işlenmek için alınmışsa ve sunucu cevap verdiği anda hala isteğin işlenme süreci devam ediyorsa, sunucu 202 Accepted
durum kodunu dönmek zorundadır.
204 No Content
Eğer POST işlemi, istemci tarafında oluşturulan id
‘yi içeriyorsa ve istenilen kayıt başarıyla oluşturulmuşsa, sunucu ya 201
Created durum kodu ile üstte belirtilen data
nesnesini dönmeli, ya da 204 No Content
durum kodunu içeren ve herhangi bir JSON nesnesi/dokümanı içermeyen cevap dönmek zorundadır.
403 Forbidden
Kaydın oluşturulması için desteklenmeyen bir istek gönderilmişse sunucu 403 Forbidden
durum kodunu döndürebilir.
404 Not Found
Eklenecek kayıt ile ilgili kaynağın (örn: yukarıda belirtilen fotoğraf dosyası) bulunmadığı durumlarda, sunucu 404 Not Found durum kodunu döndürmelidir.
409 Conflict
İstemci taraflı oluşturulan id
halihazırda bulunuyorsa, sunucu 409 Conflict
durum kodunu döndürmek zorundadır.
POST isteğinde yer alan type
alanının değeri, servis tarafından belirtilen tipler arasında bulunmadığı durumda da sunucu 409 Conflict
HTTP durum kodunu döndürmek zorundadır.
Sunucu, hata detaylarını ve oluşan çakışmanın anlaşılması ile ilgili yeterli bilgiyi sunmalıdır.
Diğer Sunucu Cevapları
Sunucu diğer HTTP durum kodlarını kullanarak cevap dönebilir ve oluşan hatalı cevaplar, ilgili hata detaylarını içerebilir. Sunucu, HTTP Semantiğine göre cevapları oluşturmak zorunda ve istemci de bu bağlamda cevapları yorumlamak zorundadır.
Güncelleme İşlemleri ve PATCH Başlığının Kullanımı
REST servislerinde HTTP’nin doğası gereği yeni bir kaynak oluşturma işleminde POST
, kaynağın güncellenmesi işleminde ise PATCH
başlığının kullanılması gereklidir. Fakat günümüzde yazılan birçok REST servisi, güncelleme işlemleri için POST veya PUT başlıkları kullanmaktadır. Bu kullanım yanlış olmakla birlikte Internet Explorer 8 ve önceki sürümlerin PATCH isteklerini desteklemediği dönemden kalma bir alışkanlıktır.
PATCH isteğinde birincil veri olarak tek bir data
nesnesi bulunmalıdır ve bu nesne type
ve id
parametrelerini içermek zorundadır.
Örnek bir PATCH başlığının kullanımına bakalım:
PATCH /articles/1 HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data":{ "type":"articles", "id":"1", "attributes":{ "title":"JavaScript vs. WebAssembly" } } }
Bu cevapta articles türünde, id’si 1 olan makalenin title’ı “JavaScript vs. WebAssembly” olarak atanır.
Kaydın Özelliklerinin Güncellenmesi
PATCH isteği içerisinde yer alan JSON dokümanındaki data elemanının içerisinde, kaydın güncellenecek herhangi bir özelliği veya bütün özellikleri bulunabilir. Eğer istek, kaydın tüm özelliklerini içermiyorsa sunucu, ilgili isteği bu özelliklerin varolan değerlerini içeriyormuş gibi ele almak zorundadır. Sunucu, istekte olmayan özelliklerin değerlerini asla null
olarak yorumlamamak zorundadır.
Örneğin aşağıdaki PATCH isteği, sunucu tarafından makalenin sadece title ve text alanlarını güncellenecek şekilde yorumlanır.
PATCH /articles/1 HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data":{ "type":"articles", "id":"1", "attributes":{ "title":"JavaScript vs. WebAssembly", "text":"..." } } }
Kaydın İlişkilerinin Güncellenmesi
PATCH isteğindeki kaynak nesnede, kaynağın ilişkilerinden bir kısmı veya tamamı yer alabilir.
Eğer istek içerisinde kaynak ile ilgili ilişkiler eksikse, sunucu eksik ilişkilerin varolan değerlerini korumak zorundadır (boş veya null
olarak yorumlamamalıdır).
Bir PATCH isteği içerisindeki kaynak nesnenin, relationships
elemanında bir ilişki belirtilmişse, ilgili değerin, data
elemanı ile ilişkisinin bulunması zorunludur. Çünkü var olan ilişkinin değeri, relationships
elemanında belirtilen değer ile değiştirilecektir.
Örnek olarak aşağıdaki PATCH isteği, ilgili makalenin author
ilişkisini güncellemektedir:
PATCH /articles/1 HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data":{ "type":"articles", "id":"1", "relationships":{ "author":{ "data":{ "type":"people", "id":"1" } } } } }
Aynı şekilde aşağıdaki PATCH isteği, makalenin etiketlerinin hepsini, ilgili istekte yer alan etiketler ile değiştirecektir:
PATCH /articles/1 HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data":{ "type":"articles", "id":"1", "relationships":{ "tags":{ "data":[ { "type":"tags", "id":"2" }, { "type":"tags", "id":"3" } ] } } } }
Not: Çoklu silme işlemleri tehlikeli bir işlem olabileceğinden dolayı sunucu, 1-N ilişkilerin tamamını tek seferde güncellenmesini reddedebilir. Bu durumda sunucu, güncellemenin tamamını reddeder ve 403 Forbidden
cevabını döndürür.
Cevaplar
202 Accepted
Eğer bir güncelleme isteği sunucu tarafından işlenmek üzere alınmışsa ve sunucu cevap verdiği anda hala isteğin işlenme süreci devam ediyorsa, sunucu 202 Accepted durum kodunu dönmek zorundadır.
200 OK
Eğer sunucu, güncelleme isteğini kabul etmişse ve güncellenecek kaydın istekte yer almayan kısımlarını (örneğin, olusturma_tarihi
değişkeni veya sunucu tarafında hesaplanmış SHA değeri) değiştiriyorsa, 200 OK
durum kodunu dönmek zorundadır. Dönen cevap içerisinde, aynı GET isteğinde olduğu gibi güncellenen kaydın ilgili özelliklerinin bulunması zorunludur.
Eğer bir güncelleme başarılıysa, istemcinin mevcut verisi güncel kalıyorsa ve sunucu data
olmaksızın sadece meta
kök elemanını döndürüyorsa da 200 OK
durum kodunu dönmelidir. Bu durumda sunucu, güncellenen ilişkilerin bilgisini dönmemelidir.
403 Forbidden
Kaydın oluşturulması için desteklenmeyen bir istek gönderilmişse sunucu 403 Forbidden
durum kodunu döndürebilir.
Diğer Sunucu Cevapları
Sunucu diğer HTTP durum kodlarını kullanarak cevap dönebilir ve oluşan hatalı cevaplar, ilgili hata detaylarını içerebilir. Sunucu, HTTP Semantiğine göre cevapları oluşturmak zorunda ve istemci de bu bağlamda cevapları yorumlamak zorundadır.
Kaynakların Silinmesi
Bir kaynak, ilişkili olduğu URL’e DELETE isteği gönderilerek silinebilir:
DELETE /photos/1 HTTP/1.1 Accept: application/vnd.api+json
Sunucu Cevapları
202 Accepted
Eğer bir silme isteği sunucu tarafından işlenmek üzere alınmışsa ve sunucu cevap verdiği anda hala isteğin işlenme süreci devam ediyorsa, sunucu 202 Accepted
durum kodunu dönmek zorundadır.
204 No Content
Silme isteği başarılıysa ve hiçbir içerik döndürülmeyecekse sunucu 204 No Content
durum kodunu döndürür.
200 OK
Silme isteği başarılıysa ve cevap olarak sadece meta
alanı döndürülecekse, sunucu 200 OK
durum kodunu döndürür.
404 Not Found
Eğer ilgili kaynak bulunmadığından dolayı silinme işlemi gerçekleşmediyse sunucu 404 Not Found
durum kodunu döndürür.
Diğer Sunucu Cevapları
Sunucu diğer HTTP durum kodlarını kullanarak cevap dönebilir ve oluşan hatalı cevaplar, ilgili hata detaylarını içerebilir. Sunucu, HTTP Semantiğine göre cevapları oluşturmak zorunda ve istemci de bu bağlamda cevapları yorumlamak zorundadır.
URL Sorgu Parametreleri
Uygulamaya ait spesifik olarak oluşturulan URL sorgu parametreleri, oluşturulan eleman isimleri ile aynı isimlendirme kurallarına bağlı kalmak zorundadırlar. Buna ek olarak, en az bir adet a-z (U+0061
– U+007A
) olmayan karakter içermek zorundadırlar. Eksi karakteri “-” (U+002D
), alt çizgi karakteri “_” (U+005F
) veya cameCase olarak yazımda büyük karakterler kullanılabilir.
Eğer sunucu yukarıdaki isimlendirme kurallarına uymayan bir sorgu parametresi ile karşılaşırsa ve sunucu bu kurallara bakarak sorguyu hangi şekilde işleyeceğini bilmiyorsa, istemciye 400 Bad Request
cevabını dönmek zorundadır.
Hatalar
Hataların İşlenilmesi
Bir sunucu, herhangi bir sorun oluştuğunda isteğin çalışmasını durdurabilir veya işleyişe devam ederek birden fazla sorun ile karşılaşarak cevap dönebilir. Örneğin sunucu, nesnenin birden fazla elemanını işleyebilir ve tek cevapta çoklu validasyon problemlerini döndürebilir.
Sunucu tek istekte birden fazla sorun ile karşılaştığında, en genel HTTP hata kodunu döndürmelidir. Örneğin 400 Bad Request
cevabı çoklu 4xx hatalarında kullanılabilirken, 500 Internal Server Error
cevabı çoklu 5xx hatalarında kullanılabilir.
Hata Nesneleri
Oluşturulan hata nesnesi, bir işlem gerçekleştirilirken karşılaşılan sorun ile ilgili ek bilgi sunulmasını sağlar. Hata nesneleri, oluşan JSON API dokümanının kök seviyesinde errors
adı altındaki bir array içerisinde yer almak zorundadırlar.
Bir hata nesnesi aşağıdaki elemanları içerebilir:
id
: oluşan hata için benzersiz bir numara veya string verisidir.links
: aşağıdaki elemanları içeren links nesnesidir:about
: oluşan bu problem ile ilgili detaylı bilgilerin bulunduğu sayfanın linkini sunar.
status
: bu probleme uyan HTTP durum kodudur, string değer alır.code
: Uygulama-tanımlı hata kodudur, string değer alır.title
: Kısa, aynı hata oluştukça (çoklu dil desteği haricinde) değişmemesi gereken, problem hakkında okunaklı özet bilgi sunan bir alandır.detail
: Sorun hakkında detaylı bilgi sunar. title alanı gibi çoklu dil desteği için özelleştirilebilir.source
: Hatanın kaynağı hakkında ilgili referansları ve isteğe bağlı olarak aşağıdaki alanları içerebilir.pointer
: istek JSON’ında belirtilen ilişkili elemanın URL parçasını bulundurur. Örnek olarak, ana data nesnesi için “/data” pointer’ı veya nesnenin spesifik bir elemanı için “/data/attributes/title” pointer’ı verilebilir.parameter
: hataya neden olan URL sorgu parametresidir.meta
: hata hakkında standart olmayan üst-bilgi içeren bir meta nesnesidir.
Örneğin aşağıdaki cevapta, istenilen kaydın eklenmesi/güncellenmesi işleminde geçersiz first-name
özelliğinden dolayı hata oluşmuştur.
HTTP/1.1 422 Unprocessable Entity Content-Type: application/vnd.api+json { "errors":[ { "status":"422", "source":{ "pointer":"/data/attributes/first-name" }, "title":"Invalid Attribute", "detail":"First name must contain at least three characters." } ] }
Daha fazla bilgi için http://jsonapi.org/format/ sitesini ziyaret edebilirsiniz. Bir sonraki yazıda görüşmek üzere.
12 Comments
Murat kakun
27 Nisan 2017 at 11:01Merhabalar zafer kardesim.Gercekten cok guzel faydali ve aciklayici bir yazi olmus.Konu en ince ayrintisina kadar anlatilmis.Yazilarin devamini bekliyoruz.Tesekkurler.
Zafer Ayan
28 Nisan 2017 at 12:58Teşekkür ederim Murat
Hüseyin Altunbaş
27 Nisan 2017 at 12:29.net mvc tarafında nasıl bir yol izlenmeli bu standartlar için.
Örnek bir proje varmı .net için.
Zafer Ayan
28 Nisan 2017 at 13:01Hüseyin merhaba. .NET için ilgili kütüphaneler ve dokümanlar mevcut. Buradaki kütüphanelerden birini kullanabilirsin http://jsonapi.org/implementations/#server-libraries-net
MURAT
28 Nisan 2017 at 21:12bu kadar gereksiz detaya hiç gerek yok.
durup dururken işi zorlaştırma anlam bulamıyorum.
200,401,403,500 kullan yeter
ayrıca saf veri gönder, yok datanın içinde attirube yok error alanı falan filen gereksiz yere iş gücü bunlar.
500,400 göndereceksen error gönder yeter.
200 gönderipte error göndereni hiç anlamıyorum.
Zafer Ayan
15 Mayıs 2017 at 00:15Murat merhaba,
Evet { “Status”: 200, “Description”: “Kayıtlar başarıyla getirildi.” } mantığı da iyi bir yöntem. Küçük projelerde ve ekiplerde sorunsuz çalışacaktır. Fakat büyük bir ekip içerisinde, ekibin diğer elemanlarının kendi insiyatifleri doğrultusunda non-standard bir şekilde API’nin farklı bölümlerini yazabilirler (Örneğin biri 200 yazar diğeri string olarak”200″ yazar). Bu gibi sorunların giderilmesi için yazılacak API’ın da standartlaştırılması gerekiyor. Bu yüzden JSON API standardı var.
Hakan
29 Aralık 2017 at 08:28Aynen hocam çok doğru söylemişsin. Verdiğin örneklerle karşılaştım
Zafer Ayan
15 Ocak 2018 at 23:54Rica ederim Hakan.
Değerli yorumun için teşekkürler,
agit
2 Mayıs 2017 at 08:43merhaba, güzel bir yazı olmuş teşekkürler,
sanırım bundan daha iyisi swagger ve oData’nın birlikte kullanıldığı bir standart olacaktır diye düşünüyorum
Zafer Ayan
16 Mayıs 2017 at 00:07Merhaba agit,
Dediğin gibi bir yazı da yazabiliriz tabi ki neden olmasın.
Önerin için teşekkürler
Murat
17 Nisan 2018 at 13:50data içerisinde status kodunu koymak !!!!! http status kodları varken. !!!! standard !!
Zafer Ayan
6 Mayıs 2018 at 16:49Merhaba Murat,
HTTP içerisinde bir istek için sadece bir adet status kodu üretiliyor. Birden fazla hata mesajı dönmek istendiğinde sadece 1 adet status kodu yetersiz olacaktır. Daha fazla bilgi için JSONAPI’nin resmi sitesinden (http://jsonapi.org/examples/#error-objects-basics) bilgi alabilirsin.
Teşekkürler.