在这种情况下,我总是首先考虑接口,然后编写PHP代码以支持该接口。
- 它是REST API,因此必须具有有意义的HTTP状态代码。
- 您希望与客户端之间发送一致,灵活的数据结构。
让我们考虑所有可能出错的地方及其HTTP状态代码:
- 服务器抛出错误(500)
- 身份验证失败(401)
- 找不到请求的资源(404)
- 自加载数据以来,您正在修改的数据已更改(409)
- 保存数据时的验证错误(422)
- 客户超出了他们的请求率(429)
- 不支持的文件类型(415)
请注意,您还可以在以后进行其他研究。
对于大多数故障情况,仅返回一条错误消息。的422 Unprocessable Entity
我用于“验证错误”响应可能会返回多个错误---每个表单字段一个或多个错误。
我们需要灵活的数据结构来进行错误响应。
以以下示例为例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!"
]
}
}
尝试将某些内容发布到服务器时,将其与简单的验证错误进行对比:
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"
]
}
}
它们的关键是内容类型为text/json
。这告诉客户端应用程序可以使用JSON解码器解码响应主体。例如,如果没有发现内部服务器错误,而是传递了通用的“发生了问题”网页,则内容类型应该为,text/html; charset=utf-8
以便客户端应用程序不会尝试将响应主体解码为JSON。
在您需要支持JSONP响应之前,这看起来都很花哨。200 OK
即使失败,也必须返回响应。在这种情况下,您必须检测到客户端正在请求JSONP响应(通常是通过检测称为callback
)并稍微改变数据结构:
(获取/ 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!"
]
}
}
});
然后,客户端(在Web浏览器中)的响应处理程序应具有一个名为的全局JavaScript函数displayBlogPost
,该函数接受单个参数。此函数必须确定响应是否成功:
function displayBlogPost(response) {
if (response.status == 500) {
alert(response.data.errors.general[0]);
}
}
因此,我们已经照顾了客户。现在,让我们来照顾服务器。
<?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;
}
}
并在发生服务器错误的情况下使用此命令:
try {
// some code that throws an exception
}
catch (Exception $ex) {
return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}
或验证用户输入时:
// 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;
之后,您只需要使用返回的响应对象并将其转换为JSON并以愉快的方式发送响应的对象即可。