Apa cara terbaik untuk membuat model respons kesalahan REST API dan sistem kode kesalahan?

15

Implementasi REST saya akan mengembalikan kesalahan di JSON dengan struktur berikut:

{
 "http_response":400,
 "dev_message":"There is a problem",
 "message_for_user":"Bad request",
 "some_internal_error_code":12345
}

Saya menyarankan untuk membuat model respons khusus, di mana saya dapat memberikan nilai yang diperlukan untuk properti (dev_message, message_for_user, some_internal_error_code), dan mengembalikannya. Dalam kode akan mirip dengan ini:

$responseModel = new MyResponseModel(400,"Something is bad", etc...);

Seperti apa model ini? Haruskah saya menerapkan metode mis. SuccessResponse () di mana saya hanya akan menyampaikan informasi teks, dan kode akan menjadi default 200 di sana? Saya terjebak dengan ini. Dan ini adalah bagian pertama dari pertanyaan saya: apakah saya perlu menerapkan model ini, apakah ini praktik yang baik? Karena untuk sekarang, saya hanya mengembalikan array langsung dari kode.

Bagian kedua adalah tentang sistem kode kesalahan. Kode kesalahan akan dijelaskan dalam dokumentasi. Tetapi masalah yang saya temui adalah dalam kode. Apa cara terbaik untuk mengelola kode kesalahan? Haruskah saya menuliskannya di dalam model? Atau akan lebih baik untuk membuat layanan terpisah untuk menangani ini?

PEMBARUAN 1

Saya sudah mengimplementasikan kelas model untuk respons. Ini mirip dengan jawaban Greg, logika yang sama, tetapi selain itu saya memiliki kesalahan dalam model hardcoded tertulis dan di sini adalah bagaimana tampilannya:

    class ErrorResponse
    {
     const SOME_ENTITY_NOT_FOUND = 100;
     protected $errorMessages = [100 => ["error_message" => "That entity doesn't exist!"]];

     ...some code...
    }

Kenapa saya melakukan ini? Dan untuk apa?

  1. Itu terlihat keren dalam kode: return new ErrorResponse(ErrorResponse::SOME_ENTITY_NOT_FOUND );
  2. Mudah untuk mengubah pesan kesalahan. Semua pesan berada di satu tempat, bukan pengontrol / layanan / dll atau apa pun yang Anda akan menempatkannya.

Jika Anda memiliki saran untuk meningkatkan ini, silakan, komentar.

Grokking
sumber

Jawaban:

13

Dalam situasi ini, saya selalu memikirkan antarmuka terlebih dahulu, kemudian menulis kode PHP untuk mendukungnya.

  1. Ini adalah REST API, jadi kode status HTTP yang berarti adalah suatu keharusan.
  2. Anda ingin struktur data yang fleksibel dan konsisten dikirim ke dan dari klien.

Mari kita pikirkan semua hal yang bisa salah dan kode status HTTP mereka:

  • Server melempar kesalahan (500)
  • Kegagalan otentikasi (401)
  • Sumber daya yang diminta tidak ditemukan (404)
  • Data yang Anda modifikasi telah diubah sejak Anda memuatnya (409)
  • Kesalahan validasi saat menyimpan data (422)
  • Klien telah melampaui tingkat permintaan mereka (429)
  • Jenis file tidak didukung (415)

Perhatikan, ada orang lain yang bisa Anda teliti nanti.

Untuk sebagian besar kondisi kegagalan, hanya ada satu pesan kesalahan yang akan dikembalikan. Itu422 Unprocessable Entity respon, yang saya digunakan untuk "kesalahan validasi" bisa kembali lebih dari satu kesalahan --- Satu atau lebih kesalahan per formulir isian.

Kami membutuhkan struktur data yang fleksibel untuk respons kesalahan.

Ambil sebagai contoh, 500 Internal Server Error:

HTTP/1.1 500 Internal Server Error
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

{
    "errors": {
        "general": [
            "Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
        ]
    }
}

Bandingkan dengan kesalahan validasi sederhana ketika mencoba POST sesuatu ke server:

HTTP/1.1 422 Unprocessable Entity
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

{
    "errors": {
        "first_name": [
            "is required"
        ],
        "telephone": [
            "should not exceed 12 characters",
            "is not in the correct format"
        ]
    }
}

