最近在用 yii2 框架写 restful 应用,第一步就是要把 yii2 的 urlManager 和 nginx 的伪静态/ 路由重写结合起来。查了资料之后,发现和 php
的 PATH_INFO
有关,这个例子参考了http://www.thinkphp.cn/topic/3228.html ,希望读者能提出更好的方案。
不完全解决方案
- 开启 yii2 路由美化
'enablePrettyUrl' => true
和'showScriptName' => true
- 写一个控制器,继承自
yii\rest\ActiveController
,并声明其$modelClass
为一个继承自yii\web\ActiveRecord
的模型类 - 在 yii2 对应目录的
urlManager
配置块中,增加
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'my-restful',
// 这里的控制器类的名字实际是
// MyRestfulController
],
],
并删除掉原有的 rules
键值对们(目测 yii\rest\UrlRule
自带一套 rules
而不必显式重写)
- 开启 nginx 伪静态之一,添加
fastcgi_split_path_info ^(.+\.php)(.*)$;
- 开启 nginx 伪静态之二,添加
fastcgi_param PATH_INFO $fastcgi_path_info;
利用php
的PATH_INFO
来储存查询参数 - 以上两项
nginx
的配置都在nginx.conf
的location ~ \.php
区块中,可以把location ~ \.php$
区块重命名后再添加以上nginx
的配置 - 我还在
nginx.conf
的location /
区块里面加了try_files $uri $uri/ index.php?$args;
这个配置如下,
location ~ \.php { # 原为 location ~ \.php$
root my/web/root;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME my/web/root$fastcgi_script_name;
include fastcgi_params;
}
运行效果
实现了url 附带参数形式的变化,也就是从
http://my-host.com/app/web/index.php?r=module_name/controller_name/action_name
变成了http://my-host.com/app/web/index.php/controller_name/action_name
实际运行中,让
http://my-host.com/api/web/index.php?r=user/security/login
变成了http://my-host.com/api/web/index.php/user/login
存在问题
未设置以上
nginx
配置时,原本的 restful 应用(也就是继承自yii\rest\ActiveController
类的控制器)访问正常,用了上面的全部配置后,登陆页面终于可以访问(AppAsset
加载的 js 脚本、css 样式也恢复正常),但是原本的 restful 应用成了 Not Found, 我猜和checkAccess()
方法有关,还有其他未证实的,后面会跟进 :)
小的改进
不论是 restful 还是简单的 MVC 应用, yii2 配置各个 action 的权限的格式都需要注意。下面添加对
actionIndex()
的访问权限,是位于behaviors
配置块或者behaviors()
方法返回的数组[]
中的。
'access' => [
'class' => AccessControl::className(),
'only' => ['index','logout'],
'rules' => [
[
'actions' => ['index'],
'allow' => true,
'roles' => ['@'], // 登陆后才可以访问
]
],
]
在 restful 应用中,由于
yii\rest\UrlRule
自带了一套访问规则,我们一般把基类yii\rest\ActiveController
的behaviors
引入到自己实现的类里面。后面还有个设置showScriptName
为false
导致了no input file specified
的问题,我进一步看看。