In dieser Situation denke ich immer zuerst an die Schnittstelle und schreibe dann PHP-Code, um sie zu unterstützen.
- Es ist eine REST-API, daher sind aussagekräftige HTTP-Statuscodes ein Muss.
- Sie möchten konsistente, flexible Datenstrukturen zum und vom Client senden.
Denken wir an all die Dinge, die schief gehen könnten, und an ihre HTTP-Statuscodes:
- Der Server gibt einen Fehler aus (500)
- Authentifizierungsfehler (401)
- Die angeforderte Ressource wurde nicht gefunden (404)
- Die Daten, die Sie ändern, wurden geändert, seit Sie sie geladen haben (409)
- Validierungsfehler beim Speichern von Daten (422)
- Der Kunde hat seine Anfragerate überschritten (429)
- Nicht unterstützter Dateityp (415)
Beachten Sie, dass es andere gibt, die Sie später recherchieren können.
Für die meisten Fehlerzustände gibt es nur eine Fehlermeldung, die zurückgegeben werden muss. Die 422 Unprocessable Entity
Antwort, die ich für "Validierungsfehler" verwendet habe, kann mehr als einen Fehler zurückgeben - einen oder mehrere Fehler pro Formularfeld.
Wir brauchen eine flexible Datenstruktur für Fehlerreaktionen.
Nehmen Sie als Beispiel 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!"
]
}
}
Vergleichen Sie dies mit einfachen Überprüfungsfehlern, wenn Sie versuchen, etwas auf dem Server zu veröffentlichen:
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"
]
}
}
Sie geben hier den Inhaltstyp an text/json
. Dies teilt Clientanwendungen mit, dass sie den Antworttext mit einem JSON-Decoder decodieren können. Wenn beispielsweise ein interner Serverfehler nicht abgefangen wird und stattdessen Ihre generische Webseite "Something went wrong" ausgeliefert wird, sollte der Inhaltstyp so sein, text/html; charset=utf-8
dass Clientanwendungen nicht versuchen, den Antworttext als JSON zu dekodieren.
Das sieht alles find and dandy aus, bis Sie JSONP- Antworten unterstützen müssen. Sie müssen eine 200 OK
Antwort zurückgeben, auch bei Fehlern. In diesem Fall müssen Sie feststellen, dass der Client eine JSONP-Antwort anfordert (normalerweise durch Erkennen eines aufgerufenen URL-Anforderungsparameters callback
) und die Datenstruktur ein wenig ändern:
(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!"
]
}
}
});
Dann sollte der Response-Handler auf dem Client (in einem Webbrowser) eine globale JavaScript-Funktion haben, displayBlogPost
die ein einzelnes Argument akzeptiert. Diese Funktion müsste feststellen, ob die Antwort erfolgreich war:
function displayBlogPost(response) {
if (response.status == 500) {
alert(response.data.errors.general[0]);
}
}
Also haben wir uns um den Kunden gekümmert. Nun kümmern wir uns um den Server.
<?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;
}
}
Und um dies im Falle eines Serverfehlers zu verwenden:
try {
// some code that throws an exception
}
catch (Exception $ex) {
return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}
Oder beim Validieren von Benutzereingaben:
// 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;
Danach brauchen Sie nur noch etwas, das das zurückgegebene Antwortobjekt in JSON konvertiert und die Antwort auf fröhliche Weise sendet.