REST API 404: Bozuk URI mı, yoksa Eksik Kaynak mı?


219

Bir REST API oluşturuyorum, ancak bir sorunla karşılaştım.

Bir REST API'sini tasarlamada kabul edilen uygulama, istenen kaynak mevcut değilse, 404'ün döndürülmesidir.

Ancak, bu bana gereksiz bir belirsizlik katıyor. HTTP 404 daha geleneksel olarak kötü bir URI ile ilişkilidir. Yani aslında diyoruz ki "Ya doğru yere geldiniz, ama bu özel kayıt mevcut değil ya da İnternette böyle bir yer yok! Gerçekten hangisinin olduğundan emin değilim ..."

Aşağıdaki URI'yı düşünün:

http://mywebsite/api/user/13

Eğer bir 404 geri alırsam, bu Kullanıcı 13 olmadığı için mi? Yoksa URL'm olması gerektiği için:

http://mywebsite/restapi/user/13

Geçmişte, HTTP 200 OKkayıt yoksa bir yanıt kodu ile NULL sonuç döndürdüm . Basit ve bence çok temiz, zorunlu olarak kabul edilmiş bir uygulama olmasa bile. Ama bunu yapmanın daha iyi bir yolu var mı?



7
Diğer soru URI Sorgu dizesi formatları ile ilgili gibi görünüyor. 404 hakkındaki tartışma sorumu cevaplamak için yeterli değil, bu da 404'ün gerçekte ne anlama geldiğini belirlemenin daha uygun ya da yararlı bir yolu olup olmadığıdır. Göndermeden önce bunu inceledim.
Brian Lacy

Tarayıcı concole 404 hatalarını içerdiğinde normal mi? Ne zaman doğru uri ama kaynak bulunamadı ile düzenli işlemleri yapmak.
Vasiliy Mazhekin

3
404 hatalı bir URI'yi, Bulunamadığı bir kaynağı gösterir. Bunun nedeni, '13' kullanıcısı olmaması veya kaynak / sitem / api olmaması olabilir.
ChrisV

Yanıtlar:


101

404 sadece HTTP yanıt kodudur. Bunun üzerine, bir yanıt gövdesi ve / veya diğer üstbilgilere geliştiricilerin göreceği daha anlamlı bir hata mesajı verebilirsiniz.


7
Bu mantıklı. Yine de, 404'ü geri döndürmekten herhangi bir fayda elde edip etmediğini merak etmeliyim, buna karşılık 200 yanıt vermedi mi?
Brian Lacy

15
200 ile null değerini döndürürseniz, HTTP Spec'e karşı gidersiniz. Bunu yapabilirsiniz, ancak api'niz REST'in "Uniformed Interface" Kısıtlamasına uymayacaktır.
dava açmak

5
... ve yanıt gövdeniz geçerli URI'lere köprüler içermelidir. Kök URI'yi ve belki de bir veya iki yer işaretini yasaklayan istemcileriniz her zaman sunucu tarafından kendilerine verilen bağlantıları izlemelidir. O zaman tam olarak sistem dışında çalışmaya nasıl karar verdiklerine dair ayrıntılı semantikler icat etmeye gerek yoktur.
fumanchu

@DarrylHebbes Ne demek istiyorsun, Chrome geliştirici konsolunun Ağ sekmesinde bir istekte bulunabilir ve yanıtın tüm içeriğini görebilirim.
jaapz

Boş sonuçlar döndüren herhangi bir sorgu için bir hata (404 ya da her neyse) döndürmek inanılmaz derecede kötü. Hiçbir veritabanı bunu yapmaz ve genellikle boş bir sonucu kullanıcıya beklenmedik bir durum olarak bildirmek istemezsiniz. Örnek: Önümüzdeki 30 dakika için planlanmış bir randevu olup olmadığını kontrol etmek için API'nizi sorgularsınız. Hiçbiri yoksa, 404 döndürmemelisiniz. 200 OK ve boş bir dizi döndürün, lütfen.
esmiralha

59

404Kaynak yoksa kullanın . 200Boş bir bedenle geri dönmeyin.

Bu, programlamada undefinedboş dizeye (ör. "") Benzer. Çok benzer olsa da, kesinlikle bir fark var.

404bu URI'de hiçbir şeyin olmadığı anlamına gelir (programlamadaki tanımlanmamış bir değişken gibi). 200Boş bir gövdeyle geri dönmek , orada bir şeyin var olduğu ve şu anda bir şeyin boş olduğu anlamına gelir (programlamadaki boş bir dize gibi).

404"kötü bir URI" olduğu anlamına gelmez. URI hatalarına yönelik özel HTTP kodları vardır (örn. 414 Request-URI Too Long).


