İç içe geçmiş REST kaynakları için en iyi uygulamalar nelerdir?


301

Her bir kaynağın sadece bir kanonik yolu olması gerektiğini söyleyebildiğim kadarıyla . Aşağıdaki örnekte iyi URL kalıpları ne olurdu?

Şirketlerin geri kalanı için örnek gösterelim. Bu varsayımsal örnekte her şirket sahibi 0 veya daha fazla bölüm ve her bölüm sahibi , 0 veya daha fazla çalışanı.

Bir departman , ilişkili bir şirket olmadan var olamaz.

Bir çalışan , ilişkili bir departman olmadan var olamaz.

Şimdi kaynak kalıplarının doğal temsilini bulacağım.

  • /companies Şirketler koleksiyonu - Yeni bir şirket için kabul eder. Tüm koleksiyon için alın.
  • /companies/{companyId}Bireysel bir şirket. GET, PUT ve DELETE kabul eder
  • /companies/{companyId}/departmentsYeni bir öğe için POST'u kabul eder. (Şirket içinde bir departman oluşturur.)
  • /companies/{companyId}/departments/{departmentId}/
  • /companies/{companyId}/departments/{departmentId}/employees
  • /companies/{companyId}/departments/{departmentId}/employees/{empId}

Kısıtlamalar göz önüne alındığında, bölümlerin her birinde, biraz derin yuvalanmışsa bunun mantıklı olduğunu hissediyorum.

Ancak, GETtüm şirketlerdeki tüm çalışanları listelemek ( ) istiyorsam zorluğum geliyor .

Bunun için kaynak modeli /employees(Tüm çalışanların toplanması)

Bu /employees/{empId}da olması gerektiği anlamına mı geliyor, eğer öyleyse aynı kaynağı almak için iki URI var mı?

Ya da belki tüm şema düzleştirilmelidir, ancak bu çalışanların iç içe üst düzey bir nesne olduğu anlamına gelir.

Temel düzeyde /employees/?company={companyId}&department={deptId}, çalışanların en derinden iç içe geçmiş kalıp ile aynı görüşlerini verir.

Kaynakların başka kaynaklara ait olduğu ancak ayrı olarak sorgulanabilmesi gereken URL kalıpları için en iyi uygulama hangisidir?


1
Bu, yanıtlarla ilgili olabileceği halde stackoverflow.com/questions/7104578/… ' de açıklanan sorunun neredeyse tam tersidir . Her iki soru da sahiplikle ilgilidir, ancak bu örnek, üst düzey nesnenin sahip olduğu nesne olmadığını gösterir.
Wes

1
Tam olarak merak ettiğim şey. Verilen kullanım durumu için çözümünüz iyi görünüyor, ancak ilişki bir kompozisyondan ziyade bir toplama ise? Yine de burada en iyi uygulamanın ne olduğunu bulmakta zorlanıyoruz ... Ayrıca, bu çözüm sadece ilişkinin yaratılmasını mı, örneğin mevcut bir kişinin istihdam edildiğini mi yoksa bir kişi nesnesi mi oluşturduğunu?
Jakob O.

Hayali örneğimde bir insan yaratıyor. Bu alan adı terimlerini kullanmamın nedeni, gerçek sorunumu taklit etmesine rağmen makul derecede anlaşılabilir bir örnektir. Bir şiddet ilişkisi için sizi daha fazla durdurabilecek bağlantılı soruyu incelediniz mi?
Wes

Sorumu bir cevaba ve soruya ayırdım.
Wes

Yanıtlar:


152

Yaptığınız doğru. Genel olarak aynı kaynağa birçok URI olabilir - bunu yapmamanız gerektiğini söyleyen hiçbir kural yoktur.

Ve genellikle, öğelere doğrudan veya başka bir şeyin alt kümesi olarak erişmeniz gerekebilir - böylece yapınız bana mantıklı geliyor.

Çalışanların departman altında erişilebilir olması:

company/{companyid}/department/{departmentid}/employees

Şirket altında da erişilemeyecekleri anlamına gelmez:

company/{companyid}/employees

Bu da o şirketin çalışanlarını iade ederdi. Tüketici müşterinizin neye ihtiyaç duyduğuna bağlıdır - bunun için tasarlamanız gerekir.

