Nessa situação, eu sempre penso na interface primeiro e depois escrevo o código PHP para suportá-la.
- É uma API REST, portanto, códigos de status HTTP significativos são uma obrigação.
- Você deseja que estruturas de dados flexíveis e consistentes sejam enviadas para e a partir do cliente.
Vamos pensar em todas as coisas que podem dar errado e em seus códigos de status HTTP:
- O servidor gera um erro (500)
- Falha na autenticação (401)
- O recurso solicitado não foi encontrado (404)
- Os dados que você está modificando foram alterados desde que você o carregou (409)
- Erros de validação ao salvar dados (422)
- O cliente excedeu a taxa de solicitações (429)
- Tipo de arquivo não suportado (415)
Observe que existem outros que você pode pesquisar mais tarde.
Para a maioria das condições de falha, há apenas uma mensagem de erro a ser retornada. o422 Unprocessable Entity
resposta que eu usei para "erros de validação" pode retornar mais de um erro --- Um ou mais erros por campo de formulário.
Precisamos de uma estrutura de dados flexível para respostas a erros.
Tome como exemplo, o 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!"
]
}
}
Compare isso com erros simples de validação ao tentar postar algo no servidor:
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"
]
}
}
A chave aqui é o tipo de conteúdo text/json
. Isso informa aos aplicativos clientes que eles podem decodificar o corpo da resposta com um decodificador JSON. Se, por exemplo, um erro interno do servidor não for detectado e a página da Web genérica "Algo deu errado" for entregue, o tipo de conteúdo deverá ser text/html; charset=utf-8
para que os aplicativos clientes não tentem decodificar o corpo da resposta como JSON.
Parece tudo achado e elegante, até que você precise suportar respostas JSONP . Você deve retornar uma 200 OK
resposta, mesmo para falhas. Nesse caso, você precisará detectar que o cliente está solicitando uma resposta JSONP (geralmente detectando um parâmetro de solicitação de URL chamadocallback
) e alterar um pouco a estrutura de dados:
(GET / posts / 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!"
]
}
}
});
Em seguida, o manipulador de respostas no cliente (em um navegador da web) deve ter uma função JavaScript global chamada displayBlogPost
que aceita um único argumento. Esta função precisaria determinar se a resposta foi bem-sucedida:
function displayBlogPost(response) {
if (response.status == 500) {
alert(response.data.errors.general[0]);
}
}
Então, nós cuidamos do cliente. Agora, vamos cuidar do servidor.
<?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;
}
}
E para usar isso no caso de um erro do servidor:
try {
// some code that throws an exception
}
catch (Exception $ex) {
return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}
Ou ao validar a entrada do usuário:
// 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;
Depois disso, você só precisa de algo que pegue o objeto de resposta retornado e o converta em JSON e envie a resposta de maneira alegre.