URL - 实现伪静态

  • 作者:KK

  • 发表日期:2016.12.10


为何要伪装URL

http://xxx.com/?r=user/info这样的URL会暴露程序设计的细节,比如让懂得MVC WEB开发的访问用户了解到我们是使用user控制器的info方法来实现的

然后他们可能会恶意地篡改成http://xxx.com/?r=user/into23xx这样的地址再访问一下,由于找不到 into23xx这个方法,导致程序报错,产生报错日志…

而报错页面又可能会把文件路径、服务器信息等内容暴露出去等等问题,所以,我们就希望在URL能掩盖程序的设计

比如伪装成“http://xxx.com/u123.html”这个网址就是调用 user控制器的info方法输出123这个用户的信息,又比如伪装成“http://xxx.com/n223.html ”这样的网址来调用news控制器的某个方法来输出223号新闻的内容

这种伪装的URL在业内一般都称之为伪静态,表面上是静态的页面的址,指向一个html页面,实际上它是动态的数据输出


实现伪静态URL

这是通过urlManager(URL管理者)这个组件来实现的,你可以先在控制器里var_dump一下Yii::$app->urlManager这个组件,它是Yii的核心组件

框架默认情况下是没有开启伪静态URL的,我们需要修改一下urlManager组件的配置来开启伪静态

在配置里增加urlManager组件的配置,注意值是一个数组哦!因为我们要控制一些组件属性的值嘛

至于这个组件都有什么属性呢,详细需要参考官方的urlManager组件的对应类的说明yii\web\UrlManager

而咱们要实现伪静态只需要知道几个属性:enablePrettyUrlshowScriptNameenableStrictParsingrules这四个属性!

请按照我下面这样来配置urlManager组件:

'urlManager' => [
	'enablePrettyUrl' => true,	//开启伪静态URL模式
	'showScriptName' => false,	//生成的网址里不带入口脚本名称
	'enableStrictParsing' => true,	//开启严格伪静态模式
	'rules' => [
		//伪静态规则配置,下面会讲解如何填写
	],
],

好了,估计 ?r=test/abc 这样的URL一旦访问时就会找不到页面而报404错误了,说明已经成功开启了伪静态模式

接下来我们添加一个URL白名单让它路由到test控制器的abc方法

上面增加urlManager组件的配置后,里面有个rules属性,值是一个数组,这里就是放URL白名单的地方了

比如我们要实现/uiui.html这个网址访问到test控制器的abc方法,就这样添加rules内容:

'rules' => [
	'uiui.html' => 'test/abc',
],

神奇的事发生了!当你在浏览器输入“**http://xxx.com/uiui.html” 就会运行test控制器abc方法的代码

你又学会一招办法来实现这么高大上的偷天换日效果了,瞬间感觉你变得强大了好多,明天即可当上CTO,迎娶白富美了!!!......(路子还远呐亲)

然后你试着在控制器里echo Url::to(['test/abc']);也会输出“/uiui.html” 这个结果!

再来,你在rules里面将规则改成'baba.html' => 'test/abc'那Url::to方法也会将“test/abc”这个控制器方法标记生成为“/baba.html

这就是为什么不提倡直接将网址写死在模板上的原因了,因为伪静态规则可能会在配置里被改变的,我们应该尽可能用Url::to来生成网址,减少维护的修改量


在伪静态中传递GET参数

我们用/uiui.html这样的伪静态来取代了/?r=test/abc的网址形式

那么又如何表达/?r=test/abc&userId=123呢?难道是/uiui123.html 这样吗?不行不行

其实简单地做,就可以这样写:/uiui.html?userId=123,这样控制器里依然可以收到参数,不信你试试 Yii::$app->request->get('userId')

但一般情况下我们项目中都尽量把GET参数也隐藏在伪静态之中,比如实现

/uiui7788.html就相当于/?r=test/abc&userId=7788的效果,配置办法如下:

'rules' => [
	'uiui<userId:\d+>.html' => 'test/abc',
],

然后你试试

echo Url::to(['test/abc', 'userId' => 7788]);

就能看到输出/uiui7788.html


rules的key写法简介

这里需要一些正则表达式的知识基础,反正里面就是增加<>这两个符号,表达这块区域要套一个参数进来

然后以参数的名称加:号开头,比如userId:这样Url::to方法的'userId' => 值才能知道它要生成到伪静态的哪个位置上

:号后面的\d+表示当user_id的值是数字的时候才可以生成,否则的话,比如'userId' => 'abc'这样的参数放在to方法里

由于值是字符串abc而不是一个数字,所以无法跟rules里的\d+(数字)匹配,不会生成预想中的/uiuiabc.html ,如果要实现这样的话,必须将\d+改成\w+(英数字下划线),如果不熟悉正则基础的去恶补知识吧

而通常情况下rules配置中我也最常用的就是\d+\w+这两个正则匹配符


复杂点的多参数伪静态

多个参数时其实也很容易理解,看看:

'uiui<userId:\d+><userName:\w+>-balabala<age:\d{2}><type:\d><width:\d+>-<height:\d+>.html' => 'test/abc'

这里一共有6个参数,userId、userName、age、type、width和height,其中age只匹配2位数字,type只匹配一位数字

小弟又提供测试代码给你复制了多好:

echo Url::to(['test/abc',
	'userId' => 65535,
	'userName' => 'jay',
	'age' => 18,
	'type' => 4,
	'width' => 200,
	'height' => 100,
]);

结果就生成了“http://xxx.com/uiui65535jay-balabala184200-100.html

对了你有没有发现,test控制器不用建立abc方法也能生成?其实生成URL只是检查rules配置,匹配了就生成,它不管你有没有控制器方法(学过其它框架的话也很清楚,这个不新鲜了)

这个URL代入get参数后就是

$_GET = [
	'userId' => 45678,
	'userName' => 'kkqbx',
	'age' => 18,
	'type' => 2,
	'width' => 1024,
	'height' => 768,
];

rules的规则定义顺序

相似的几个伪静态共存时,精确的伪静态必须放在最前面,模糊的放后面,比如有以下伪静态

'rules' => [
	'a<id:\w+>.html' => 'test/a',
	'abc.html' => 'test/b',
]

当访问/abc.html时永远不会路由到test/b,因为它已经被a<id:\w+>.html这条规则匹配成功,不再往下匹配了

想匹配test/b的话就要将abc.html这条规则移到test/a的规则前面才可以

重要知识点:原则上就是越精确的伪静态就越靠前就好

比如abc.html是没有正则表达式的,所以它绝对精确地描述了匹配内容,如果将带正则的规则放在前面就会被别人匹配掉了(因为正则匹配其实就是一种模糊匹配)


题外话:分享我的命名风格

如果伪静态URL是提供AJAX请求返回数据用的,我在伪静态里一般都使用.json结尾

显示一个页面则用.html

而比如是登陆、充值、删除这些,虽然操作后服务端也是返回json给前端,但它的“主要行为目的不是为了取数据,而是为了处理数据”,则我会命名为.do

甚至验证码的我就会用.jpg了,附上示例代码:

'rules' => [
	'users.html' => 'user/list',
	'users/<page:\d+>.json' => 'user/list-data',
	'users/<id:\d+>.html' => 'user/info',
	'login.do' => 'user/login',
	'users/delete.do' => 'user/delete',
	'captcha.jpg' => 'site/captcha',
	
	'cgi-bin/pay-order.aid8877.bid<bid:\w+>' => 'banner/compose',
]

最完善的URL生成和伪静态定义知识请见英文官方文档,讲得比中文官方文档还要完整

但我讲的这些目前至少是够你做一般的程序用