Ancak, tüm URL işleyicilerinin, kodları çoğaltmamanız için istekleri karşılamak için aynı destek kodunu kullanmasını umuyorum.


11
Bu, RESTful'in ruhuna işaret ediyor, önce sadece anlamlı bir kaynak düşünürseniz yapmanız ya da yapmamanız gerektiğini söyleyen hiçbir kural yok . Ama dahası, bu tür senaryolarda kodu çoğaltmamak için en iyi uygulamanın ne olduğunu merak ediyorum .
abookyun

13
Her iki rotaya da ihtiyacınız varsa @abookyun, aralarında tekrarlanan denetleyici kodu servis nesnelerine soyutlanabilir.
bgcode

Bunun REST ile bir ilgisi yok. REST, URL'lerinizin yol kısmını nasıl yapılandırdığınızla ilgilenmez ... umursayacağı tek şey geçerli, umarım dayanıklı URI'ler ...
redben

Bu cevaba göre, dinamik segmentlerin benzersiz tanımlayıcıların bulunduğu herhangi bir api'nin birden fazla dinamik segmenti ( ) işlemesi gerekmemesi gerektiğini düşünüyorum . API her kaynağı almanın yollarını sağlıyorsa, bu isteklerin her birini yapmak bir istemci tarafı kitaplığında ya da kodu yeniden kullanan bir defalık uç nokta olarak yapılabilir. /company/3/department/2/employees/1
max

1
Bir yasak olmasa da, bir kaynağa giden tek bir yolun daha zarif olduğunu düşünüyorum - tüm zihinsel modelleri daha basit tutar. Ayrıca, yuvalama varsa URI'lerin kaynak türlerini değiştirmemelerini tercih ederim. örneğin /company/*, yalnızca şirket kaynağını döndürmeli ve kaynak türünü değiştirmemelidir. Bunların hiçbiri REST tarafından belirtilmemiştir - genellikle kötü bir şekilde belirtilmiştir - sadece kişisel tercihtir.
kashif

174

Her iki tasarım stratejisini de denedim - iç içe ve iç içe olmayan uç noktalar. Şunu buldum:

  1. iç içe kaynağın birincil anahtarı varsa ve üst birincil anahtarına sahip değilseniz, iç içe geçmiş yapı, sistem gerçekten gerektirmese de bu anahtarı almanızı gerektirir.

  2. yuvalanmış uç noktalar genellikle yedek uç noktalar gerektirir. Başka bir deyişle, departmanlardaki çalışanların bir listesini alabilmek için daha sık olmamakla birlikte ek / çalışanların uç noktasına ihtiyacınız olacaktır. / Çalışanlarınız varsa, / şirketler / departmanlar / çalışanlar sizi tam olarak ne satın alır?

  3. yuvalama uç noktaları o kadar iyi gelişmez. Örneğin, şimdi çalışanları aramanıza gerek olmayabilir, ancak daha sonra da iç içe geçmiş bir yapınız varsa, başka bir bitiş noktası eklemek dışında bir seçeneğiniz yoktur. Yuvalanmamış bir tasarımla, daha basit olan daha fazla parametre eklersiniz.

  4. bazen bir kaynağın birden çok ebeveyn türü olabilir. Sonuçta birden fazla uç nokta aynı kaynak döndürür.

  5. gereksiz uç noktalar dokümanların yazılmasını zorlaştırır ve aynı zamanda api'nin öğrenilmesini zorlaştırır.

Kısacası, iç içe olmayan tasarım daha esnek ve daha basit bir uç nokta şemasına izin veriyor gibi görünüyor.


24
Bu cevaba rastlamak çok ferahlatıcı oldu. "Doğru yol" olarak öğretildikten sonra birkaç aydır iç içe uç noktaları kullanıyorum. Yukarıda listelediğiniz sonuçların hepsine geldim. İç içe olmayan bir tasarımla çok daha kolay.
user3344977

6
Bazı dezavantajların bazılarını yukarı yönlü olarak listeliyorsunuz. "Sadece daha fazla parametreyi tek bir uç noktaya sıkıştırın" API'yi belgelemeyi ve öğrenmeyi zorlaştırır, tersi değil. ;-)
Drenmi