Kunci mereka di sini adalah tipe konten text/json. Ini memberi tahu aplikasi klien bahwa mereka dapat men-decode isi respons dengan dekoder JSON. Jika, katakanlah, kesalahan server internal tidak terdeteksi dan halaman web "Sesuatu yang salah" generik Anda dikirimkan sebagai gantinya, jenis konten harusnya text/html; charset=utf-8sehingga aplikasi klien tidak akan mencoba untuk memecahkan kode badan respons sebagai JSON.

Ini terlihat semua menemukan dan keren, sampai Anda perlu mendukung tanggapan JSONP . Anda harus mengembalikan 200 OKrespons, bahkan untuk kegagalan. Dalam hal ini Anda harus mendeteksi bahwa klien meminta respons JSONP (biasanya dengan mendeteksi parameter permintaan URL yang disebutcallback ) dan mengubah sedikit struktur data:

(DAPATKAN / posting / 123? Callback = displayBlogPost)

<script type="text/javascript" src="/posts/123?callback=displayBlogPost"></script>

HTTP/1.1 200 OK
Content-Type: text/javascript
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...

displayBlogPost({
    "status": 500,
    "data": {
        "errors": {
            "general": [
                "Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
            ]
        }
    }
});

Maka penangan respons pada klien (dalam browser web) harus memiliki fungsi JavaScript global displayBlogPostyang disebut yang menerima argumen tunggal. Fungsi ini harus menentukan apakah respons berhasil:

function displayBlogPost(response) {
    if (response.status == 500) {
        alert(response.data.errors.general[0]);
    }
}

Jadi kami sudah merawat klien. Sekarang, mari kita jaga servernya.

<?php

class ResponseError
{
    const STATUS_INTERNAL_SERVER_ERROR = 500;
    const STATUS_UNPROCESSABLE_ENTITY = 422;

    private $status;
    private $messages;

    public function ResponseError($status, $message = null)
    {
        $this->status = $status;

        if (isset($message)) {
            $this->messages = array(
                'general' => array($message)
            );
        } else {
            $this->messages = array();
        }
    }

    public function addMessage($key, $message)
    {
        if (!isset($message)) {
            $message = $key;
            $key = 'general';
        }

        if (!isset($this->messages[$key])) {
            $this->messages[$key] = array();
        }

        $this->messages[$key][] = $message;
    }

    public function getMessages()
    {
        return $this->messages;
    }

    public function getStatus()
    {
        return $this->status;
    }
}

Dan untuk menggunakan ini jika terjadi kesalahan server:

try {
    // some code that throws an exception
}
catch (Exception $ex) {
    return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}

Atau saat memvalidasi input pengguna:

// Validate some input from the user, and it is invalid:

$response = new ResponseError(ResponseError::STATUS_UNPROCESSABLE_ENTITY);
$response->addMessage('first_name', 'is required');
$response->addMessage('telephone', 'should not exceed 12 characters');
$response->addMessage('telephone', 'is not in the correct format');

return $response;

Setelah itu, Anda hanya perlu sesuatu yang mengambil objek respons yang dikembalikan dan mengubahnya menjadi JSON dan mengirimkan respons dengan cara riang.

Greg Burghardt
sumber
Terima kasih atas jawabannya! Saya sudah menerapkan solusi serupa. Satu-satunya perbedaan yang saya tidak mengirimkan pesan sendiri, mereka sudah diatur (lihat pertanyaan saya yang diperbarui).
Grokking
-2

Saya menghadapi sesuatu yang serupa, saya melakukan 3 hal,

  1. Dibuat ExceptionHandler untuk saya sendiri yang disebut ABCException.

Karena saya menggunakan Java & Spring,

Saya mendefinisikannya sebagai

 public class ABCException extends Exception {
private String errorMessage;
private HttpStatus statusCode;

    public ABCException(String errorMessage,HttpStatus statusCode){
            super(errorMessage);
            this.statusCode = statusCode;

        }
    }

Kemudian menyebutnya dimanapun diperlukan, seperti ini,

throw new ABCException("Invalid User",HttpStatus.CONFLICT);

Dan ya, Anda perlu membuat ExceptionHandler di controller Anda jika Anda menggunakan layanan web berbasis REST.

Beri anotasi dengan @ExceptionHandlerjika menggunakan Spring

Dave Ranjan
sumber
Programmer adalah tentang pertanyaan konseptual dan jawaban diharapkan dapat menjelaskan banyak hal . Melempar kesedihan alih-alih penjelasannya seperti menyalin kode dari IDE ke papan tulis: mungkin terlihat biasa dan bahkan terkadang dapat dimengerti, tetapi rasanya aneh ... hanya aneh. Papan tulis tidak memiliki kompiler
nyamuk