控制器与路由
2026/10/26大约 2 分钟
控制器与路由
插件业务接口统一走注解路由:类级 #[RouteGroup] 声明前缀,方法级 #[Get]/#[Post] 声明子路径,前缀自动拼接。
典型控制器
namespace plugin\am_demo\app\controller;
use support\Request;
use support\Response;
use support\annotation\route\Get;
use support\annotation\route\RouteGroup;
#[RouteGroup('/app/am_demo')]
class AppController
{
// 免登录白名单:写在此列表的 action 绕过基座 AuthCheck
protected array $noNeedLogin = ['index', 'manage', 'publicPosts'];
#[Get('/info')]
public function info(): Response
{
return json(['code' => 200, 'message' => 'ok', 'data' => ['name' => 'am_demo']]);
}
#[Get('/public/posts')]
public function publicPosts(): Response { /* ... */ }
}- 上面的
info实际访问路径是GET /app/am_demo/info(前缀 + 子路径)。 - 业务接口不需要在
route.php重复注册;只有页面入口(/app/{name}、/app/{name}/manage这类无尾斜杠路径)才在config/route.php手动Route::get。
鉴权三姿势
基座 AuthCheck 只校验 JWT 是否有效,不区分管理员/会员。每个 action 的鉴权姿势:
| 姿势 | 做法 |
|---|---|
| 公开 | action 写入 $noNeedLogin |
| 必登录(前台混合控制器) | 写入 $noNeedLogin 绕过 AuthCheck,方法首行 if ($resp = $this->requireMember($request)) return $resp;(详见会员态鉴权) |
| 后台管理 | 不写白名单,由 AuthCheck 拦截,配合 #[Permission] 做 RBAC |
基座控制器 helper
继承 app\basic\BaseController 可用一批通用方法:
| 方法 | 作用 |
|---|---|
$this->userInfo | 当前登录主体(来自 JWT extend) |
validate($scene, $data) | 调用注入的校验器场景 |
search($model, $where) | 按搜索器组装 withSearch |
getList($model) | 分页/排序/数据权限统一处理 |
returnListData($list) | 统一列表响应体 |
transaction($closure) | 事务包装 |
assertCanManage($level) / assertRowVisible() | 行政级别与行级数据权限校验 |
响应体
统一结构由全局 helper 生成(app/functions.php 的 ret()),键名固定为 {code, msg, total, data, page}:
return success('操作成功', $data, $total, $page); // code 200
return warning('参数错误'); // code 400
return error('服务器异常'); // code 500键名是 msg 不是 message
基座前端 utils/http 的响应拦截器读的是 response.data.msg,并只按 code(200 成功、401 未授权、403 禁止)分流。自己 json([...]) 手写时务必用 msg。示例插件 am_demo 的 info/publicPosts 图省事写成了 'message' => 'ok',属不规范偏离——因 code=200 会被拦截器短路放行才没出问题,但提示文案取不到。新代码请走 success()/warning()/error()。
结构与前缀全表见响应与接口前缀。
