REST API 404: URI buruk, atau Sumber Daya Hilang?

219

Saya sedang membangun API REST, tetapi saya mengalami masalah.

Tampaknya praktik yang diterima dalam merancang API REST adalah bahwa jika sumber daya yang diminta tidak ada, 404 dikembalikan.

Namun, bagi saya, ini menambah ambiguitas yang tidak perlu. HTTP 404 lebih tradisional dikaitkan dengan URI yang buruk. Jadi sebenarnya kami mengatakan, "Anda berada di tempat yang tepat, tetapi catatan spesifik itu tidak ada, atau tidak ada lokasi seperti itu di Internet! Saya benar-benar tidak yakin yang mana ..."

Pertimbangkan URI berikut:

http://mywebsite/api/user/13

Jika saya mendapatkan kembali 404, apakah itu karena Pengguna 13 tidak ada? Atau karena URL saya seharusnya :

http://mywebsite/restapi/user/13

Di masa lalu, saya baru saja mengembalikan hasil NULL dengan HTTP 200 OKkode respons jika catatan tidak ada. Ini sederhana, dan menurut saya sangat bersih, bahkan jika itu belum tentu diterima. Tetapi apakah ada cara yang lebih baik untuk melakukan ini?

Brian Lacy
sumber
Mungkin duplikat. stackoverflow.com/questions/3821663/…
Spencer Kormos
7
Pertanyaan lain tampaknya terkait dengan format string URI Query. Diskusi pada 404 tidak cukup untuk menjawab pertanyaan saya, yaitu apakah ada cara yang lebih tepat atau berguna untuk menentukan apa arti sebenarnya dari 404. Saya mengulasnya sebelum memposting.
Brian Lacy
Apakah normal ketika browser concole mengandung kesalahan 404? Ketika saya melakukan operasi reguler dengan uri yang benar tetapi sumber daya tidak ditemukan.
Vasiliy Mazhekin
3
404 tidak menunjukkan URI buruk, itu menunjukkan sumber daya Tidak Ditemukan. Itu mungkin karena tidak ada pengguna '13', atau mungkin karena tidak ada sumber daya / situs web / api.
ChrisV

Jawaban:

101

404 hanyalah kode respons HTTP. Selain itu, Anda dapat memberikan badan tanggapan dan / atau header lainnya dengan pesan kesalahan yang lebih bermakna yang akan dilihat oleh pengembang.

Robert Levy
sumber
7
Ini masuk akal. Saya harus bertanya-tanya, apakah manfaat sebenarnya diperoleh dari mengembalikan 404 di tempat pertama, dibandingkan mengembalikan 200 dengan respons nol?
Brian Lacy
15
Jika Anda mengembalikan 200 dengan nol Anda akan menentang HTTP Spec. Anda dapat melakukan ini, tetapi kemudian api Anda tidak akan mematuhi Kendala REST "Uniformed Interface".
menggugat
5
... dan badan tanggapan Anda harus menyertakan hyperlink ke URI yang valid. Kecuali URI root, dan mungkin satu atau dua bookmark, klien Anda harus selalu mengikuti tautan yang diberikan kepada mereka oleh server. Maka tidak perlu menemukan semantik terperinci tentang bagaimana mereka memutuskan untuk bekerja di luar sistem.
fumanchu
@DarrylHebbes apa maksud Anda, saya dapat melakukan permintaan dan melihat isi respons lengkap di tab Jaringan pada konsol pengembang Chrome.
jaapz
Sangat buruk untuk mengembalikan kesalahan (404 atau apa pun) untuk setiap kueri yang mengembalikan hasil kosong. Tidak ada database yang akan melakukan itu dan biasanya Anda tidak ingin melaporkan hasil kosong sebagai kondisi yang tidak terduga kepada pengguna. Contoh: Anda meminta api Anda untuk memeriksa apakah ada janji yang dijadwalkan untuk 30 menit berikutnya. Jika tidak ada, Anda TIDAK boleh mengembalikan 404. Tolong kembalikan 200 OK dan array kosong.
esmiralha
59

Gunakan 404jika sumber daya tidak ada. Jangan kembali 200dengan tubuh kosong.

Ini mirip dengan undefinedstring kosong (misalnya "") dalam pemrograman. Meskipun sangat mirip, pasti ada perbedaan.

404berarti tidak ada yang ada pada URI itu (seperti variabel yang tidak didefinisikan dalam pemrograman). Kembali 200dengan tubuh kosong berarti ada sesuatu yang ada di sana dan ada sesuatu yang kosong sekarang (seperti string kosong dalam pemrograman).

404tidak berarti itu adalah "URI buruk". Ada kode HTTP khusus yang dimaksudkan untuk kesalahan URI (misalnya 414 Request-URI Too Long).

