什么是地址重写
注:由作者本人经验总结,不保证定义准确性,只当大致了解即可
地址重写又称 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 全部实现进去,但是还有另一种方法,那就是路由分发。
在使用路由分发机制时,我们一般会把重写规则变成如下(简单示意):
非常简单,就是把 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 以外,还可以用来把外链转换成内链,如果遇上了某些又臭又长的外链非常有用,这也是博客系统经常用的功能。
傻瓜般的路由分配系统
这个例子是傻瓜式匹配路由路径,以 @
作为分隔符来划分路径部分和 GET 部分,最终的 URL 呈现大概是这样:https://xxxxxx.xxx/index.php?action/link@do=get&id=123
。
如果你再进一步进行地址重写,你可以把它以假乱真成这样:https://xxxxxx.xxx/action/link@do=get&id=123
,这个有点碍眼的小问号就没了。当然你还可以修改一下代码,把 @
给换成 ?
,让这段 URL 看起来更正常一点:https://xxxxxx.xxx/action/link?do=get&id=123
。
我们选择在路由系统内部调用 API(include 实现)。在 API 中直接调用 rRouter::$queryString
、rRouter::$get
和 rRouter::$post
可以分别得到真正的查询字符串 do=get&id=123
、get 数组(即代替原来的 $_GET
)和 post 数组(原 $_POST
)。
add 函数是用来添加路径的,当然理想状态是用正则匹配啥的,但是简单的查表对应也够了。
to404 函数看字面就知道是干嘛的了。
despatch 函数就是真正用来进行路由分发的,所有的解析都发生在这里。
<?php
// rRouter.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 就完事了。
<?php
// index.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 就行了。