1
Hey, sanırım bir şeyin üzerinde olabilirsiniz. "41" den birini döndürmek daha anlamlı olmaz mıydı? kaynak ile ilgili bir sorun olduğunda hatalar? Örneğin, 410 Gitti araçlar "İstenen kaynak sunucuda artık mevcut değil ve yönlendirme adresi biliniyor." - (Bkz. W3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11 )
Brian Lacy

Aslında, yukarıda bahsettiğim kaynak ayrıca, "Sunucu bilmiyorsa veya koşulun kalıcı olup olmadığını belirleyecek bir olanağı yoksa, bunun yerine 404 (Bulunamadı) durum kodu kullanılmalıdır." Belki de bu mutlaka en iyi seçenek değildir ..
Brian Lacy

2
41x hatalarından herhangi birinin kullanım durumunuza uygun olduğunu düşünmüyorum. Cevabımdaki noktanın daha özlü bir versiyonu olan undefined ile boş dize karşılaştırmasını seviyorum. Bir fark var, ancak bu boş bir dizenin bir hata olduğu anlamına gelmez. Benzetme germek için: Bir yöntem olabilir String getName()böyle bir uygulama vardır: return this.name == null ? "" : this.name. İstemci biliyor istemedikçe Bu yanlış değil this.nameise null.
jhericks

1
404 hala en uygun seçenektir. Bu 404 yanıtının gövdesini, kullanıcıyı / istemciyi hatayı almak için belirli ayrıntılar hakkında bilgilendirmek için kullanabilirsiniz (ve kullanmaya teşvik edilirsiniz). Bkz . Stackoverflow.com/a/9999335/105484 . Sizin durumunuzda, bu gövdeyi kullanıcıya URI'larını / restapi / user / USER_ID
nategood

Tamam, burada bazı iyi noktaları görebiliyorum, ancak 4XX hata kodları, bu bir hata durumu nasıl? Müşteri 404'ün olmasını nasıl önleyebilir?
Krzysztof Kubicki

20

Çoğu şeyde olduğu gibi, "duruma bağlıdır". Ama bana göre, uygulama kötü değil ve HTTP Spec karşı gitmiyor haddi zatında . Ancak, bazı şeyleri temizleyelim.

İlk olarak, URI'ler opak olmalıdır. İnsanlara opak olmasalar bile, makinelere opaktırlar. Diğer bir deyişle, arasındaki fark http://mywebsite/api/user/13, http://mywebsite/restapi/user/13arasındaki fark ile aynıdır http://mywebsite/api/user/13ve http://mywebsite/api/user/14aynı aynı dönemi değildir yani değil. Yani bir 404 http://mywebsite/api/user/14(eğer böyle bir kullanıcı yoksa) için tamamen uygun olacaktır , ancak tek uygun cevap mutlaka gerekli değildir.

Ayrıca boş bir 200 yanıtı veya daha açık bir şekilde 204 (İçerik Yok) yanıtı döndürebilirsiniz. Bu, müşteriye başka bir şey iletecektir. Tarafından tanımlanan kaynağın http://mywebsite/api/user/14hiçbir içeriği olmadığı veya aslında hiçbir şey olmadığı anlamına gelir . Bu böyle bir kaynak olduğu anlamına gelir . Bununla birlikte, mutlaka 14 numaralı bir veri deposunda kalıcı olan bazı kullanıcılar olduğunu iddia ettiğiniz anlamına gelmez. Dolayısıyla, kaynaklarınızı bu şekilde modellemek mantıklıysa, devam edin.

Müşterilerinize meşru URI'leri tahmin etmelerini kolaylaştıracak bilgiler vermenin bazı güvenlik etkileri vardır. 404 yerine miss'lere 200 döndürmek, müşteriye en azından http://mywebsite/api/userparçanın doğru olduğuna dair bir ipucu verebilir . Kötü amaçlı bir istemci yalnızca farklı tamsayıları denemeye devam edebilir. Ama bana göre, kötü niyetli bir müşteri bu http://mywebsite/api/userparçayı zaten tahmin edebilirdi . Daha iyi bir çözüm UUID'leri kullanmak olacaktır. yani http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66daha iyidir http://mywebsite/api/user/14. Bunu yaparak, 200'leri iade etme tekniğinizi çok fazla vermeden kullanabilirsiniz.


1
Bu, seçtiğim ve 404 yerine 204 kullanarak gittiğim çözüm. Bana göre 204, "Kodunuzu buldum, ancak sonuç bulamadım" ve 404, "Çalıştırmak için herhangi bir kod bulamadım, bu yanlış yol? "
Matt Sanders

11

404 Not Found teknik uri anda anlamına gelir haritasına bir kaynağa. Örneğinizde, bu URL'nin hiçbir zaman bir kaynağa eşlenmediğini ima etmek http://mywebsite/api/user/13için 404 döndüren bir isteği yorumluyorum . Müşteri için, bu konuşmanın sonu olmalıdır.