baik
sumber
1
Hei, saya pikir Anda mungkin ke sesuatu. Bukankah lebih masuk akal untuk mengembalikan salah satu dari "41?" kesalahan saat ada masalah dengan sumber daya yang diminta? Misalnya, 410 Hilang berarti "Sumber daya yang diminta tidak lagi tersedia di server dan tidak ada alamat penerusan yang diketahui." - (Lihat w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11 )
Brian Lacy
Sebenarnya, sumber daya yang saya rujuk di atas juga mengatakan, "Jika server tidak tahu, atau tidak memiliki fasilitas untuk menentukan, apakah kondisinya permanen atau tidak, kode status 404 (Tidak Ditemukan) HARUS digunakan sebagai gantinya." Jadi mungkin itu belum tentu pilihan terbaik ..
Brian Lacy
2
Saya tidak berpikir ada kesalahan 41x yang sesuai dengan kasus penggunaan Anda. Saya suka perbandingan untuk string tidak terdefinisi vs kosong, yang merupakan versi yang lebih ringkas dari poin saya dalam jawaban saya. Ada perbedaan, tetapi itu tidak berarti bahwa string kosong adalah kesalahan. Untuk meregangkan analogi: Anda bisa memiliki metode String getName()yang memiliki implementasi seperti ini: return this.name == null ? "" : this.name. Itu tidak benar kecuali jika Anda ingin klien untuk mengetahui bahwa this.nameadalah null.
jhericks
1
404 masih merupakan opsi yang paling tepat. Anda dapat (dan didorong untuk) menggunakan isi dari respons 404 itu untuk memberi tahu pengguna / klien perincian spesifik untuk menerima kesalahan. Lihat: stackoverflow.com/a/9999335/105484 . Dalam kasus Anda, Anda mungkin ingin menggunakan badan itu untuk menyarankan kepada pengguna agar mereka memformat ulang URI mereka agar terlihat seperti / restapi / user / USER_ID
nategood
Ok, saya bisa melihat beberapa poin bagus di sini, tetapi 4XX adalah kode kesalahan, bagaimana itu merupakan situasi kesalahan? Bagaimana klien dapat mencegah 404 terjadi?
Krzysztof Kubicki
20

Seperti kebanyakan hal, "itu tergantung". Tapi bagiku, latihan tidak buruk dan tidak akan melawan HTTP spec per se . Namun, mari kita jelaskan beberapa hal.

Pertama, URI harus buram. Bahkan jika mereka tidak buram pada orang, mereka tidak jelas pada mesin. Dengan kata lain, perbedaan antara http://mywebsite/api/user/13, http://mywebsite/restapi/user/13sama dengan perbedaan antara http://mywebsite/api/user/13dan http://mywebsite/api/user/14yaitu tidak sama bukan periode yang sama. Jadi 404 akan sepenuhnya sesuai untuk http://mywebsite/api/user/14(jika tidak ada pengguna seperti itu) tetapi tidak harus satu-satunya respons yang tepat.

Anda juga dapat mengembalikan 200 tanggapan kosong atau lebih eksplisit respons 204 (Tidak Ada Konten). Ini akan menyampaikan sesuatu yang lain kepada klien. Ini akan menyiratkan bahwa sumber daya yang diidentifikasi oleh http://mywebsite/api/user/14tidak memiliki konten atau pada dasarnya tidak ada. Ini artinya bahwa ada adalah sumber daya seperti itu. Namun, itu tidak selalu berarti bahwa Anda mengklaim ada beberapa pengguna yang bertahan di penyimpanan data dengan id 14. Itu urusan pribadi Anda, bukan urusan klien yang mengajukan permintaan. Jadi, jika masuk akal untuk memodelkan sumber daya Anda seperti itu, silakan.

Ada beberapa implikasi keamanan untuk memberikan informasi kepada klien Anda yang akan membuatnya lebih mudah bagi mereka untuk menebak URI yang sah. Mengembalikan 200 pada kesalahan alih-alih 404 dapat memberi klien petunjuk bahwa setidaknya http://mywebsite/api/userbagian itu benar. Klien jahat bisa terus mencoba bilangan bulat yang berbeda. Tetapi bagi saya, klien jahat tetap bisa menebak http://mywebsite/api/userbagian itu. Obat yang lebih baik adalah dengan menggunakan UUID. yaitu http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66lebih baik daripada http://mywebsite/api/user/14. Melakukan itu, Anda bisa menggunakan teknik mengembalikan 200 tanpa memberikan banyak.

jhericks
sumber
1
Ini adalah solusi yang saya pilih dan gunakan dengan 204 daripada 404. Bagi saya 204 berarti "Saya menemukan kode Anda, tetapi tidak menemukan hasil" dan 404 berarti "Saya tidak dapat menemukan kode untuk dieksekusi, apakah ini jalan yang salah? "
Matt Sanders
11

