Dans cette situation, je pense toujours à l'interface d'abord, puis j'écris du code PHP pour la supporter.
- Il s'agit d'une API REST, des codes d'état HTTP significatifs sont donc indispensables.
- Vous souhaitez que des structures de données cohérentes et flexibles soient envoyées vers et depuis le client.
Pensons à toutes les choses qui pourraient mal tourner et à leurs codes d'état HTTP:
- Le serveur renvoie une erreur (500)
- Échec d'authentification (401)
- La ressource demandée est introuvable (404)
- Les données que vous modifiez ont été modifiées depuis que vous les avez chargées (409)
- Erreurs de validation lors de l'enregistrement des données (422)
- Le client a dépassé son taux de demande (429)
- Type de fichier non pris en charge (415)
Notez qu'il y en a d'autres que vous pouvez rechercher plus tard.
Pour la plupart des conditions d'échec, un seul message d'erreur doit être renvoyé. La 422 Unprocessable Entity
réponse, que j'ai utilisée pour les "erreurs de validation" pourrait renvoyer plus d'une erreur --- Une ou plusieurs erreurs par champ de formulaire.
Nous avons besoin d'une structure de données flexible pour les réponses aux erreurs.
Prenons comme exemple 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!"
]
}
}
Comparez cela avec de simples erreurs de validation lorsque vous essayez de POSER quelque chose au serveur:
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 clé ici est le type de contenu text/json
. Cela indique aux applications clientes qu'elles peuvent décoder le corps de réponse avec un décodeur JSON. Si, par exemple, une erreur de serveur interne n'est pas détectée et que votre page Web générique «Quelque chose s'est mal passé» est livrée à la place, le type de contenu doit être text/html; charset=utf-8
tel que les applications clientes n'essaieront pas de décoder le corps de réponse en JSON.
Cela semble tout trouver et dandy, jusqu'à ce que vous ayez besoin de prendre en charge JSONP réponses . Vous devez renvoyer une 200 OK
réponse, même en cas d'échecs. Dans ce cas, vous devrez détecter que le client demande une réponse JSONP (généralement en détectant un paramètre de demande d'URL appelé callback
) et modifier un peu la structure des données:
(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!"
]
}
}
});
Ensuite, le gestionnaire de réponse sur le client (dans un navigateur Web) doit avoir une fonction JavaScript globale appelée displayBlogPost
qui accepte un seul argument. Cette fonction devrait déterminer si la réponse a réussi:
function displayBlogPost(response) {
if (response.status == 500) {
alert(response.data.errors.general[0]);
}
}
Nous avons donc pris soin du client. Maintenant, prenons soin du serveur.
<?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;
}
}
Et pour l'utiliser en cas d'erreur de serveur:
try {
// some code that throws an exception
}
catch (Exception $ex) {
return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}
Ou lors de la validation de la saisie utilisateur:
// 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;
Après cela, vous avez juste besoin de quelque chose qui prend l'objet de réponse retourné et le convertit en JSON et envoie la réponse sur son chemin joyeux.