Mod-Rewrite还是PHP路由器?

2022-08-30 23:34:50

我正在讨论使用以下两个选项之一路由我的请求:

选项1:简单的捕获路由,带有Mod-Rewrite和漏斗写入路由到索引.php用于加载...$_GET

#default routing
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule    ^blog/([0-9]+)?$    index.php?rt=blog&params=$1    [L,QSA]
// ..more custom routes, and then a default route
RewriteRule    ^([A-Za-z]+)/([A-Za-z]+)/(.*)?$    index.php?rt=$1/$2&params=$3    [L,QSA]

选项2:只需将请求路由到前端控制器,并创建一个PHP路由类来处理路由...

#default routing
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]

/* --- on front controller, process $_GET['rt'] --- */

在一天结束时,哪个将运行得更快,更容易保护,更容易维护?

还有其他想法吗?

注意:我没有运行已知的框架。我正在构建自己的MVC模式来学习它。


答案 1

通常在MVC框架中,这种事情通常最好由前端控制器(命名或类似名称)处理。然后,您可以使用mod_rewrite隐藏所有URL,以便您的用户看到漂亮的干净路径。index.phpindex.php

在PHP中处理它也比在Apache的重写指令中更容易处理。PHP更加灵活,更易于编写/理解。我不确定我是否见过mod_rewrite被用作任何Web框架的唯一路由引擎,现在我想到了它。

您的第二段代码是重写指令的必经之路。


答案 2

我也在从头开始构建LAMP MVC系统。

MVC 路由器类问题

性能

在创建了一个bash shell脚本来从源代码编译Apache 2.4.x之后,您注意到一个Perl兼容正则表达式库被嵌入到该过程中。每当必须使用正则表达式代码时,http服务器的响应速度会变慢。因此,如果性能是您关心的问题,则选项一是不可行的。正则表达式所涉及的分析是有成本的。Apache Web服务器正在借用正则表达式的逻辑。它不是Apaches的本土代码。

安全

安全性是另一个问题。首先,重写URL并不能使它们安全。这是通过晦涩难懂的简单安全性,并使客户端看起来很漂亮。不时会在正则表达式引擎代码中发现安全漏洞。因此,就真正的安全性而言,您将处于与没有任何重写相同的基本位置:人或机器人可以向服务器发送不良内容,并且您需要一种方法来过滤和验证输入。请务必筛选并验证所有输入,尤其是要使用的重写查询字符串的任何部分。

因此,选项一提供了一个更干净的界面( / )来启动您的安全职责。选项 2 要求(如果您尝试彻底)作为第一步筛选和验证整个字符串。第二步(通常)是分解并从更大的字符串中提取您希望收集的内容。INPUT_GET$_GET

同样,您应该筛选和验证字符串的每个部分。因此,虽然在PHP中过滤/验证/分解/提取/较大的字符串可能更易于管理(更容易,更方便)(例如,使用某种安全类中的方法),您仍然需要为每个部分,每个请求做工作。选项一为您省去了必须分解较大字符串的麻烦,因为在每个请求上执行正则表达式引擎的成本。但是,在大多数情况下,您只需开始过滤和验证您希望在 或 中接收的元素。INPUT_GET$_GET

但是,请注意,选项一主要适用于真正了解正则表达式如何工作以及这如何适用于服务器可能收到的潜在URL的人。如果您需要多个 ,则可以这样就可以将类似的东西(或其他原因)放入服务器。RewriteRule

index.php?0=model 

index.php?0=model&1=method

index.php?0=model&1=method&2=methodArgs

这样可以更轻松地筛选和验证输入。但请注意,最后一行意味着您可能仍然需要执行一些行拆分和进一步的筛选/验证(但这可能不必在每个请求上都发生,就像在选项1中所做的那样)。

