Yii2实现RESTful架构配置最佳实践
为什么要用RESTful API
在服务器端,应用程序状态和功能可以分为各种资源。资源是一个有趣的概念实体,它向客户端公开。资源的例子有:应用程序对象、数据库记录、算法等等。每个资源都使用 URI (Universal Resource Identifier) 得到一个唯一的地址。所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、PUT、POST 和 DELETE。Hypermedia 是应用程序状态的引擎,资源表示通过超链接互联。
无状态,分层,可扩展
本文为原创博客,被收录在我自己的GitHub项目中点我查看项目文件
基于Yii2的RESTful API 的实现
不用自带的REST实现方式
首先,Yii2自带了实现RESTful api的方式,但是,官方的例子过于简单,把一个资源限定成了数据库中的一个表,这显然是和REST中定义的资源不相符的,并且实际的业务需求过于复杂,不能通过一个表进行数据的操作。
所以,我们采用了另一种方式,通过使用Yii2的不同组件,完成RESTful API的构建
路由
REST要求定义资源,采用不同HTTP方式进行访问。
这里用到了框架内部的路由
在项目的配置文件/config/web.php
,中,对不同资源进行路由设置,从而达到同一个URL用不同的访问方式来处理不同业务的目的。
//url配置对应规则
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
//配置规则,实现RESTful的方式
'POST accounts'=>'account/login', // 登录
'GET accounts'=>'account/get-avatar', //获取头像
"GET accounts/status"=>'account/status', //查询登录状态
//获取用户组信息
"GET groups"=>"account/profile",
//用户申请加入用户组的接口
"GET users/ask"=>"user/apply", //申请列表
"PUT users/<uid:\d+>"=>"user/pass", //通过
"PATCH users/<uid:\d+>"=>"user/nopass", //不通过
"GET users"=>"user/list", //通过的用户列表
"DELETE users/<uid:\d+>"=>"user/remove", //移除现有的用户
"GET users/<uid:\d+>"=>"user/info", //网红的具体信息
"GET users/serach"=>"user/serach", //搜索当前用户的用户
//消息中心相关
"GET notices"=>"notice/list", //消息列表
"PUT notices/<nid:\d+>"=>"notice/mark", //消息标记为已读
"GET notices/status"=>"notice/status", //获取消息的概要
],
这部分的设计,参考官方文档对于RESTful实现的部分
- GET /users: 逐页列出所有用户
- HEAD /users: 显示用户列表的概要信息
- POST /users: 创建一个新用户
- GET /users/123: 返回用户 123 的详细信息
- HEAD /users/123: 显示用户 123 的概述信息
- PATCH /users/123 and PUT /users/123: 更新用户123
- DELETE /users/123: 删除用户123
- OPTIONS /users: 显示关于末端 /users 支持的动词
- OPTIONS /users/123: 显示有关末端 /users/123 支持的动词
接收数据
我们需要获取客户端传递过来的GET
,POST
,Header
的数据,框架自带了Yii::$app->request
,可以直接得到客户端传过来的数据。
参考\yii\web\Request|\yii\console\Request $request The request component. This property is read-only.
处理数据
这部分和一般的项目一样,都用/models
中的数据表对应的Model文件,继承了\yii\db\ActiveRecord
的类,实现对数据表的CURD操作。
参考URL: http://www.yiichina.com/doc/guide/2.0/db-active-record
响应数据
我们在处理数据之后,需要返回给客户端对应的数据,在REST的设计规则里是这样处理的
- Body只返回主要的数据,比如用户列表,用户的详细数据
- Header返回其他的信息,包括页码信息,身份校验信息
- 完全的使用HTTP状态码作为资源被请求状态的返回,比如404,403
HTTP状态码的说明如下
Code | HTTP Operation | Body Contents | Description |
---|---|---|---|
200 | GET,PUT | 资源 | 操作成功 |
201 | POST | 资源,元数据 | 对象创建成功 |
202 | POST,PUT,DELETE,PATCH | N/A | 请求已经被接受 |
204 | DELETE,PUT,PATCH | N/A | 操作已经执行成功,但是没有返回数据 |
301 | GET | link | 资源已被移除 |
303 | GET | link | 重定向 |
304 | GET | N/A | 资源没有被修改 |
400 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 参数列表错误(缺少,格式不匹配) |
401 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 未授权 |
403 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 访问受限,授权过期 |
404 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 资源,服务未找到 |
405 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 不允许的http方法 |
409 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 资源冲突,或者资源被锁定 |
415 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 不支持的数据(媒体)类型 |
429 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 请求过多被限制 |
500 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 系统内部错误 |
501 | GET,PSOT,PUT,DELETE,PATCH | 错误提示(消息) | 接口未实现 |
格式化数据,我们的返回数据的格式为JSON
$response= Yii::$app->response;
$response->format = \yii\web\Response::FORMAT_JSON; //返回json
错误处理
通过不同的错误码,返回不同的HTTP状态码。
由于的非常频繁的调用,所以对数据进行了封装。
错误处理类Error.php
/**
* 错误处理类
*
* ------------------------------------
*
* 错误处理扩展 seaslog
*
* http://www.oschina.net/news/50333/seaslog-0-21
*
* http://neeke.github.io/SeasLog/
*
* ------------------------------------
*
* @author Calvin
* @version 1.0
* @copyright (c) 2016,
*/
namespace app\components\error;
use Yii;
class Error {
/**
* 公共的错误返回处理,通过传入参数,返回对应的错误代码,这里也定义了错误返回的格式,以及对日志的记录
*
* @param $error_code
* @return array
*/
public static function errorJson($error_code) {
$requests = Yii::$app->request; //返回值
$post = json_encode($requests->post());
$get = json_encode($requests->get());
$headers = json_encode($requests->getHeaders()->toArray());
//带入记录错误代码的文件
$error_file = require_once \Yii::$app->basePath . "/components/error/ErrorCode.php";
//获取http状态码,以及文字说明
$error_info = $error_file["$error_code"];
$http_code = $error_info['http_code'];
$error_text = $error_info['remark'];
$error_body = [ //设置返回的格式
'request' => $requests->getUrl(),
'method'=>$requests->getMethod(),
'error_code' => $error_code,
'error' =>$error_text,
];
$response = Yii::$app->response;
$response->statusCode=$http_code;
$response->format = \yii\web\Response::FORMAT_JSON;
$headers = Yii::$app->response->headers;
$headers->add('X-Halo-Result', 0);
// $response->data = $error_body;
// $userIP = $requests->userIP;
//写入日志
// \SeasLog::error('error === {error} && error_text === {error_text} && userIP === {userIP} && post === {post} && get === {get} && header === {header} ', [
// '{error}' => $error,
// '{error_text}' => $error_file["$error"],
// '{post}' => $post,
// '{get}' => $get,
// '{userIP}' => $userIP,
// '{header}' => $headers,
// ], $request);
// $appLog = \Alibaba::AppLog();
// $appLog->debug("debug-log-emssage");
// $appLog->info("info-log-emssage");
// $appLog->warn("warn-log-emssage");
// $appLog->error("error-log-emssage");
return $error_body;
}
}
错误代码文件ErrorCode.php
/**
* 增加对不同类型的HTTP状态码的返回
*
* 错误码,HTTP状态码,返回值
*/
return [
'1001'=>[
'http_code'=>403,
'remark'=>"Token验证失败",
],
'1002'=>[
'http_code'=>400,
'remark'=>"参数错误",
],
'1003'=>[
'http_code'=>400,
'remark'=>"参数不全",
],
'2001'=>[
'http_code'=>404,
'remark'=>"用户不存在",
],
];
调用错误数据返回
return Error::errorJson(1002);
文档
有句话说得好,程序员不愿意写文档,但是看到没有文档的项目又会抓狂。。。。
概括结构
一个合格的API文档应该包含下面几项
- 概括说明
- 加密协议
- 数据类型
- 错误处理
- 接口文档
- 参考资料
接口文档
接口文档告诉客户端,调用什么数据,怎么掉,异常了咋办。。。
- 简单说明
- 访问地址
- 请求方式
- 返回结果
- 返回结果字段说明
- 错误代码
- 更新记录
总结
RESTful API的好处在于更简洁的规范了数据请求的方式,通过资源来设计数据接口,方便客户端的调用,减少沟通成本。
不过协议毕竟只是个建议,我们可以根据自己项目的实际情况,有选择的满足协议的需求,更好的为自己的项目服务。
:)