Qual é a melhor maneira de criar o modelo de resposta a erros da API REST e o sistema de códigos de erro?

15

Minha implementação REST retornará erros em JSON com a próxima estrutura:

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

Sugiro criar um modelo de resposta especial, no qual posso transmitir os valores necessários para propriedades (dev_message, message_for_user, some_internal_error_code) e retorná-los. No código, seria semelhante a isto:

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

Como este modelo deve ser parecido? Devo implementar métodos, e. successResponse () onde vou passar apenas informação de texto, e o código será o padrão 200 aí? Estou preso com isso. E esta é a primeira parte da minha pergunta: eu preciso implementar esse modelo, essa é uma boa prática? Porque, por enquanto, estou apenas retornando matrizes diretamente do código.

A segunda parte é sobre o sistema de código de erro. Códigos de erro serão descritos na documentação. Mas o problema que estou encontrando está no código. Qual é a melhor maneira de gerenciar códigos de erro? Devo escrevê-los dentro do modelo? Ou seria melhor criar um serviço separado para lidar com isso?

UPDATE 1

Eu implementei uma classe de modelo para resposta. É semelhante a resposta de Greg, a mesma lógica, mas adicionalmente eu tenho codificado erros escritos no modelo e aqui está como é:

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

     ...some code...
    }

Por que fiz isso? E para quê?

  1. Parece legal no código: %código%
  2. Fácil de alterar a mensagem de erro. Todas as mensagens estão em um só lugar ao invés de controller / service / etc ou o que quer que você tenha colocado.

Se você tiver alguma sugestão para melhorar isso, por favor, comente.

    
por Grokking 16.01.2015 / 10:41
fonte

2 respostas

13

Nesta situação, eu sempre penso na interface primeiro, depois escrevo código PHP para suportá-la.

  1. É uma API REST, portanto códigos de status HTTP significativos são obrigatórios.
  2. Você deseja que estruturas de dados consistentes e flexíveis sejam enviadas de e para o cliente.

Vamos pensar em todas as coisas que poderiam dar errado e nos códigos de status HTTP deles:

  • O servidor gera um erro (500)
  • Falha de autenticação (401)
  • O recurso solicitado não foi encontrado (404)
  • Os dados que você está modificando foram alterados desde que você os 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)

Note que há outras que você pode pesquisar mais tarde.

Para a maioria das condições de falha, há apenas uma mensagem de erro a ser retornada. A resposta 422 Unprocessable Entity , que usei para "erros de validação", poderia retornar mais de um erro --- um ou mais erros por campo de formulário.

Precisamos de uma estrutura de dados flexível para respostas de 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 de validação simples ao tentar enviar algo para o 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 sendo text/json . Isso informa aos aplicativos cliente que eles podem decodificar o corpo da resposta com um decodificador JSON. Se, digamos, um erro interno do servidor não for detectado e sua 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. / p>

Isso tudo parece fácil, até que você precise dar suporte a respostas JSONP . Você deve retornar uma resposta 200 OK , mesmo para falhas. Nesse caso, você terá que detectar que o cliente está solicitando uma resposta JSONP (geralmente detectando um parâmetro de solicitação de URL chamado callback ) e alterando um pouco a estrutura de dados:

(GET / posts / 123? retorno de chamada = 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 resposta no cliente (em um navegador da Web) deve ter uma função JavaScript global chamada displayBlogPost , que aceita um único argumento. Esta função teria que determinar se a resposta foi bem sucedida:

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

Então, 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 no 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 receba o objeto de resposta retornado, converta-o em JSON e envie a resposta de maneira feliz.

    
por 16.01.2015 / 19:13
fonte
-2

Eu estava enfrentando algo parecido, fiz 3 coisas,

  1. Criei um ExceptionHandler para mim chamado ABCException.

Desde que eu estou usando o Java & Primavera,

Eu defini como

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

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

        }
    }

Em seguida, ligue para onde for necessário, assim,

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

E sim, você precisa criar um ExceptionHandler no seu controlador se estiver usando o serviço da Web baseado em REST.

Anote com @ExceptionHandler se estiver usando o Spring

    
por 16.01.2015 / 11:47
fonte

Tags