404 Tidak Ditemukan secara teknis berarti uri saat ini tidak memetakan ke sumber daya. Dalam contoh Anda, saya menafsirkan permintaan http://mywebsite/api/user/13yang mengembalikan a 404 yang menyiratkan bahwa url ini tidak pernah dipetakan ke sumber daya. Kepada klien, itu harus menjadi akhir dari percakapan.

Untuk mengatasi masalah ambiguitas, Anda dapat meningkatkan API Anda dengan memberikan kode respons lain. Misalnya, jika Anda ingin mengizinkan klien mengeluarkan GET permintaan url http://mywebsite/api/user/13, Anda ingin berkomunikasi bahwa klien harus menggunakan url kanonik http://mywebsite/restapi/user/13. Dalam hal ini, Anda mungkin ingin mempertimbangkan mengeluarkan pengalihan permanen dengan mengembalikan 301 Moved Permanently dan memasok url kanonik di header Lokasi respons. Ini memberi tahu klien bahwa untuk permintaan di masa mendatang mereka harus menggunakan url kanonik.

Ralph Allan Rice
sumber
11

Jadi intinya, sepertinya jawabannya bisa bergantung pada bagaimana permintaan itu terbentuk.

Jika sumber daya yang diminta merupakan bagian dari URI sesuai permintaan http://mywebsite/restapi/user/13dan pengguna 13 tidak ada, maka 404 mungkin sesuai dan intuitif karena URI mewakili pengguna / entitas / dokumen / dll yang tidak ada. Hal yang sama berlaku untuk teknik yang lebih aman menggunakan GUID http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66dan argumen api / restapi di atas.

Namun, jika ID sumber daya yang diminta dimasukkan dalam tajuk permintaan [sertakan contoh Anda sendiri], atau memang, dalam URI sebagai parameter, mis. http://mywebsite/restapi/user/?UID=13Maka URI akan tetap benar (karena konsep PENGGUNA tidak ada padahttp://mywebsite/restapi/user/ ); dan oleh karena itu responsnya bisa masuk akal diharapkan menjadi 200 (dengan pesan yang tepat verbose) karena pengguna spesifik yang dikenal sebagai 13 tidak ada tetapi URI tidak. Dengan cara ini kami mengatakan URI baik, tetapi permintaan untuk data tidak memiliki konten.

Secara pribadi 200 masih terasa tidak benar (meskipun saya sebelumnya berpendapat itu benar). 200 kode respons (tanpa respons verbal) dapat menyebabkan masalah untuk tidak diselidiki ketika ID yang salah dikirim misalnya.

Pendekatan yang lebih baik adalah mengirim 204 - No Contenttanggapan. Ini sesuai dengan deskripsi w3c * Server telah memenuhi permintaan tetapi tidak perlu mengembalikan entitas-badan, dan mungkin ingin mengembalikan metainformation yang diperbarui. * 1 Kebingungan, menurut saya disebabkan oleh entri Wikipedia yang menyatakan 204 Tidak Ada Konten - Server berhasil memproses permintaan, tetapi tidak mengembalikan konten apa pun. Biasanya digunakan sebagai respons terhadap permintaan penghapusan yang berhasil .Kalimat terakhir sangat bisa diperdebatkan. Pertimbangkan situasi tanpa kalimat itu dan solusinya mudah - cukup kirim 204 jika entitas tidak ada. Bahkan ada argumen untuk mengembalikan 204 bukannya 404, permintaan telah diproses dan tidak ada konten yang dikembalikan! Perlu diketahui juga, 204's tidak memperbolehkan konten di badan respons

Sumber

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

john
sumber
9

Itu adalah posting yang sangat lama tetapi saya menghadapi masalah yang sama dan saya ingin berbagi pengalaman saya dengan kalian.

Saya sedang membangun arsitektur microservice dengan API lainnya. Saya punya beberapa layanan GET istirahat, mereka mengumpulkan data dari sistem back-end berdasarkan parameter permintaan.

Saya mengikuti dokumen desain API lainnya dan saya mengirim kembali HTTP 404 dengan pesan kesalahan JSON yang sempurna ke klien ketika tidak ada data yang selaras dengan kondisi kueri (misalnya, nol catatan dipilih).

Ketika tidak ada data untuk dikirim kembali ke klien saya menyiapkan pesan JSON sempurna dengan kode kesalahan internal, dll. Untuk menginformasikan klien tentang alasan "Tidak Ditemukan" dan itu dikirim kembali ke klien dengan HTTP 404. Itu bekerja dengan baik.

Kemudian saya telah membuat kelas klien API sisanya yang merupakan bantuan mudah untuk menyembunyikan kode terkait komunikasi HTTP dan saya menggunakan bantuan ini setiap saat ketika saya memanggil API sisanya dari kode saya.

