ThinkPHP6/5.x对接Vue/React需解决路由、CORS、验证码三冲突:关闭强制路由、域名分组API、限定HTML后缀fallback;CORS须配置具体域名并响应OPTIONS;验证码改用Redis+captcha_id,登录态切换JWT;接口必须显式return json();Nginx需补PATH_INFO参数。

老项目还在用ThinkPHP6或5.x,现在要对接Vue/React前端做前后端分离,接口一调就404、跨域报错、验证码总失败、Session拿不到数据——不是代码写错了,是旧框架默认行为和新架构存在三处隐性冲突:路由兜底逻辑吞掉/api请求、CORS预检被静默丢弃、验证码绑定在失效的跨域Session上。
路由隔离:让前端页面和API各走各的路
第一步:关闭强制路由,否则未定义路径直接返回404,连/api/login都进不了控制器。【'url_route_must' => false】必须写在config/route.php顶部,不能只改中间件或控制器。
第二步:显式声明API入口,别用Route::rule('[:path]', 'index/index')这种无差别兜底——它会把/api/v1/user也重定向到前端控制器,后端根本没执行。正确写法是用域名分组:Route::domain('api', function () { Route::group('v1', function () { Route::get('user', 'api/v1/User/index'); }); });
第三步:非API请求统一fallback到前端入口,但加->ext('html')限定后缀,避免匹配到/api/login.json这类真实接口路径。写成Route::rule('[:path]', 'index/index')->ext('html');就行。
立即学习“PHP免费学习笔记(深入)”;
CORS配置:预检请求必须被明确响应
方法一:用官方插件(推荐)
执行composer require topthink/think-cors,安装后检查app/middleware.php是否已注册\think\middleware\Cors::class。注意:若前端带Cookie或Authorization头,config/cors.php里'allowed_origins'必须填具体域名数组,比如['http://localhost:5173', 'https://admin.example.com'],【绝不能写['*']】,否则浏览器直接拒绝。
方法二:手写中间件(调试用)
新建app/middleware/CorsMiddleware.php,核心逻辑是对所有请求设置响应头:$response->header('Access-Control-Allow-Origin', $origin)->header('Access-Control-Allow-Credentials', 'true')->header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS')->header('Access-Control-Allow-Headers', 'Content-Type,Authorization,X-Requested-With');。对OPTIONS请求直接返回204状态码,不要exit或die。
验证码与登录态:放弃跨域不可靠的Session
老项目验证码校验依赖$this->session->get('captcha.key'),但跨域下浏览器不自动发Cookie,Session ID断链,永远返回null。必须切换为可传递的唯一标识。
生成验证码时,后端返回json(['captcha_id' => 'abc123', 'image' => 'data:image/png;base64,...']),前端把captcha_id存入表单hidden字段;校验时,接口接收captcha_id和用户输入code,从Redis查captcha:abc123比对,查完立刻DEL captcha:abc123防重放。
登录态改用JWT:登录成功后返回token,前端后续请求在Header加Authorization: Bearer xxx,后端用$request->header('authorization')取值,完全绕过Session依赖。
接口输出:确保返回纯JSON不掺HTML
控制器方法末尾必须显式return json($data),不能只写$this->success(...)或依赖default_return_type配置。ThinkPHP8默认仍可能渲染模板,尤其当控制器继承了自定义基类且该基类调用了$this->fetch()时,JSON会被HTML包裹。
检查app/controller目录下所有控制器,确认没调用fetch、display、view等视图方法。如果用了自定义基类,把它删掉或重构,让控制器直接继承\think\Controller即可。
Nginx转发:PATH_INFO缺失导致全站404
本地Apache能跑,上线Nginx全404,大概率是转发规则漏了fastcgi_param PATH_INFO $fastcgi_path_info;。这个参数决定ThinkPHP能否解析路由,缺了就无法识别/api/v1/user中的v1/user部分。
完整Nginx配置片段必须包含:location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_pass php-fpm; }



















