请考虑以下非常基本的“控制器”(为简单起见,在这种情况下的功能):
function Index() {
var_dump(__FUNCTION__); // show the "Index" page
}
function Send($n) {
var_dump(__FUNCTION__, func_get_args()); // placeholder controller
}
function Receive($n) {
var_dump(__FUNCTION__, func_get_args()); // placeholder controller
}
function Not_Found() {
var_dump(__FUNCTION__); // show a "404 - Not Found" page
}
以下基于正则表达式的Route()
函数:
function Route($route, $function = null)
{
$result = rtrim(preg_replace('~/+~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']))), '/');
if (preg_match('~' . rtrim(str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $route), '/') . '$~i', $result, $matches) > 0)
{
exit(call_user_func_array($function, array_slice($matches, 1)));
}
return false;
}
现在我想将以下URL(忽略尾部斜杠)映射到相应的“控制器”:
/index.php -> Index()
/index.php/send/:NUM -> Send()
/index.php/receive/:NUM -> Receive()
/index.php/NON_EXISTENT -> Not_Found()
这是事情开始变得棘手的部分,我有两个问题我无法解决......我想我不是第一个遇到这个问题的人,所以那里的人应该有溶液
我找不到区分对根(index.php
)的请求和不应该存在的请求(index.php/notHere
)的方法。我最终为URL提供了默认的index.php
路由,否则该路由应该是404 - Not Found
错误页面。 我该如何解决这个问题?
编辑 - 解决方案在我脑海中浮现:
Route('/send/(:num)', 'Send');
Route('/receive/(:num)', 'Receive');
Route('/:any', 'Not_Found'); // use :any here, see the problem bellow
Route('/', 'Index');
如果我按照“逻辑”顺序设置路线,如下所示:
Route('/', 'Index');
Route('/send/(:num)', 'Send');
Route('/receive/(:num)', 'Receive');
Route(':any', 'Not_Found');
所有URL请求都由Index()
控制器捕获,因为空的正则表达式(记住:尾随斜杠被忽略)匹配所有内容。但是,如果我以“hacky”顺序定义路线,如下所示:
Route('/send/(:num)', 'Send');
Route('/receive/(:num)', 'Receive');
Route('/:any', 'Not_Found');
Route('/', 'Index');
所有似乎按照应有的方式工作。 有解决此问题的优雅方法吗?
路由可能并不总是硬编码(从数据库或其他东西中提取),并且我需要确保它不会因为定义它们的顺序而忽略任何路由。 感谢任何帮助!
答案 0 :(得分:1)
好吧,我知道有一种方法可以给猫皮肤,但为什么你会这样做呢?看起来像某些RoR方法可以使用mod_rewrite轻松处理
话虽这么说,我重写了你的路线功能,并且能够实现你的目标。请记住,我添加了另一个条件来直接捕获索引,因为你正在删除所有的/这就是为什么它匹配索引时你希望它匹配404.我还整合了4个Route()调用使用一个foreach()。
function Route()
{
$result = rtrim(preg_replace('~/+~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']))), '/');
$matches = array();
$routes = array(
'Send' => '/send/(:num)',
'Receive' => '/receive/(:num)',
'Index' => '/',
'Not_Found' => null
);
foreach ($routes as $function => $route)
{
if (($route == '/' && $result == '')
|| (preg_match('~' . rtrim(str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $route)) . '$~i', $result, $matches) > 0))
{
exit(call_user_func_array($function, array_slice($matches, 1)));
}
}
return false;
}
Route();
干杯!
答案 1 :(得分:1)
这是MVC webapps的一个常见问题,通常在它成为问题之前就已经解决了。
最简单,最通用的方法是使用例外。如果您没有给定参数的内容,则抛出PageNotFound异常。在应用程序的顶层,捕获所有异常,如此简化示例:
的index.php:
try {
$controller->method($arg);
} catch (PageNotFound $e) {
show404Page($e->getMessage());
} catch (Exception $e) {
logFatalError($e->getMessage());
show500Page();
}
Controller.php这样:
function method($arg) {
$obj = findByID($arg);
if (false === $obj) {
throw new PageNotFound($arg);
} else {
...
}
}
可以通过对正则表达式进行排序来解决排序问题,以便首先匹配最具体的正则表达式,并且最后匹配最不具体的正则表达式。为此,请计算正则表达式中的路径separtors(即斜杠),不包括开头的路径分隔符。你会得到这个:
Regex Separators
--------------------------
/send/(:num) 1
/send/8/(:num) 2
/ 0
按降序排序,然后处理。流程订单是:
答案 2 :(得分:0)
首先确定:
foo.com/index.php/more/info/to/follow
完全有效,并且按照标准应该加载index.php并将$ _SERVER [PATH_INFO]设置为/ more / info / to / follow。这是CGI/1.1 standard。如果您希望服务器不执行PATH_INFO扩展,请在服务器设置中将其关闭。在apache下,它使用:
完成AcceptPathInfo Off
如果你在Apache2下将它设置为Off ...它将发送一个404.
我不确定IIS标志是什么,但我认为你可以找到它。