Який найкращий спосіб створити модель відповіді на помилки REST API та систему кодів помилок?


15

Моя реалізація REST поверне помилки в JSON з наступною структурою:

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

Я пропоную створити спеціальну модель відповідей, де я можу передати необхідні значення для властивостей (dev_message, message_for_user, some_internal_error_code) та повернути їх. У коді це буде подібним до цього:

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

Як повинна виглядати ця модель? Чи потрібно застосовувати методи, наприклад, successResponse (), де я передаватиму лише текстову інформацію, а код буде за замовчуванням 200? Я застряг у цьому. І це перша частина мого запитання: чи потрібно мені реалізовувати цю модель, чи це хороша практика? Тому що зараз я просто повертаю масиви прямо з коду.

Друга частина стосується системи кодів помилок. Коди помилок будуть описані в документації. Але проблема, з якою я стикаюся, полягає в коді. Який найкращий спосіб керувати кодами помилок? Чи варто їх записувати всередині моделі? Або було б краще створити окремий сервіс для вирішення цього питання?

ОНОВЛЕННЯ 1

Я реалізував модельний клас для відповіді. Це схожа відповідь Грега, така ж логіка, але крім того, у мене є жорсткі кодові написані помилки в моделі, і ось, як це виглядає:

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

     ...some code...
    }

Чому я це зробив? А для чого?

  1. Це виглядає круто в коді: return new ErrorResponse(ErrorResponse::SOME_ENTITY_NOT_FOUND );
  2. Легко змінити повідомлення про помилку Усі повідомлення знаходяться в одному місці замість контролера / служби / тощо або того, що ви розмістили.

Якщо у вас є пропозиції щодо покращення цього, будь ласка, прокоментуйте.

Відповіді:


13

У цій ситуації я завжди спочатку думаю про інтерфейс, а потім пишу PHP-код для його підтримки.

  1. Це REST API, тому значущі коди статусу HTTP є обов'язковими.
  2. Ви хочете, щоб послідовні, гнучкі структури даних надсилалися клієнтові та від нього.

Давайте подумаємо про всі речі, які можуть піти не так, і їх коди HTTP-статусу:

  • Сервер видає помилку (500)
  • Помилка аутентифікації (401)
  • Запитаний ресурс не знайдено (404)
  • Дані, які ви змінюєте, були змінені з моменту завантаження (409)
  • Помилки перевірки під час збереження даних (422)
  • Клієнт перевищив рівень запиту (429)
  • Непідтримуваний тип файлу (415)

Зауважте, є й інші, які ви зможете дослідити пізніше.

У більшості умов відмови є лише одне повідомлення про помилку, яке потрібно повернути. 422 Unprocessable EntityВідповідь, яку я використовував для «помилки перевірки» може повернути більше однієї помилки --- Один або кілька помилок в поле форми.

Для реагування на помилки нам потрібна гнучка структура даних.

Візьмемо як приклад 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!"
        ]
    }
}

На противагу цьому, з простими помилками перевірки при спробі розмістити щось на сервері:

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"
        ]
    }
}

Тут головним є тип вмісту text/json. Це повідомляє клієнтським програмам, що вони можуть декодувати тіло відповіді за допомогою декодера JSON. Якщо, скажімо, внутрішня помилка сервера не виявлена ​​і натомість отримується загальна веб-сторінка "Щось пішло не так", тип вмісту повинен бути text/html; charset=utf-8таким, щоб клієнтські програми не намагалися розшифрувати орган відповіді як JSON.

Це виглядає всіми знахідками та денді, поки вам не потрібно підтримати відповіді JSONP . Ви повинні повернути 200 OKвідповідь, навіть за невдачі. У цьому випадку вам доведеться виявити, що клієнт запитує відповідь JSONP (як правило, виявляючи параметр запиту URL-адреси, який називається callback) і трохи змінити структуру даних:

(GET / posts / 123? Зворотний виклик = 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!"
            ]
        }
    }
});

Тоді обробник відповідей на клієнті (у веб-браузері) повинен мати глобальну функцію JavaScript, яка називається, displayBlogPostяка приймає єдиний аргумент. Ця функція повинна визначати, чи відповідь була успішною:

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

Тож ми подбали про клієнта. Тепер давайте подбаємо про сервер.

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

І використовувати це у випадку помилки сервера:

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

Або під час перевірки введення користувача:

// 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;

Після цього вам просто потрібно щось, що бере об'єкт відповіді, що повертається, і перетворює його в JSON і надсилає відповідь на його веселий шлях.


Дякую за відповідь! Я реалізував подібне рішення. Єдина відмінність у тому, що я не передаю жодних повідомлень сам, вони вже встановлені (див. Моє оновлене запитання).
Гуркіт

-2

Я стикався з чимось подібним, я робив 3 речі,

  1. Створив для себе ExceptionHandler під назвою ABCException.

Оскільки я використовую Java & Spring,

Я визначив це як

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

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

        }
    }

Потім називали його там, де потрібно, наприклад,

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

І так, вам потрібно зробити ExceptionHandler у своєму контролері, якщо ви використовуєте веб-сервіс на базі REST.

Повідомте його, @ExceptionHandlerякщо використовуєте Spring


Програмісти це про концептуальних питань та відповіді на них , як очікується, пояснити речі . Закидання скидів коду замість пояснення - це як копіювання коду з IDE на дошку: це може здатися звичним і навіть іноді бути зрозумілим, але це дивно ... просто дивно. У дошці немає компілятора
gnat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.