Belirsizlikle ilgili endişeleri gidermek için, diğer yanıt kodlarını sağlayarak API'nizi geliştirebilirsiniz. Örneğin, istemcilerin URL'yi GET istekleri yayınlamasına izin vermek http://mywebsite/api/user/13istediğinizi, istemcilerin standart URL'yi kullanmaları gerektiğini bildirmek istediğinizi varsayalım http://mywebsite/restapi/user/13. Bu durumda, 301 Kalıcı Olarak Taşındı'yı döndürerek ve yanıtın Konum başlığına standart URL'yi sağlayarak kalıcı bir yönlendirme yayınlamayı düşünebilirsiniz . Bu, müşteriye gelecekteki istekler için standart URL'yi kullanmaları gerektiğini bildirir.


11

Yani özünde, cevap isteğin nasıl oluştuğuna bağlı olabilir.

İstenen kaynak, bir talebe göre URI'nin bir parçasını oluşturuyorsa http://mywebsite/restapi/user/13ve kullanıcı 13 mevcut değilse, 404 muhtemelen uygun ve sezgiseldir, çünkü URI mevcut olmayan bir kullanıcı / varlık / belge / vb. Aynı şey, bir GUID http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66ve yukarıdaki api / restapi argümanını kullanan daha güvenli teknik için de geçerlidir.

Bununla birlikte, talep edilen kaynak kimliği istek başlığına [kendi örneğinizi ekleyin] veya gerçekten URI'ye parametre olarak eklenmişse, örneğin http://mywebsite/restapi/user/?UID=13, URI yine de doğru olacaktır (çünkü bir KULLANICI kavramı şu adresten çıkar http://mywebsite/restapi/user/); ve bu nedenle yanıtın makul olarak 200 (uygun şekilde ayrıntılı bir mesajla) olması beklenebilir, çünkü 13 olarak bilinen belirli kullanıcı mevcut değildir ancak URI vardır. Bu şekilde URI'nin iyi olduğunu söylüyoruz, ancak veri talebinin içeriği yok.

Şahsen 200 hala doğru hissetmiyor (daha önce iddia ettiğim halde). 200 yanıt kodu (ayrıntılı bir yanıt olmadan), örneğin yanlış bir kimlik gönderildiğinde sorunun araştırılmamasına neden olabilir.

Daha iyi bir yaklaşım, bir 204 - No Contentyanıt göndermek olacaktır . Bu sunucu isteği yerine getirdi fakat bir varlık gövdesi göndermemesi gerekmez ve güncellenmiş metainformation dönmek isteyebilirsiniz * W3C bilgi ile uyumludur. * 1 karışıklık, Bence belirten Vikipedi girişi kaynaklanır içinde 204 İçerik Yok - Sunucu isteği başarıyla işledi ancak içerik döndürmüyor. Genellikle başarılı bir silme isteğine yanıt olarak kullanılır .Son cümle son derece tartışmalıdır. Bu cümle olmadan durumu düşünün ve çözüm kolaydır - eğer varlık yoksa bir 204 gönderin. 404 yerine 204 döndürmek için bir argüman bile var, istek işlendi ve içerik döndürülmedi! Bununla birlikte, 204'lerin yanıt gövdesinde içeriğe izin vermediğini lütfen unutmayın

Kaynaklar

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 1. http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html


9

Bu çok eski bir yazı ama benzer bir sorunla karşılaştım ve deneyimlerimi sizlerle paylaşmak istiyorum.

Ben dinlenme API'leri ile mikro hizmet mimarisi inşa ediyorum. Bazı dinlenme GET hizmetleri var, onlar istek parametrelerine dayalı arka uç sistemden veri toplamak.

Geri kalan API tasarım belgelerini takip ettim ve sorgu koşullarına uygun bir veri olmadığında (örneğin sıfır kayıt seçildi) HTTP 404'ü mükemmel bir JSON hata mesajıyla geri gönderdim.

Müşteriye geri gönderilecek veri olmadığında, müşteriyi "Bulunamadı" nedeni hakkında bilgilendirmek için dahili hata kodu vb. İle mükemmel bir JSON mesajı hazırladım ve HTTP 404 ile istemciye geri gönderildi. iyi çalışıyor.

Daha sonra HTTP iletişimiyle ilgili kodu gizlemek için kolay bir yardımcı olan bir dinlenme API istemci sınıfı oluşturduk ve bu yardımcıyı kodumdan kalan API'ları aradığımda her zaman kullandım.

