En esta situación, siempre pienso primero en la interfaz, luego escribo código PHP para admitirla.
- Es una API REST, por lo que los códigos de estado HTTP significativos son imprescindibles.
- 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 arroja un error (500)
- Fallo 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 excedido 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 se devuelve un mensaje de error. La 422 Unprocessable Entity
respuesta, 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 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!"
]
}
}
Compare eso con simples errores de validación cuando intente enviar 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 el tipo de contenido text/json
. Esto le dice a las aplicaciones cliente que pueden decodificar el cuerpo de respuesta con un decodificador JSON. Si, por ejemplo, no se detecta un error interno del servidor y se entrega su página web genérica "Algo salió mal", el tipo de contenido debería ser text/html; charset=utf-8
para que las aplicaciones cliente no intenten decodificar el cuerpo de respuesta como JSON.
Esto parece todo encontrar y dandy, hasta que necesite admitir respuestas JSONP . Debe devolver una 200 OK
respuesta, incluso para fallas. En este caso, deberá detectar que el cliente solicita 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 único 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 del 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 necesita algo que tome el objeto de respuesta devuelto y lo convierta en JSON y envíe la respuesta de manera feliz.
Estaba enfrentando algo similar, hice 3 cosas,
Como estoy usando Java y Spring,
Lo definí como
Luego lo llamé donde sea necesario, así,
Y sí, necesita hacer un ExceptionHandler en su controlador si está utilizando un servicio web basado en REST.
Anótelo con
@ExceptionHandler
si usa Springfuente