FastAdmin 新 Controller curl 200 但浏览器报网络错误的真相
在某后台管理系统里新建了一个 PHP 接口模块。后端 curl 测试全部 200,PHP 日志干净,服务器毫无异样。前端却一直弹「网络请求失败」,Chrome Network 面板显示请求已发出但响应被浏览器静默丢弃。
排查了半小时,以为是 nginx 配置问题、以为是前端 axios 拦截器的锅,直到看了 Response Headers——Access-Control-Allow-Origin 根本不存在。
现象
- 浏览器发出 CORS 预检(OPTIONS),返回头里没有任何
Access-Control-Allow-* - 正式请求(POST)服务器返回 200,但浏览器 CORS 校验失败,前端拿不到响应体
- 用
curl直接打同一接口,完全正常,数据全回来 - 同项目其他已有接口在浏览器里运行良好
- 新接口没有任何认证拦截,无 token 也能通过
根因
FastAdmin 基于 ThinkPHP 5,API 模块有一个专用基类 app\common\controller\Api,它的 _initialize() 会在每次请求进来时做四件事:
protected function _initialize()
{
check_cors_request(); // ① 注入 CORS 响应头
check_ip_allowed(); // ② IP 白名单
$this->request->filter('trim,strip_tags,htmlspecialchars'); // ③ 输入过滤
$this->auth = Auth::instance(); // ④ Token 鉴权
// ... 提取 token、校验登录状态 ...
}check_cors_request() 读取 fastadmin.cors_request_domain 配置,把允许的域写入 Access-Control-Allow-Origin 等响应头。没有这一步,浏览器的同源策略就会无情拦截响应。
而新建 controller 时,手写了:
use think\Controller;
class MyController extends Controller { ... }think\Controller 是框架的通用基类,没有任何上述初始化逻辑。四件事全部缺席:没有 CORS、没有认证、没有过滤。
curl 不执行 CORS 策略,所以用 curl 测试永远成功。浏览器则严格执行,响应头里缺 Access-Control-Allow-Origin 就丢弃响应。这让 bug 看起来像「前端问题」,实际是后端忘了继承正确基类。
正解
FastAdmin API 模块下的所有 controller,一律继承 app\common\controller\Api,绝不继承 think\Controller。
<?php
namespace app\api\controller;
// 错误 ❌
use think\Controller;
class MyController extends Controller { ... }
// 正确 ✅
use app\common\controller\Api;
class MyController extends Api
{
protected $noNeedLogin = []; // 所有方法均需登录
protected $noNeedRight = ['*']; // 跳过细粒度权限(登录即可)
public function index()
{
// 业务逻辑
}
}只需把 extends Controller 改成 extends Api,_initialize() 自动注入 CORS 头、鉴权逻辑和输入过滤,无需手写任何 CORS 中间件。
附加坑:富文本字段被 strip_tags 吃掉
Api 基类默认开启 strip_tags 过滤,如果接口要接收富文本编辑器的 HTML 内容,所有标签会被剥光。解法是重写 _initialize(),先调用 parent::_initialize() 保留 CORS 和鉴权,再覆盖 filter:
protected function _initialize()
{
parent::_initialize(); // 保留 CORS + 鉴权
$this->request->filter('trim'); // 去掉 strip_tags,只保留 trim
}
// 然后对富文本字段单独做白名单过滤
if (isset($data['content'])) {
$data['content'] = strip_tags($data['content'],
'<p><br><img><strong><em><ul><ol><li><a><h1><h2><h3>');
}用一条命令扫出所有「裸继承」的 controller,有则修,保持 API 目录全部走 Api 基类:
grep -l 'extends Controller' application/api/controller/*.php
# 结果为空则安全一句话外卖
FastAdmin / ThinkPHP 5 的 API 模块里,
extends think\Controller是一颗无声地雷——CORS 头、鉴权、输入过滤全部缺席,而curl测试永远感知不到它的存在。