4
Bu cevabın hayranı değil. Yalnızca iç içe bir kaynak eklediğiniz için gereksiz uç noktalar eklemenize gerek yoktur. Aynı kaynağın iç içe kaynağa gerçekten sahip olması koşuluyla, aynı kaynağın birden fazla ebeveyn tarafından döndürülmesi de sorun değildir. Yuvalanmış kaynaklarla nasıl etkileşime girileceğini öğrenmek için bir üst kaynağa sahip olmak sorun değildir. İyi bir keşfedilebilir REST API'si bunu yapmalıdır.
Scottm

3
@Scottm - Karşılaştığım iç içe kaynakların bir dezavantajı, üst kaynak kimlikleri yanlış / yanlış eşleşmesi durumunda yanlış verilerin döndürülmesine neden olabilmesidir. Yetkilendirme sorunu olmadığı varsayılarak, iç içe kaynağın gerçekten geçirilen üst kaynağın bir alt öğesi olduğunu doğrulamak api uygulamasına bırakılmıştır. Bu kontrol kodlanmazsa, api yanıtı yanlış olabilir ve yolsuzluğa yol açabilir. Düşüncelerin nelerdir?
Andy Dufresne

1
Son kaynakların hepsinde benzersiz kimlikler varsa ara üst kimliklere ihtiyacınız yoktur. Örneğin, çalışanı kimliğine göre almak için GET / şirketler / departmanlar / çalışanlar / {empId} ya da şirketteki tüm çalışanlara sahip olmak için 123 GET / şirketler / 123 / departmanlar / çalışanlar / Yolu hiyerarşik tutmak nasıl daha belirgin filtrelemek / oluşturmak / değiştirmek için ara kaynaklara ulaşabilirsiniz ve bence keşfedilebilirliğe yardımcı olur.
PaulG

77

Sorudan yaptığım şeyi, daha fazla insanın göreceği bir cevaba taşıdım.

Yaptığım şey iç içe noktada oluşturma uç noktalarına sahip olmaktır, Bir öğeyi değiştirmek veya sorgulamak için standart uç nokta iç içe kaynakta değildir .

Bu örnekte (sadece kaynağı değiştiren uç noktaları listelemek)

  • POST /companies/ yeni bir şirket oluşturur, oluşturulan şirkete bir bağlantı döndürür.
  • POST /companies/{companyId}/departments bir departman konduğunda yeni departman /departments/{departmentId}
  • PUT /departments/{departmentId} departmanı değiştirir
  • POST /departments/{deparmentId}/employees yeni bir çalışan oluşturur /employees/{employeeId}

Her koleksiyon için kök düzeyinde kaynaklar var. Ancak oluşturma , sahip olunan nesnenin içindedir .


4
Ben de aynı tip bir tasarım buldum. Bence bu tür "ait oldukları yer" gibi şeyler oluşturmak sezgiseldir, ancak yine de bunları küresel olarak listeleyebilmektedir. Daha da ötesi bir kaynağın bir ebeveyni olması GEREKİR. Daha sonra bu kaynağı global olarak oluşturmak bunu belirginleştirmez, ancak bunu böyle bir alt kaynakta yapmak mükemmel mantıklıdır.
Joakim

Sanırım POSTanlam kullandın PUT, aksi halde.
Gerardo Lima

Aslında hayır Not, bu durumda sunucu (bağlantıda) kimliğini döndürmek için sorumlu olarak oluşturmak için önceden atanmış Kimlikleri kullanmıyorum. Bu nedenle POST yazmak doğrudur (aynı uygulamaya geçilemez). Ancak koymak tüm kaynağı değiştirir, ancak hala aynı yerde kullanılabilir, bu yüzden ben koymak. PUT ve POST farklı bir konudur ve tartışmalıdır. Örneğin stackoverflow.com/questions/630453/put-vs-post-in-rest
Wes

@Wes Ben bile fiil yöntemlerini ebeveyn altında olmak için değiştirmeyi tercih ederim. Ancak, global kaynak için geçen sorgu parametresinin iyi kabul edildiğini görüyor musunuz? Örn: şirket = şirket kimliği ile POST / departmanlar
Ayyappa

1
@Mohamad Hem anlamanın hem de kısıtlamaların uygulanmasının diğer yolunun daha kolay olduğunu düşünüyorsanız, cevap vermekten çekinmeyin. Bu durumda eşlemeyi açık hale getirmekle ilgilidir. Bir parametre ile çalışabilir ama sorunun ne olduğunu gerçekten thats. En iyi yol nedir.
Wes