TETAPI saya perlu menulis kode tambahan yang membingungkan hanya karena HTTP 404 memiliki dua fungsi berbeda:

  • HTTP 404 asli ketika API sisanya tidak tersedia di url yang diberikan, itu dilemparkan oleh server aplikasi atau server web tempat aplikasi API lainnya berjalan
  • klien mendapatkan kembali HTTP 404 juga ketika tidak ada data dalam database berdasarkan kondisi permintaan di mana.

Penting: Penangan kesalahan API istirahat saya menangkap semua pengecualian yang muncul di layanan back-end yang berarti jika terjadi kesalahan, API sisanya selalu kembali dengan pesan JSON sempurna dengan detail pesan.

Ini adalah versi pertama dari metode pembantu klien saya yang menangani dua respons HTTP 404 yang berbeda:

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");
}

TAPI , karena klien Java atau JavaScript saya dapat menerima dua jenis HTTP 404, entah bagaimana, saya perlu memeriksa isi respons jika ada HTTP 404. Jika saya dapat menguraikan respons, maka saya yakin saya mendapatkan kembali respons di mana ada tidak ada data untuk dikirim kembali ke klien.

Jika saya tidak dapat menguraikan respons itu berarti saya mendapatkan kembali HTTP 404 nyata dari server web (bukan dari aplikasi API lainnya).

Sangat membingungkan dan aplikasi klien selalu perlu melakukan parsing ekstra untuk memeriksa alasan sebenarnya dari HTTP 404.

Jujur saya tidak suka solusi ini. Ini membingungkan, perlu menambahkan kode omong kosong tambahan ke klien sepanjang waktu.

Jadi alih-alih menggunakan HTTP 404 dalam dua skenario berbeda ini, saya memutuskan untuk melakukan hal berikut:

  • Saya tidak menggunakan HTTP 404 sebagai respons kode HTTP dalam aplikasi lain saya.
  • Saya akan menggunakan HTTP 204 (Tidak Ada Konten) alih-alih HTTP 404.

Dalam hal ini kode klien bisa lebih elegan:

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);
}

Saya pikir ini menangani masalah itu dengan lebih baik.

Jika Anda memiliki solusi yang lebih baik, silakan bagikan dengan kami.

Zappee
sumber
Metode Apache HttpClient tidak memberikan pengecualian pada respons 204. Jika klien Anda hanya menjawab objek bisnis (model), maka itu akan mengembalikan nol. Bekerja, tetapi sulit bagi pengguna akhir untuk mendeteksi situasi 204.
chrisinmtown
Semuanya baik, tetapi pertanyaannya adalah: seberapa sering Anda menulis pernyataan if untuk 404? Dan apakah itu? Di sisi lain, jika Anda membuat panggilan ke api, yang menentukan kemungkinan 404 dengan json kesalahan di dalam, Anda dapat mengatasinya hanya untuk kasus ini.
Maks
zappee Saya setuju dengan Anda. Saya memiliki layanan yang mengembalikan data. Tetapi ada situasi di mana data rusak. URL permintaan sudah benar (jadi bukan 404), tetapi sebenarnya bukan kesalahan logika (500), jadi 204 sepertinya yang paling cocok. Ini benar-benar menjengkelkan tentang kurangnya hal isi pesan. Meskipun bisa dibilang saya bisa memasukkan alasan ke Header.
cs94njw
6

Artikel tua tapi bagus ini ... http://www.infoq.com/articles/webber-rest-workflow mengatakan ini tentang hal itu ...

404 Tidak Ditemukan - Layanan ini terlalu malas (atau aman) untuk memberi kami alasan sebenarnya mengapa permintaan kami gagal, tetapi apa pun alasannya, kami harus menanganinya.

Michael Dausmann
sumber
1

Uniform Resource Identifier adalah penunjuk unik ke sumber daya. Bentuk URI yang buruk tidak menunjuk ke sumber daya dan karenanya melakukan GET di atasnya tidak akan mengembalikan sumber daya. 404 berarti Server belum menemukan sesuatu yang cocok dengan Request-URI. Jika Anda memasukkan URI yang salah atau URI yang buruk itu adalah masalah Anda dan alasan Anda tidak mendapatkan sumber daya apakah halaman HTML atau IMG.

menggugat
sumber
0

Untuk skenario ini, HTTP 404 adalah kode respons untuk respons dari REST API. Seperti 400, 401, 404, 422 entitas yang tidak dapat diproses

gunakan penanganan Exception untuk memeriksa pesan pengecualian penuh.

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);
     }

}

Blok pengecualian ini memberi Anda pesan yang tepat yang dilemparkan oleh REST API

Venkata Naresh Babu
sumber