插件API开发指南
1. API目录结构
插件的API相关文件通常放在插件目录下,按照功能模块进行组织。
1.1 目录结构示例
addons/
└─diandi_mall/
├─api/ # API控制器目录
│ ├─GoodsController.php # 商品API控制器
│ ├─OrderController.php # 订单API控制器
│ └─SearchController.php # 搜索API控制器
├─config/ # 配置目录
│ └─api.php # API路由配置
├─api.php # API入口文件
└─models/ # 模型目录
├─goods/ # 商品模型
└─order/ # 订单模型
2. API入口文件
2.1 入口文件配置
API入口文件用于注册插件的API路由和配置。
<?php
/**
* API入口文件
*/
return [
'modules' => [
'diandi_mall' => [
'class' => 'addons\diandi_mall\Module',
],
],
];
3. API路由配置
3.1 路由配置文件 (config/api.php)
API路由配置文件用于定义插件的API路由规则。
<?php
/**
* API路由配置
*/
return [
'goods' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/goods'],
'pluralize' => false,
'extraPatterns' => [
'GET list' => 'list',
'GET view' => 'view',
'POST create' => 'create',
'PUT update' => 'update',
'DELETE delete' => 'delete',
],
],
'order' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/order'],
'pluralize' => false,
'extraPatterns' => [
'GET list' => 'list',
'GET view' => 'view',
'POST create' => 'create',
'PUT update' => 'update',
'DELETE delete' => 'delete',
],
],
'search' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/search'],
'pluralize' => false,
'extraPatterns' => [
'GET goods' => 'goods',
'GET advanced' => 'advanced',
],
],
];
3.2 路由配置说明
| 配置项 | 描述 | 必需 |
|---|---|---|
class | 路由规则类,通常使用 yii\rest\UrlRule | 是 |
controller | 控制器映射,格式为 ['插件标识/控制器名称'] | 是 |
pluralize | 是否支持复数形式,建议设置为 false | 是 |
extraPatterns | 额外的路由规则,定义HTTP方法和控制器方法的映射 | 是 |
4. API控制器开发
4.1 基础控制器
API控制器需要继承系统提供的基础API控制器类,以获得统一的功能和特性。
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
// API方法
}
4.2 控制器示例
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use addons\diandi_mall\models\goods\Goods;
use addons\diandi_mall\models\goods\GoodsSearch;
use Yii;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
/**
* 获取商品列表
* @return array
*/
public function actionList()
{
$searchModel = new GoodsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->success([
'list' => $dataProvider->getModels(),
'total' => $dataProvider->getTotalCount(),
'page' => Yii::$app->request->get('page', 1),
'pageSize' => $dataProvider->pagination->pageSize,
]);
}
/**
* 获取商品详情
* @return array
*/
public function actionView()
{
$id = Yii::$app->request->get('id');
$goods = Goods::findOne($id);
if (!$goods) {
return $this->error('商品不存在');
}
return $this->success($goods);
}
/**
* 创建商品
* @return array
*/
public function actionCreate()
{
$goods = new Goods();
$goods->load(Yii::$app->request->post(), '');
if ($goods->save()) {
return $this->success($goods);
} else {
return $this->error('创建失败', $goods->getErrors());
}
}
/**
* 更新商品
* @return array
*/
public function actionUpdate()
{
$id = Yii::$app->request->post('id');
$goods = Goods::findOne($id);
if (!$goods) {
return $this->error('商品不存在');
}
$goods->load(Yii::$app->request->post(), '');
if ($goods->save()) {
return $this->success($goods);
} else {
return $this->error('更新失败', $goods->getErrors());
}
}
/**
* 删除商品
* @return array
*/
public function actionDelete()
{
$id = Yii::$app->request->post('id');
$goods = Goods::findOne($id);
if (!$goods) {
return $this->error('商品不存在');
}
if ($goods->delete()) {
return $this->success('删除成功');
} else {
return $this->error('删除失败');
}
}
}
5. API响应格式
5.1 成功响应
// 成功响应
return $this->success([
'id' => 1,
'name' => '商品名称',
'price' => 99.99,
]);
// 响应格式
{
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
"name": "商品名称",
"price": 99.99
}
}
5.2 错误响应
// 错误响应
return $this->error('操作失败');
// 带错误信息的错误响应
return $this->error('验证失败', $model->getErrors());
// 响应格式
{
"code": 400,
"message": "操作失败",
"data": null
}
// 带错误信息的响应格式
{
"code": 400,
"message": "验证失败",
"data": {
"name": ["商品名称不能为空"],
"price": ["商品价格必须大于0"]
}
}
6. API参数验证
6.1 请求参数验证
使用Yii2的验证机制对API请求参数进行验证。
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use Yii;
use yii\base\Model;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
/**
* 创建商品
* @return array
*/
public function actionCreate()
{
$model = new class extends Model {
public $name;
public $price;
public $stock;
public function rules()
{
return [
[['name', 'price', 'stock'], 'required'],
[['price'], 'number', 'min' => 0],
[['stock'], 'integer', 'min' => 0],
[['name'], 'string', 'max' => 255],
];
}
};
if ($model->load(Yii::$app->request->post(), '') && $model->validate()) {
// 参数验证通过,创建商品
$goods = new Goods();
$goods->name = $model->name;
$goods->price = $model->price;
$goods->stock = $model->stock;
if ($goods->save()) {
return $this->success($goods);
} else {
return $this->error('创建失败', $goods->getErrors());
}
} else {
return $this->error('参数验证失败', $model->getErrors());
}
}
}
7. API权限控制
7.1 权限检查
在API控制器中添加权限检查,确保只有授权用户能够访问API。
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use Yii;
/**
* 订单API控制器
*/
class OrderController extends ApiController
{
/**
* 获取订单列表
* @return array
*/
public function actionList()
{
// 检查用户是否登录
if (!Yii::$app->user->isGuest) {
$userId = Yii::$app->user->id;
// 查询用户的订单
// ...
} else {
return $this->error('请先登录');
}
}
}
8. API性能优化
8.1 缓存使用
使用缓存减少数据库查询,提高API响应速度。
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use addons\diandi_mall\models\goods\Goods;
use Yii;
use yii\caching\FileCache;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
/**
* 获取商品详情
* @return array
*/
public function actionView()
{
$id = Yii::$app->request->get('id');
$cacheKey = 'goods_' . $id;
$cache = new FileCache();
$goods = $cache->get($cacheKey);
if ($goods === false) {
$goods = Goods::findOne($id);
if ($goods) {
// 缓存商品详情,有效期1小时
$cache->set($cacheKey, $goods, 3600);
}
}
if (!$goods) {
return $this->error('商品不存在');
}
return $this->success($goods);
}
}
8.2 查询优化
优化数据库查询,减少不必要的字段和关联查询。
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use addons\diandi_mall\models\goods\Goods;
use Yii;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
/**
* 获取商品列表
* @return array
*/
public function actionList()
{
$query = Goods::find()
->select(['id', 'name', 'price', 'stock', 'status', 'created_at'])
->where(['status' => 1])
->orderBy(['created_at' => SORT_DESC]);
$pagination = new \yii\data\Pagination([
'totalCount' => $query->count(),
'pageSize' => Yii::$app->request->get('pageSize', 10),
'page' => Yii::$app->request->get('page', 0),
]);
$list = $query
->offset($pagination->offset)
->limit($pagination->limit)
->all();
return $this->success([
'list' => $list,
'total' => $pagination->totalCount,
'page' => $pagination->page + 1,
'pageSize' => $pagination->pageSize,
]);
}
}
9. API调试
9.1 调试工具
使用Postman、Insomnia等API调试工具测试API接口。
9.2 日志记录
在API控制器中添加日志记录,便于调试和排查问题。
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use Yii;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
/**
* 获取商品列表
* @return array
*/
public function actionList()
{
// 记录请求参数
Yii::info('Goods list request: ' . json_encode(Yii::$app->request->queryParams), 'api');
// 业务逻辑
// ...
// 记录响应结果
Yii::info('Goods list response: success', 'api');
return $this->success($data);
}
}
10. API版本控制
10.1 版本控制策略
使用URL路径或请求头进行API版本控制。
10.1.1 URL路径版本控制
<?php
/**
* API路由配置
*/
return [
'v1/goods' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/v1-goods'],
'pluralize' => false,
'extraPatterns' => [
'GET list' => 'list',
],
],
'v2/goods' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/v2-goods'],
'pluralize' => false,
'extraPatterns' => [
'GET list' => 'list',
],
],
];
10.1.2 请求头版本控制
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use Yii;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
/**
* 获取商品列表
* @return array
*/
public function actionList()
{
// 获取版本号
$version = Yii::$app->request->headers->get('X-API-Version', 'v1');
switch ($version) {
case 'v1':
// v1版本逻辑
break;
case 'v2':
// v2版本逻辑
break;
default:
return $this->error('不支持的API版本');
}
// 业务逻辑
// ...
}
}
11. 常见问题
11.1 API路由404
可能原因:
- 路由配置错误
- 控制器不存在
- 控制器方法不存在
- 插件未安装
解决方案:
- 检查路由配置是否正确
- 确认控制器文件存在
- 确认控制器方法存在
- 确认插件已正确安装
11.2 API响应500
可能原因:
- 代码语法错误
- 数据库查询错误
- 权限不足
- 依赖服务不可用
解决方案:
- 检查代码语法是否正确
- 检查数据库连接和查询
- 确认用户权限是否足够
- 检查依赖服务是否可用
11.3 API参数验证失败
可能原因:
- 请求参数格式错误
- 必填参数缺失
- 参数类型错误
- 参数值超出范围
解决方案:
- 检查请求参数格式是否正确
- 确保所有必填参数都已提供
- 确保参数类型正确
- 确保参数值在有效范围内
12. 最佳实践
12.1 API设计建议
- RESTful设计:遵循RESTful API设计规范
- 版本控制:实现API版本控制,确保向后兼容
- 参数验证:对所有API请求参数进行严格验证
- 错误处理:统一的错误处理机制,返回清晰的错误信息
- 性能优化:使用缓存、查询优化等手段提高API性能
- 安全性:注意API安全,防止SQL注入、XSS等攻击
- 文档完善:提供详细的API文档,包括参数说明和示例
12.2 API开发建议
- 模块化设计:将API功能拆分为多个模块,便于维护和扩展
- 代码复用:提取公共代码,减少重复代码
- 测试覆盖:为API编写单元测试,确保API功能正确
- 日志记录:完善的日志记录,便于调试和问题排查
- 监控告警:实现API监控和告警,及时发现和解决问题
13. 示例代码
13.1 完整API控制器示例
<?php
namespace addons\diandi_mall\api;
use common\controllers\addons\ApiController;
use addons\diandi_mall\models\goods\Goods;
use addons\diandi_mall\models\goods\GoodsSearch;
use Yii;
use yii\caching\FileCache;
/**
* 商品API控制器
*/
class GoodsController extends ApiController
{
/**
* 获取商品列表
* @return array
*/
public function actionList()
{
$searchModel = new GoodsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->success([
'list' => $dataProvider->getModels(),
'total' => $dataProvider->getTotalCount(),
'page' => Yii::$app->request->get('page', 1),
'pageSize' => $dataProvider->pagination->pageSize,
]);
}
/**
* 获取商品详情
* @return array
*/
public function actionView()
{
$id = Yii::$app->request->get('id');
$cacheKey = 'goods_' . $id;
$cache = new FileCache();
$goods = $cache->get($cacheKey);
if ($goods === false) {
$goods = Goods::findOne($id);
if ($goods) {
// 缓存商品详情,有效期1小时
$cache->set($cacheKey, $goods, 3600);
}
}
if (!$goods) {
return $this->error('商品不存在');
}
return $this->success($goods);
}
/**
* 创建商品
* @return array
*/
public function actionCreate()
{
$goods = new Goods();
$goods->load(Yii::$app->request->post(), '');
if ($goods->save()) {
// 清除相关缓存
$cache = new FileCache();
$cache->delete('goods_list');
return $this->success($goods);
} else {
return $this->error('创建失败', $goods->getErrors());
}
}
/**
* 更新商品
* @return array
*/
public function actionUpdate()
{
$id = Yii::$app->request->post('id');
$goods = Goods::findOne($id);
if (!$goods) {
return $this->error('商品不存在');
}
$goods->load(Yii::$app->request->post(), '');
if ($goods->save()) {
// 清除相关缓存
$cache = new FileCache();
$cache->delete('goods_' . $id);
$cache->delete('goods_list');
return $this->success($goods);
} else {
return $this->error('更新失败', $goods->getErrors());
}
}
/**
* 删除商品
* @return array
*/
public function actionDelete()
{
$id = Yii::$app->request->post('id');
$goods = Goods::findOne($id);
if (!$goods) {
return $this->error('商品不存在');
}
if ($goods->delete()) {
// 清除相关缓存
$cache = new FileCache();
$cache->delete('goods_' . $id);
$cache->delete('goods_list');
return $this->success('删除成功');
} else {
return $this->error('删除失败');
}
}
}
13.2 API路由配置示例
<?php
/**
* API路由配置
*/
return [
'goods' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/goods'],
'pluralize' => false,
'extraPatterns' => [
'GET list' => 'list',
'GET view' => 'view',
'POST create' => 'create',
'PUT update' => 'update',
'DELETE delete' => 'delete',
],
],
'order' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/order'],
'pluralize' => false,
'extraPatterns' => [
'GET list' => 'list',
'GET view' => 'view',
'POST create' => 'create',
'PUT update' => 'update',
'DELETE delete' => 'delete',
],
],
'search' => [
'class' => 'yii\rest\UrlRule',
'controller' => ['diandi_mall/search'],
'pluralize' => false,
'extraPatterns' => [
'GET goods' => 'goods',
'GET advanced' => 'advanced',
],
],
];