35

Yukarıdaki cevapların hepsini okudum ama ortak bir stratejileri yok gibi görünüyor. Microsoft Documents'ın Tasarım API'sındaki en iyi uygulamalar hakkında iyi bir makale buldum . Bence başvurmalısın.

Daha karmaşık sistemlerde, bir istemcinin çeşitli ilişkiler düzeylerinde gezinmesini sağlayan URI'ler sağlamak cazip gelebilir, /customers/1/orders/99/products.ancak, bu karmaşıklık düzeyini korumak zor olabilir ve gelecekte kaynaklar arasındaki ilişkiler değişiyorsa esnek değildir. Bunun yerine URI'leri nispeten basit tutmaya çalışın . Bir uygulamanın bir kaynağa başvurusu olduğunda, bu kaynakla ilgili öğeleri bulmak için bu başvuruyu kullanmak mümkün olmalıdır. Önceki sorgu, /customers/1/ordersmüşteri 1 için tüm siparişleri /orders/99/productsbulmak ve daha sonra bu siparişteki ürünleri bulmak için URI ile değiştirilebilir .

.

İpucu

Kaynak URI'lerden daha karmaşık olmasını istemeyin collection/item/collection.


3
Verdiğiniz referans, karmaşık URI'lar yapmamanın dışında durduğunuz nokta ile birlikte şaşırtıcı.
vicco

Bir kullanıcı için bir ekip oluşturmak istediğimde, POST / ekipler (gövdede userId) veya POST / kullanıcılar /: id / teams
coinhndp 16:09

@coinhndp Merhaba, POST / ekipleri kullanmalısınız ve erişim belirtecini yetkilendirdikten sonra userId alabilirsiniz. Yani yetki koduna ihtiyacınız olan bir şey oluşturduğunuzda, değil mi? Hangi çerçeveyi kullandığınızı bilmiyorum ama API denetleyicisinde userId alabileceğinizden eminim. Örneğin: ASP.NET API'sinde, ApiController üzerindeki bir yöntemden RequestContext.Principal öğesini çağırın. Spring Secirity'de SecurityContextHolder.getContext (). GetAuthentication (). GetPrincipal () size yardımcı olacaktır. AWS NodeJS Lambda'da bu, cognito: headers nesnesindeki kullanıcı adıdır.
Long Nguyen

POST / users /: id / teams ile ilgili sorun nedir. Sanırım yukarıda gönderdiğiniz Microsoft Belgesinde tavsiye edilir
coinhndp

@coinhndp Yönetici olarak takım oluşturursanız, bu iyidir. Ancak, normal kullanıcılar olarak, neden userId yoluna ihtiyacınız olduğunu bilmiyorum? Ben kullanıcı_A ve kullanıcı_B var sanırım, eğer kullanıcı_A POST / kullanıcılar / kullanıcı_B / ekipleri çağırırsanız kullanıcı_A kullanıcı_B için yeni bir ekip oluşturabilir ne düşünüyorsun. Bu nedenle, userId'yi bu durumda geçirmeye gerek yoktur, userId yetkilendirmeden sonra alabilir. Ancak, ekipler:: id / projeler, örneğin ekip ve proje arasında ilişki kurmak için iyidir.
Long Nguyen

10

URL'lerinizin görünümünün REST ile hiçbir ilgisi yoktur. Her şey olur. Aslında bir "uygulama detayı" dır. Yani değişkenlerinizi nasıl adlandırdığınız gibi. Olmaları gereken tek şey eşsiz ve dayanıklıdır.

Bu konuda çok fazla zaman kaybetmeyin, sadece bir seçim yapın ve ona bağlı kalın / tutarlı olun. Örneğin, hiyerarşilerle giderseniz, bunu tüm kaynaklarınız için yaparsınız. Sorgu parametreleri ile giderseniz ... vb kodunuzdaki adlandırma kuralları gibi.

Neden öyle ? Bildiğim kadarıyla "RESTful" API göz atılabilir olmalı (biliyorsunuz ... "Uygulama Durumunun Motoru Olarak Hipermedya"), bu nedenle bir API istemcisi URL'lerinizin neye benzediklerini umursamıyor geçerli (SEO yok, bu "dost URL'leri" okuması gereken insan yok, hata ayıklama hariç ...)

