¿Cuál es la mejor manera de crear un modelo de respuesta de error REST API y un sistema de códigos de error?

15

La implementación de Mi REST devolverá errores en JSON con la siguiente estructura:

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

Sugiero crear un modelo de respuesta especial, donde puedo pasar los valores necesarios para las propiedades (dev_message, message_for_user, some_internal_error_code) y devolverlos. En código sería similar a esto:

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

¿Cómo debería ser este modelo? ¿Debo implementar métodos por ej. successResponse () donde pasaré solo información de texto y el código será 200 por defecto allí? Estoy atascado con esto. Y esta es la primera parte de mi pregunta: ¿necesito implementar este modelo? ¿Es esta buena práctica? Porque por ahora, solo estoy devolviendo matrices directamente desde el código.

La segunda parte es sobre el sistema de código de error. Los códigos de error serán descritos en la documentación. Pero el problema que estoy encontrando está en el código. ¿Cuál es la mejor manera de gestionar los códigos de error? ¿Debo escribirlas dentro del modelo? ¿O sería mejor crear un servicio separado para manejar esto?

ACTUALIZACIÓN 1

He implementado la clase de modelo para la respuesta. Es similar a la respuesta de Greg, la misma lógica, pero adicionalmente he escrito hardcoded errores escritos en el modelo y aquí es cómo se ve:

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

     ...some code...
    }

¿Por qué hice esto? ¿Y para qué?

  1. Se ve bien en el código: %código%
  2. Mensaje de error fácil de cambiar. Todos los mensajes están en un lugar en lugar de controlador / servicio / etc o lo que sea que haya colocado.

Si tienes alguna sugerencia para mejorar esto, por favor, comenta.

    
pregunta Grokking 16.01.2015 - 10:41

2 respuestas

13

En esta situación, siempre pienso primero en la interfaz, luego escribo el código PHP para admitirlo.

  1. Es una API REST, por lo que los códigos de estado HTTP significativos son obligatorios.
  2. Desea que se envíen estructuras de datos consistentes y flexibles hacia y desde el cliente.

Pensemos en todas las cosas que podrían salir mal y sus códigos de estado HTTP:

  • El servidor lanza un error (500)
  • Error de autenticación (401)
  • No se encontró el recurso solicitado (404)
  • Los datos que está modificando han cambiado desde que los cargó (409)
  • Errores de validación al guardar datos (422)
  • El cliente ha superado su tasa de solicitud (429)
  • Tipo de archivo no compatible (415)

Tenga en cuenta que hay otros que puede investigar más adelante.

Para la mayoría de las condiciones de falla, solo hay un mensaje de error que devolver. La respuesta 422 Unprocessable Entity , que he usado para "errores de validación" podría devolver más de un error --- Uno o más errores por campo de formulario.

Necesitamos una estructura de datos flexible para las respuestas de error.

Tomemos como ejemplo, el 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!"
        ]
    }
}

Contraste eso con errores de validación simples al intentar enviar un POST algo al 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"
        ]
    }
}

La clave aquí es que el tipo de contenido es text/json . Esto le dice a las aplicaciones cliente que pueden decodificar el cuerpo de la respuesta con un decodificador JSON. Si, por ejemplo, no se detecta un error interno del servidor y su página web genérica "Algo salió mal" se entrega, el tipo de contenido debería ser text/html; charset=utf-8 para que las aplicaciones cliente no descodifiquen el cuerpo de la respuesta como JSON. / p>

Esto se ve todo a encontrar y excelente, hasta que necesite admitir las respuestas JSONP . Debe devolver una respuesta 200 OK , incluso para las fallas. En este caso, tendrá que detectar que el cliente está solicitando una respuesta JSONP (generalmente detectando un parámetro de solicitud de URL llamado callback ) y cambiar un poco la estructura de datos:

(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!"
            ]
        }
    }
});

Luego, el controlador de respuestas en el cliente (en un navegador web) debe tener una función global de JavaScript llamada displayBlogPost que acepte un solo argumento. Esta función debería determinar si la respuesta fue exitosa:

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

Así que nos hemos ocupado del cliente. Ahora, cuidemos el 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;
    }
}

Y para usar esto en el caso de un error de servidor:

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

O al validar la entrada del usuario:

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

Después de eso, solo necesitas algo que tome el objeto de respuesta devuelto, lo convierte en JSON y envía la respuesta de forma alegre.

    
respondido por el Greg Burghardt 16.01.2015 - 19:13
-2

Estaba enfrentando algo similar, hice 3 cosas,

  1. Creé un ExceptionHandler para mí llamado ABCException.

Ya que estoy usando Java & Primavera,

Lo definí como

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

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

        }
    }

Luego lo llamó donde sea necesario, como este,

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

Y sí, debes hacer un ExceptionHandler en tu controlador si estás usando un servicio web basado en REST.

Anótelo con @ExceptionHandler si usa Spring

    
respondido por el Dave Ranjan 16.01.2015 - 11:47

Lea otras preguntas en las etiquetas