关于地址重写与路由分发的研究


(感觉我可能需要专门写一个BGM控件了)


什么是地址重写

注:由作者本人经验总结,不保证定义准确性,只当大致了解即可

地址重写又称URL重写,是由服务器所支持的,可以用来隐藏真实的地址以及实现伪静态功能。

地址重写会遵循一套特定的规则,而不同的服务器框架会用不同的规则,一般常见的服务器框架有IIS、Apache和Nginx,他们的重写规则不完全相同。

简单来讲,服务器在对地址进行重写时,做的就是替换工作,如把:https://xxxxxx.xxx/p/123给换成https://xxxxxx.xxx/passage.php?id=123,我们通过这种方法隐藏了真正起作用的API——passage.php,可以一定程度上防止被扒裤衩,还可以使得URL更加漂亮。

什么是路由分发

注:由作者本人经验总结,不保证定义准确性,只当大致了解即可

当然在网站里肯定不止有一个简简单单的"/p/123",还有很多诸如"/catagory/none"、"/tag/none"等其他不同的API,你当然可以直接在地址重写里面把这些API全部实现进去,但是还有另一种方法,那就是路由分发。

在使用路由分发机制时,我们一般会把重写规则变成如下(简单示意):

https://xxxxxx.xxx/p/123 ==> https://xxxxxx.xxx/index.php?p/123

非常简单,就是把URL的路径截出来,直接放在主页后面,转换成查询字符串的模式,这样我们在index.php里直接调用$_SERVER['QUERY_STRING']就能获得"p/123"。

然后就是路由分发发挥作用的时候了——把"p/123"解析成对应的API地址,然后调用。

这种处理伪静态URL的方法也是Typecho(v1.2)目前所使用的方法。

路由分发的背后

地址重写比较简单,只需要在网上找找不同服务器框架的重写规则,你也可以很快地写出一份rewrite文件,真正好玩的其实是路由分发。

路由分发的精髓就是解析查询字符串。访问一个URL的时候其实我们基本上就需要处理两个东西——GET请求和POST请求,其中POST请求我们根本不需要管,因为它不会出现在查询字符串里,重要的是处理查询字符串中的GET请求。

因为此时我们已经完全脱离了服务器自己的GET查询系统(整个查询字符串都被我们用来当路由了),所以只需要根据自己的需求来解析就可以了。一般情况可以用大量的正则匹配来完成(同样是Typecho的做法),把查询字符串解析成标准的URL访问API,或者直接在路由分发系统里面调用API,都可以起到隐藏API的功能。

路由分发除了隐藏API以外,还可以用来把外链转换成内链,如果遇上了某些又臭又长的外链非常有用,这也是博客系统经常用的功能。

傻瓜般的路由分配系统

注:不保证没有BUG

这个例子是傻瓜式匹配路由路径,以'@'作为分隔符来划分路径部分和GET部分,最终的URL呈现大概是这样:https://xxxxxx.xxx/index.php?action/[email protected]=get&id=123

如果你再进一步进行地址重写,你可以把它以假乱真成这样:https://xxxxxx.xxx/action/[email protected]=get&id=123,这个有点碍眼的小问号就没了。当然你还可以修改一下代码,把'@'给换成'?',让这段URL看起来更正常一点:https://xxxxxx.xxx/action/link?do=get&id=123

我们选择在路由系统内部调用API(include实现)。在API中直接调用rRouter::$queryStringrRouter::$getrRouter::$post可以分别得到真正的查询字符串"do=get&id=123"、get数组(即代替原来的$_GET)和post数组(源$_POST)。

add函数是用来添加路径的,当然理想状态是用正则匹配啥的,但是简单的查表对应也够了。

to404函数看字面就知道是干嘛的了。

despatch函数就是真正用来进行路由分发的,所有的解析都发生在这里。

// rRouter.php
<?php
class rRouter {
    static public $queryString, $get, $post;
    static private $p_list = array();

    static public function add($route, $path) {
        self::$p_list[$route] = $path;
    }

    static public function despatch() {
        self::$post = $_POST;
        $qry_ = $_SERVER['QUERY_STRING'];
        // 处理查询字符串
        $qpos_ = stripos($qry_, '@');
        if ($qpos_ !== false) {
            $qpth_ = substr($qry_, 0, $qpos_);
            self::$queryString = substr($qry_, $qpos_ + 1);
        }
        else {
            $qpth_ = $qry_;
            self::$queryString = '';
        }
        // 如果路径不存在,跳转404
        if (!array_key_exists($qpth_, self::$p_list))
            self::to404();
        // 处理查询字符串,把它解析成$_GET的样子
        self::$get = array();
        if ($qpos_ !== false) {
            $ar_ = explode('&', self::$queryString);
            foreach ($ar_ as $value_) {
                $pr_ = explode('=', $value_);
                self::$get[$pr_[0]] = $pr_[1];
            }
        }
        // 直接调用API
        include(self::$p_list[$qpth_]);
    }

    static public function to404() {
        header('HTTP/1.0 404 Not Found');
        // 支持自定义404页面
        if (array_key_exists('404', self::$p_list))
            include(self::$p_list['404']);
        exit;
    }
}

最后index.php里面用add把页面啥的定义好,再一个despatch就完事了。

// index.php
<?php
// 加载模块
include_once('rRouter.php');

// 你想把路径和哪个php页面相关联?写就是了
rRouter::add('picture', 'pic.php');
rRouter::add('audio', 'au.php');
rRouter::add('link', 'lk.php'); 

// 最后开始路由分发
rRouter::despatch();

最后

其实把路由分发模块做好了,就可以拿去到处用了。。。

写了那么多,感觉更多地是在谈路由分发呢。。。

路由分发能做的,地址重写也可以做,但是地址重写要修改rewrite文件,可能需要重启网站服务才能生效(?),路由分发可以有更大的自由度,当然注意一下BUG就行了。

标签: 技术, PHP

文档最后编辑于07月08日

评论

让我也说点啥