У цій ситуації я завжди спочатку думаю про інтерфейс, а потім пишу PHP-код для його підтримки.
- Це REST API, тому значущі коди статусу HTTP є обов'язковими.
- Ви хочете, щоб послідовні, гнучкі структури даних надсилалися клієнтові та від нього.
Давайте подумаємо про всі речі, які можуть піти не так, і їх коди 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 і надсилає відповідь на його веселий шлях.