Bir URL'nin bir REST API'sinde ne kadar hoş / anlaşılır olması, kodunuzdaki bir değişkenin adı gibi API istemcisi değil, yalnızca API geliştiricisi olarak sizi ilgilendirir.

En önemli şey, API istemcinizin medya türünüzü nasıl yorumlayacağını bilmesidir. Örneğin şunu bilir:

  • medya türünüzde kullanılabilir / ilgili bağlantıları listeleyen bir bağlantılar özelliği bulunur.
  • Her bağlantı bir ilişki ile tanımlanır (tıpkı tarayıcıların [rel = "stylesheet"] bağlantısının bir stil sayfası veya rel = favico'nun bir favicon bağlantısı olduğunu bildiği gibi ...)
  • ve bu ilişkilerin ne anlama geldiğini bilir ("şirketler" şirketlerin bir listesi, "arama", kaynak listesinde arama yapmak için şablonlanmış bir url anlamına gelir, "departmanlar", mevcut kaynağın departmanları anlamına gelir)

Aşağıda örnek bir HTTP değişimi bulunmaktadır (yazılması daha kolay olduğu için gövdeler yaml dosyasındadır):

İstek

GET / HTTP/1.1
Host: api.acme.io
Accept: text/yaml, text/acme-mediatype+yaml

Yanıt: ana kaynağa olan bağlantıların bir listesi (şirketler, insanlar, her neyse ...)

HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:04:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml

# body: this is your API's entrypoint (like a homepage)  
links:
  # could be some random path https://api.acme.local/modskmklmkdsml
  # the only thing the API client cares about is the key (or rel) "companies"
  companies: https://api.acme.local/companies
  people: https://api.acme.local/people

Talep: şirketlere bağlantı (önceki yanıtın body.links.şirketlerini kullanarak)

GET /companies HTTP/1.1
Host: api.acme.local
Accept: text/yaml, text/acme-mediatype+yaml

Yanıt: şirketlerin kısmi bir listesi (öğeler altında), kaynak sonraki birkaç şirketi (body.links.next) aramak için başka bir (templated) bağlantı (body.links.search) almak için bağlantı gibi ilgili bağlantılar içerir.

HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:06:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml

# body: representation of a list of companies
links:
  # link to the next page
  next: https://api.acme.local/companies?page=2
  # templated link for search
  search: https://api.acme.local/companies?query={query} 
# you could provide available actions related to this resource
actions:
  add:
    href: https://api.acme.local/companies
    method: POST
items:
  - name: company1
    links:
      self: https://api.acme.local/companies/8er13eo
      # and here is the link to departments
      # again the client only cares about the key department
      department: https://api.acme.local/companies/8er13eo/departments
  - name: company2
    links:
      self: https://api.acme.local/companies/9r13d4l
      # or could be in some other location ! 
      department: https://api2.acme.local/departments?company=8er13eo

Gördüğünüz gibi, URL'lerinizin yol kısmını nasıl yapılandıracağınız bağlantılar / ilişkiler yoluna giderseniz API istemcinize herhangi bir değeri yoktur. URL'lerinizin yapısını müşterinize dokümantasyon olarak iletiyorsanız, REST (veya " Richardson'ın olgunluk modeli " uyarınca en azından Seviye 3) yapmıyorsunuz )