代码示例:使用选项 2 获取 URL 参数(从底部开始!

注意:这些只是需要考虑的一些事情,而不是“”方法。

const QS_ARRAY_LIMIT = 3;

private function getPairValue($delimiter, $string)
{
    return explode('$delimiter', $string)[1];  //Get the value for a pair.
}

private function isMultiValuedQueryString()
{
    return (mb_strpos($this->queryStr, '&', 0, 'UTF-8') > 2);
}

private function getAllowedPairs($argsStr)
{
    $pairs    = explode('&', $argsStr);
    $numPairs = count($pairs);

    if($numPairs > self::QS_ARRAY_LIMIT)
    {
        throw new SecurityException("Too many query string pairs ({$numPairs}) submitted to the router.\n");
    }

    return $pairs;
}

private function isQueryStrPair($pair)
{
    $equalPos = null;
    $pairLength = mb_strlen($pair, 'UTF-8');

    if($pairLength < 3)
    {
        throw new SecurityException("Query string pair is too short: Length: {$pairLength}!, Suspect: {$pair}\n");  //Sends to  '/'
    }

    $equalPos = mb_strpos($pair, '=', 0, 'UTF-8');

    if($equalPos === 0) //The first position.
    {
        throw new SecurityException("Query sting pair cannot *start* with an equal sign (=): Suspect: {$pair}\n");  //Sends to  '/'
    }

    if($equalPos === ($pairLength - 1)) //The last position.
    {
        throw new SecurityException("Query sting pair cannot *end* with an equal sign (=): Suspect: {$pair}\n");  //Sends to  '/'
    }

    return true;
}

private function getQueryStringArgs($url)
{
    $delimiter = '?';

    if(mb_strpos($url, $delimiter, 0, 'UTF-8') > 0)
    {
        return $this->getPairValue($delimiter, $url);
    }

    throw new RuntimeException("Malformed URL passed to query string parser.");
}

private function associateArgPairs(array $argPairs, array $values)
{
    $i = 0;

    foreach($argPairs as $key => &value)
    {
        if(isset($values[$i]))
        {
            $value[$key] = $values[$i];
            ++$i;
        }
    }

    return $argPairs;
}

private function getQueryStrValues($url)
{
    $delimiter   = '=';
    $argPairs    = ['model' => null, 'method' => null, 'methodArgs' => null]
    $inputValues = [];
    // =================================================
    // Valid query strings might look like (amongst many combinations):
    //
    // index.php?arg1=foo&agr2=bar&arg3=/baz/bang/boom
    //
    // Or, just one pair with no ampersand,'&'.
    //
    // index.php?arg1=foo
    // ==================================================

    // Get everything after the question mark, '?'.
    $queryStringArgsStr = $this->getQueryStringArgs($url); 

    if($this->isMultiValuedQueryString($queryStringArgsStr))  //Check if '&' exists.
    {
        foreach($this->getAllowedPairs($queryStringArgsStr) as $pair)
        {
            if($this->isQueryStrPair($pair))
            {
                //Get the value for each pair., using '=' as the string delimiter.
                $inputValues[] = $this->getPairValue($delimiter, $pair);  
            }
        }
    }
    else
    {    
        if($this->isQueryStrPair($queryStringArgsStr))
        {
            $inputValues[] = $this->getPairValue($delimiter, $queryStringArgsStr);  //Get the value for each pair.
        }
    }

    return $this->associateArgPairs($argPairs, $inputValues);

    //Remember, you will still need to split up $argPairs[$methodArgs] if necessary.
    //With option #1, you could start closer to this point,
    //and hence filter and validate sooner.
}

总结

如果安全性是您的主要关注点(即您的接口进入您的安全方案),请咬紧牙关并使用选项一。学习mod_rewrite和URL重写会让你变得非常强大。为什么要把这种权力留在桌子上?Apache在配置它时是一种奇怪的动物。但是,如果你理解URL和正则表达式,我说男人/女人起来,然后去做。:-)如果速度,理解和易用性是您的主要关注点,请选择第一个选项。有些狂热分子希望所有内容都用PHP编码,但是您必须自己判断这两种情况的利弊。

更新:我的坏。此代码实际上更适合选项编号 1。在选项2中,1 = blah,其中blah将是类似的东西或其他任何东西。您不必查找 & 符号。/trt/43ff/3335/f/3/fr3r/

PHP: filter_inpur_array(), (使用INPUT_GET)

PHP: 超级全球

PHP: explode()

PHP: foreach (array_expression as $key => $value)

PHP:Multibtye String Functions


推荐