$request->domain() 返回的是 HTTP Host 头解析的域名,与路由绑定无关;需在路由中间件之后通过 $request->routeInfo()['domain'] 或 ['__domain__'] 获取绑定域名。

中间件里用 $request->domain() 拿不到绑定域名?
不是 $request->domain() 不工作,而是它返回的是「当前 HTTP 请求的 Host 头解析出的域名」,和路由绑定的「多应用/多域名配置」完全无关。ThinkPHP 的域名绑定(比如 route_domain 配置或 bind 方法)是在路由调度阶段才生效的,中间件执行时路由还没跑完,$request 里压根没存这个绑定结果。
想拿到实际匹配到的绑定域名,得从路由参数里挖
ThinkPHP 在完成域名绑定后,会把绑定信息写进路由参数(__domain__),但这个参数只在路由匹配成功后才注入——所以必须确保中间件注册在「路由中间件」之后(即 app/middleware.php 中放在 think\middleware\LoadLangPack、think\middleware\CheckRequestCache 等之后,且不能早于 think\middleware\ParseUrl)。否则你连参数都看不到。
- 检查中间件顺序:在
app/middleware.php中确认你的中间件在think\middleware\ParseUrl之后 - 获取方式:
$request->routeInfo()['domain'] ?? $request->routeInfo()['__domain__'] ?? null - 更稳妥写法是先判断是否已路由解析:
if (isset($request->routeInfo()['domain'])) { ... } - 注意:CLI 环境或未命中任何域名绑定规则时,该值为
null或空字符串
$request->host() 和 $request->domain() 的区别别搞混
这两个函数看着像,行为完全不同:
-
$request->host():直接读取$_SERVER['HTTP_HOST'],带端口(如site.test:8080) -
$request->domain():对host()做了处理,去掉端口、转小写、切掉子域名(只留主域+后缀),比如api.site.test→site.test - 两者都不感知 ThinkPHP 的
domain_bind配置,纯靠 HTTP 头,所以不能替代绑定域名逻辑
如果必须在前置中间件(比如鉴权)里用绑定域名
那只能提前手动解析——但代价是重复路由逻辑。不推荐,除非真有强依赖。可行但高风险的做法:
立即学习“PHP免费学习笔记(深入)”;
- 在中间件中手动调用
think\Route::check()(需传入完整请求对象),再从返回的RouteInfo中取domain - 注意:这会触发二次路由匹配,可能影响性能,且若路由规则含闭包或动态条件,结果未必和最终路由一致
- 更稳的方案是把依赖域名的逻辑往后挪:改用「路由中间件」(定义在
route/app.php里,用middleware键),那里$request->routeInfo()已完整可用
真正容易被忽略的是:域名绑定不是请求属性,是路由决策结果。想拿它,就得等路由做完事——急不得,也绕不过去。



