7
"Bir URL'nin bir REST API'sinde ne kadar hoş / anlaşılır olması, kodunuzdaki bir değişkenin adı gibi API istemcisi değil, yalnızca API geliştiricisi olarak sizin için ilginçtir." Bu neden ilginç DEĞİL? Kendinizden başka biri de API kullanıyorsa, bu çok önemlidir. Bu kullanıcı deneyiminin bir parçasıdır, bu yüzden bunun API istemci geliştiricileri için anlaşılması kolay olduğunu söyleyebilirim. Kaynakları net bir şekilde bağlayarak işleri daha da kolay hale getirmek elbette bir bonus (sağladığınız url'de seviye 3). Her şey açık ilişkilerle sezgisel ve mantıklı olmalıdır.
Joakim

1
@Joakim Düzey 3 dinlenme API'sı (Uygulama Durumunun Altyapısı Olarak Köprü Metni) yapıyorsanız, URL'nin yol yapısı (geçerli olduğu sürece) istemciye kesinlikle ilgi göstermez. Seviye 3'ü hedeflemiyorsanız, evet, bu önemlidir ve tahmin edilebilir olmalıdır. Ama gerçek REST seviye 3'tür. İyi bir makale: martinfowler.com/articles/richardsonMaturityModel.html
redben

4
Şimdiye kadar insanlar için kullanıcı dostu olmayan bir API veya kullanıcı arayüzü oluşturmaya itiraz ediyorum. Seviye 3 ya da değil, kaynakları birbirine bağlamak harika bir fikir. Ancak bunu önermek, "URL şemasını değiştirmeyi mümkün kılar" gerçeği ve insanların API'ları nasıl kullandıklarıyla bağlantısız olmaktır. Bu kötü bir öneri. Ancak tüm dünyaların en iyisinde herkes Seviye 3 REST'te olacaktır. Köprüler kullanıyorum ve insan tarafından anlaşılabilir bir URL şeması kullanıyorum. Seviye 3 öncekini dışlamaz ve bence biri önemsemelidir. İyi makale olsa :)
Joakim

Tabii ki sürdürülebilirlik ve diğer kaygılar uğruna ilgilenmeliyim, sanırım cevabımın noktasını kaçırdınız: url'nin görünüşü çok fazla düşünmeyi hak etmiyor ve "sadece bir seçim yapıp ona bağlı kalmalısınız / tutarlı "dedi. Ve bir REST API durumunda, en azından benim görüşüme göre, kullanıcı dostu url değil, çoğunlukla (medya türü) Neyse umarım
anladım

9

Bu tür bir yola katılmıyorum

GET /companies/{companyId}/departments

Departman almak istiyorsanız, a / departmanlar kaynağını kullanmanın daha iyi olduğunu düşünüyorum

GET /departments?companyId=123

Bir companiestablonuz ve bir departmentstablonuz var, sonra kullandığınız programlama dilinde onları eşleştirmek için sınıflarınız var. Ayrıca, departmanların şirketlerden başka kuruluşlara da bağlanabileceğini varsayıyorum, bu nedenle a / departmanların kaynağı basittir, kaynakların tablolarla eşleştirilmesi uygundur ve ayrıca yeniden kullanabileceğiniz için çok fazla uç noktaya ihtiyacınız yoktur

GET /departments?companyId=123

örneğin her türlü arama için

GET /departments?name=xxx
GET /departments?companyId=123&name=xxx
etc.

Bir departman oluşturmak istiyorsanız,

POST /departments

kaynak kullanılmalı ve talep kuruluşu şirket kimliğini içermelidir (departman sadece bir şirkete bağlanabilirse).


1
Bana göre, bu ancak iç içe nesne bir atomik nesne olarak anlamlıysa kabul edilebilir bir yaklaşımdır. Eğer değillerse, onları parçalamak gerçekten mantıklı olmaz.
Simme

Ben de bunu söyledim, eğer bölümleri de almak istiyorsanız, yani a / bölümlerin uç noktasını kullanırsanız.
Maxime Laval

2
Ayrıca, bir şirket getirilirken departmanların tembel yükleme yoluyla dahil edilmesine izin vermek de mantıklı olabilir, örneğin GET /companies/{companyId}?include=departmentsbu, hem şirketin hem de departmanlarının tek bir HTTP isteğinde alınmasına izin verir. Fractal bunu gerçekten iyi yapıyor.
Matthew Daly

1
Acls oluştururken, muhtemelen /departmentsuç noktayı yalnızca bir yönetici tarafından erişilebilir olacak şekilde kısıtlamak ve her şirketin kendi departmanlarına yalnızca `/ companies / {companyId} / departmanlar '
13'te Cuzox

@MatthewDaly OData da $ expand ile güzel yapıyor
Rob Grant

1

Raylar buna bir çözüm sağlar: sığ yuvalama .

Bunun iyi bir şey olduğunu düşünüyorum, çünkü bilinen bir kaynakla doğrudan uğraşırken, diğer yanıtlarda tartışıldığı gibi iç içe yollar kullanmaya gerek yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.