AMA HTTP 404'ün iki farklı işlevi olduğu için kafa karıştırıcı ekstra kod yazmam gerekiyordu:

  • dinlenme URL'si belirtilen url'de mevcut olmadığında gerçek HTTP 404, uygulama API'si veya dinlenme API uygulamasının çalıştığı web sunucusu tarafından atılır
  • sorgunun nerede durumuna göre veritabanında veri olmadığında istemci HTTP 404'ü de geri alır.

Önemli: Dinlenme API'sı hata işleyicim tüm istisnaları yakalar, arka uç hizmetinde görünür, bu da herhangi bir hata durumunda dinlenme API'mın her zaman mesaj ayrıntılarıyla mükemmel bir JSON mesajı ile döndüğü anlamına gelir.

Bu, iki farklı HTTP 404 yanıtını işleyen istemci yardımcı yöntemimin 1. sürümüdür:

public static String getSomething(final String uuid) {
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        // HTTP 200
        return response.readEntity(String.class);
    } else {
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try {
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

        } catch (IOException e) {
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        }

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");
}

AMA , Java veya JavaScript istemcim bir şekilde HTTP 404 iki tür alabilir çünkü HTTP 404 durumunda yanıt gövdesini kontrol etmek gerekir. Yanıt gövdesini ayrıştırabilirsem o zaman orada bir yanıt aldım eminim istemciye geri gönderilecek veri yok.

Ben yanıtı ayrıştırmak mümkün değilse, ben web sunucusundan gerçek bir HTTP 404 (geri kalan API uygulama değil) aldım anlamına gelir.

Bu çok kafa karıştırıcı ve istemci uygulamasının HTTP 404'ün gerçek nedenini kontrol etmek için her zaman ekstra ayrıştırma yapması gerekiyor.

Dürüst olmak gerekirse bu çözümü sevmiyorum. Kafa karıştırıcı, müşterilere her zaman ekstra saçma kodu eklemesi gerekiyor.

Bu yüzden bu iki farklı senaryoda HTTP 404 kullanmak yerine aşağıdakileri yapmaya karar verdim:

  • Artık kalan uygulamada HTTP 404 yanıt HTTP kodu olarak kullanmıyorum.
  • HTTP 404 yerine HTTP 204 (İçerik Yok) kullanacağım.

Bu durumda istemci kodu daha şık olabilir:

public static String getString(final String processId, final String key) {
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        return response.readEntity(String.class);
    } else {
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    }

    throw new AnyServerError(response, requestUrl, httpMethod);
}

Bence bu durum daha iyi işliyor.

Daha iyi bir çözümünüz varsa lütfen bizimle paylaşın.


Apache HttpClient yöntemleri 204 yanıta istisna atmaz. Müşteriniz yalnızca iş nesnelerini (modelleri) yanıtlarsa, bu null değerini döndürür. Çalışır, ancak son kullanıcıların 204 durumunu tespit etmesi zordur.
chrisinmtown

Her şey iyi, ama bir soru şu: 404 için ifadeler ne sıklıkta yazıyorsunuz? Ve ne olurdu? Öte yandan, içinde json hatası olan 404'ü tanımlayan api'ye bir çağrı yaparsanız, sadece bu durum için işleyebilirsiniz.
Maksimum

Sana katılıyorum. Verileri döndüren bir hizmetim var. Ancak verilerin bozulduğu durumlar vardır. İstek URL'si doğru (bu nedenle 404 değil), ancak gerçekten bir mantık hatası (500) değil, bu yüzden 204 en uygun görünüyor. Mesaj gövdesi eksikliğinden dolayı gerçekten sinir bozucu. Her ne kadar tartışmasız bir Header'a bir sebep ekleyebilirim.
cs94njw


1

Tekdüzen Kaynak Tanımlayıcısı kaynağın benzersiz bir göstergesidir. Kötü biçimli bir URI kaynağa işaret etmez ve bu nedenle üzerinde bir GET gerçekleştirilmesi bir kaynak döndürmez. 404, sunucunun Request-URI ile eşleşen bir şey bulamadığı anlamına gelir. Eğer yanlış URI veya kötü URI koyarsanız sorun ve bir HTML sayfasına veya IMG bir kaynağa alamadım nedeni.


0

Bu senaryo için HTTP 404, REST API'sinin yanıtı için yanıt kodudur 400, 401, 404, 422 işlenemez varlık gibi

kural dışı durum iletisinin tamamını denetlemek için Kural Dışı Durum işleme özelliğini kullanın.

try{
  // call the rest api
} catch(RestClientException e) {
     //process exception
     if(e instanceof HttpStatusCodeException){
        String responseText=((HttpStatusCodeException)e).getResponseBodyAsString();
         //now you have the response, construct json from it, and extract the errors
         System.out.println("Exception :" +responseText);
     }

}

Bu istisna bloğu size REST API tarafından atılan uygun mesajı verir

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.