MVC框架路由匹配异常:请求URL无法匹配正确的user路由
路由匹配异常问题
我在自己的MVC框架里定义了两条路由:
$routes["\/"] = $index; $routes["\/user\/(\d+)"] = $user;
通过$request_url = $_SERVER["REQUEST_URI"];获取请求URL,再用以下代码做正则匹配:
$reg_ex = "/$route/i"; $matches = []; preg_match($reg_ex, $request_url, $matches);
但出现匹配异常:当请求URL为/MVC-Framework/user/1时,只会匹配到/路由,无法匹配正确的user路由。
完整代码
$request_url = $_SERVER["REQUEST_URI"]; echo $request_url . "<br />"; $index = function(){ echo "index route"; }; $user = function(){ echo "user route"; }; $http_404 = function(){ echo "error 404"; }; $routes = []; $routes["\/"] = $index; $routes["\/user\/(\d+)"] = $user; foreach($routes as $route => $action){ //$reg_ex = "/" . $route . "/"; $reg_ex = "/$route/i"; $matches = []; preg_match($reg_ex, $request_url, $matches); print_r($matches); if(!preg_match($reg_ex, $request_url, $matches)){ continue; } if(!is_callable($action)){ return call_user_func_array($http_404, $matches); } array_shift($matches); return call_user_func_array($action, $matches); } return call_user_func_array($http_404, [$request_url]);
输出结果
/MVC-Framework/user/42
Array ( [0] => / ) index route
问题原因及解决办法
问题根源
- 正则未做精确匹配:第一个路由的正则
\/会匹配URL中任意位置的单个斜杠,比如请求URL开头的/,导致直接命中index路由。 - 循环提前终止:匹配到第一个路由后直接
return,循环停止,后续的user路由根本不会被检查。 - URL包含项目前缀:请求URL带有项目根路径
/MVC-Framework,但路由规则是基于相对路径定义的,两者不匹配。
修复步骤
1. 剥离URL中的项目前缀
先把请求URL里的项目根路径去掉,只保留路由匹配需要的相对路径:
$base_path = '/MVC-Framework'; $request_url = str_replace($base_path, '', $_SERVER["REQUEST_URI"]); // 统一路径格式,避免末尾多斜杠的情况 $request_url = rtrim($request_url, '/') ?: '/';
2. 修改路由正则为精确匹配
给每个路由正则添加^(开头锚定)和$(结尾锚定),确保只有完全匹配的路径才会命中:
$routes["^\/$"] = $index; $routes["^\/user\/(\d+)$"] = $user;
3. 调整循环逻辑,避免提前终止
先遍历所有路由找到匹配项,再执行对应逻辑,而不是匹配到一个就直接返回:
$matched_action = $http_404; $matched_params = [$request_url]; foreach($routes as $route => $action){ $reg_ex = "/$route/i"; $matches = []; if(preg_match($reg_ex, $request_url, $matches)){ if(is_callable($action)){ $matched_action = $action; array_shift($matches); $matched_params = $matches; break; // 找到第一个匹配路由后退出循环 } } } call_user_func_array($matched_action, $matched_params);
修正后的完整代码
// 剥离项目前缀 $base_path = '/MVC-Framework'; $request_url = str_replace($base_path, '', $_SERVER["REQUEST_URI"]); $request_url = rtrim($request_url, '/') ?: '/'; echo $request_url . "<br />"; $index = function(){ echo "index route"; }; $user = function($id){ echo "user route, user ID: " . $id; }; $http_404 = function($url){ echo "error 404: " . $url; }; $routes = []; // 添加首尾锚定符,确保精确匹配 $routes["^\/$"] = $index; $routes["^\/user\/(\d+)$"] = $user; $matched_action = $http_404; $matched_params = [$request_url]; foreach($routes as $route => $action){ $reg_ex = "/$route/i"; $matches = []; if(preg_match($reg_ex, $request_url, $matches)){ if(is_callable($action)){ $matched_action = $action; array_shift($matches); $matched_params = $matches; break; } } } call_user_func_array($matched_action, $matched_params);
修改后,请求/MVC-Framework/user/42会正确匹配user路由,输出user route, user ID: 42;请求/MVC-Framework/会匹配index路由。
内容的提问来源于stack exchange,提问作者coding beginner no. 133920
相关产品推荐
相关产品推荐

