FastAdmin admin 模块默认禁止外部路由,CORS 报错是假象
在一个基于 FastAdmin(ThinkPHP 5)搭建的后台管理系统里,我需要给前端 Vue 页面新增几个 API 接口。按照惯例把控制器写进了 application/admin/controller/,本地 SSH 确认文件存在,然后前端一发请求——全部 404。
更迷惑的是,浏览器控制台同时抛出了 CORS error。于是我开始猜测是跨域配置没加对,翻 nginx,加 Access-Control-Allow-Origin,改 preflight 响应……忙活一个多小时,404 纹丝不动。
现象
- 前端
axios.get('/admin/wecom.welcome/getConfig')→ HTTP 404 - 浏览器控制台:
CORS error+preflight 404双报错 curl -i https://domain/admin/wecom.welcome/getConfig返回一段固定文案:"你所浏览的页面暂时无法访问"- 同项目的
/api/index/index→ 200 OK,只有/admin/*路径全挂
用 SSH 进服务器执行:
grep -r "你所浏览的页面暂时无法访问" thinkphp/library/think/exception/
# 命中 RouteNotFoundException.php不是 nginx,不是 FPM,是框架本身在拦截。
根因
application/config.php 里有一行 FastAdmin 默认配置:
'deny_module_list' => ['common', 'admin'],ThinkPHP 5 在分发请求前会调用 App::module(),内部有这段逻辑(thinkphp/library/think/App.php):
if (in_array($module, $config['deny_module_list'])) {
throw new HttpException(404, 'module not exists:' . $module);
}也就是说,admin 模块整体对外 URL 路由被封禁。这是 FastAdmin 的安全默认设计:admin 控制器只允许通过 admin.php 入口 + 后台内部 dispatch 访问,前端通过 index.php 入口打过来的 /admin/* 请求在模块层就被砍掉,连控制器都不会碰到。
CORS 报错是次生问题:preflight 先 404,浏览器误判为跨域失败,在控制台叠加了 CORS 错误——这才是那段时间改 nginx 毫无效果的真正原因。
deny_module_list 在框架入口层运行,比控制器代码早得多。无论你的控制器写得多正确、nginx 配置多完美,只要模块名在黑名单里,请求就直接抛 404。千万不要把 admin 从黑名单删掉——那会把整个后台管理控制器全部对外暴露,彻底绕过登录鉴权。
正解
把供前端调用的 API 控制器迁移到 api 模块,而不是放在 admin 模块。
第一步:迁移目录
application/admin/controller/wecom/Welcome.php
→ application/api/controller/wecom/Welcome.php
第二步:改 namespace 和父类
<?php
namespace app\api\controller\wecom; // namespace 用 api,不是 admin
use app\common\controller\Api; // 父类换成 Api,不是 Backend
class Welcome extends Api
{
protected $noNeedLogin = []; // 需要登录鉴权
protected $noNeedRight = ['*']; // 跳过细粒度权限(按需调整)
public function getConfig()
{
// 业务逻辑
}
}注意 namespace 中子目录 wecom 全小写,这和 Java 包名习惯相反。
第三步:前端 URL 改用 api 模块
// 错误
axios.get('/admin/wecom.welcome/getConfig')
// 正确(第一段是模块名,点号分隔子目录和控制器名)
axios.get('/api/wecom.welcome/getConfig')URL 路径解析规则(TP 5 默认 url_route_on=false):
- 第 1 段
api= 模块 - 第 2 段
wecom.welcome=wecom/Welcome控制器(点号是目录分隔符) - 第 3 段
getConfig= 方法名
凡是需要被前端 SPA 直接请求的接口,统一放 application/api/controller/,继承 app\common\controller\Api。这是 FastAdmin 的标准分层:admin 模块服务于后台自身,api 模块服务于外部调用方。两条路径的鉴权机制不同,不要混用。
遇到请求返回"你所浏览的页面暂时无法访问",先跑一条 grep:
cat application/config.php | grep deny_module_list如果目标模块名在里面,路由问题已定位,不需要再查 nginx 或 CORS。
一句话外卖
FastAdmin 的 CORS 报错很可能是 404 的次生幻象——先确认模块没被
deny_module_list封掉,再查跨域。