ThinkPHP6路由快速了解
目录
- 路由的好处
- 解析过程
- 路由不好的地方
- 相关文件与配置
- 路由定义
- 变量规则
- 路由地址
- 路由参数
- 路由中间件
- 路由分组
- 资源路由
- 注解路由
- 路由绑定
- 域名路由
- MISS路由
- 支持跨域请求
- URl生成规则
- 吐槽
整理摘选自:ThinkPHP6使用基础报告 - 9ong
路由的好处
- 让URL更规范以及优雅;
- 隐式传入额外请求参数;
- 统一拦截并进行权限检查等操作;
- 绑定请求数据;
- 使用请求缓存;
- 路由中间件支持;
解析过程
- 路由定义
- 路由检测
- 路由解析
- 路由调度
作为开发者只需要关注路由的定义与配置即可。
路由不好的地方
- 需要在开发前对路由进行主体规划和定义
- 属于特殊规则,相对于默认的pathinfo规则来说比较复杂
相关文件与配置
- 路由配置文件:config/route.php
- 路由定义文件:route/app.php
- 可以在config/app.php中配置write_route关闭路由,如果是应用目录下的config/app.php将只关闭当下应用的路由功能
路由定义
路由是针对应用的,每个应用的路由是完全独立。也就是说设置路由后的访问URL地址还需要带上应用名,除非是单应用。
route目录下的任何路由定义文件都是有效的,默认的路由定义文件是app.php,而且我们可以改名,还可以添加多个路由定义文件。比如我们需要在多控制器和操作且多人密集协作,为了避免冲突时,可以考虑多路由文件定义。
-
路由注册
路由的定义,本质上由三部分组成:路由表达式、路由地址、请求类型:
Route::rule('路由表达式', '路由地址', '请求类型');
-
路由定义快捷方法get/post/put/delete/patch
Route::快捷方法名('路由表达式', '路由地址'); Route::get('new/<id>','News/read'); // 定义GET请求路由规则 Route::post('new/<id>','News/update'); // 定义POST请求路由规则 Route::put('new/:id','News/update'); // 定义PUT请求路由规则 Route::delete('new/:id','News/delete'); // 定义DELETE请求路由规则 Route::any('new/:id','News/read'); // 所有请求都支持的路由规则
-
按照定义顺序匹配路由规则
-
静态地址与动态地址路由规则
Route::rule('/', 'index'); // 首页访问路由 Route::rule('my', 'Member/myinfo'); // 静态地址路由 Route::rule('blog/:id', 'Blog/read'); // 静态地址和动态地址结合 Route::rule('new/:year/:month/:day', 'News/read'); // 静态地址和动态地址结合 Route::rule(':user/:blog_id', 'Blog/read'); // 全动态地址
-
根据路由生成URL地址
不建议使用路由标识,不要为了标识而标识,有点鸡肋。
// 注册路由到News控制器的read操作 Route::rule('new/:id','News/read') ->name('new_read');//不建议给标识别名
url('new_read', ['id' => 10]);//不建议用,只有自己才知道是什么,不便于管理 url('News/read', ['id' => 10]);//这不是挺好,看代码就知道是什么
-
额外传参
Route::get('blog/:id','blog/read') ->append(['status' => 1, 'app_id' =>5]);
变量规则
这里说的变量规则,是路由表达式中的变量规则。
-
路由规则变量有限制
用来约束动态路由变量:
// 默认的路由变量规则 只会匹配字母、数字、中文和下划线字符,并不会匹配特殊符号以及其它字符 'default_route_pattern' => '[\w\.]+',
// 支持批量添加 Route::pattern([ 'name' => '\w+', 'id' => '\d+', ]); // 定义GET请求路由规则 并设置name变量规则 Route::get('new/:name', 'News/read') ->pattern(['name' => '[\w|\-]+']);
-
动态路由
可以把路由规则中的变量传入路由地址中,就可以实现一个动态路由,例如:
// 定义动态路由 Route::get('hello/:name', 'index/:name/hello');
name变量的值作为路由地址传入,可以达到路由根据变量name的不同而路由到不同的地址上。
路由地址
-
路由支持多级控制器
Route::get('blog/:id','group.Blog/read'); //表示路由到下面的控制器类,index/controller/group/Blog
-
路由支持到类解析
\完整类名@方法名
或者(静态方法)
\完整类名::方法名
Route::get('blog/:id','\app\index\service\Blog@read');
-
路由支持302重定向
Route::redirect('blog/:id', 'http://blog.thinkphp.cn/read/:id', 302);
-
路由支持直接到view模板
// 路由到模板文件 Route::view('hello/:name', 'index/hello', ['city'=>'shanghai']); //表示该路由会渲染当前应用下面的view/index/hello.html模板文件输出。 //在模板中可以输出name和city两个变量。{$name}--{$city}!
-
路由支持直接输出响应(response) -
路由支持到闭包(当场响应请求)
Route::get('hello', function () { return 'hello,world!'; }); //自定义输出 Route::get('hello/:name', function () { response()->data('Hello,ThinkPHP') ->code(200) ->contentType('text/plain'); }); //传递参数 Route::get('hello/:name', function ($name) { return 'Hello,' . $name; }); //依赖注入 Route::rule('hello/:name', function (Request $request, $name) { $method = $request->method(); return '[' . $method . '] Hello,' . $name; });
路由参数
路由参数提供对url的检查及二次处理。
Route::get('new/:id', 'News/read')
->ext('html')
->https();
更多参数详细参考官方文档:路由参数 · ThinkPHP6.0完全开发手册 · 看云
路由中间件
在中间件部分,已经着重介绍过路由中间件。
更多参考:路由中间件 · ThinkPHP6.0完全开发手册 · 看云
路由分组
路由分组功能允许把相同前缀的路由定义合并分组,这样可以简化路由定义,并且提高路由匹配的效率,不必每次都去遍历完整的路由规则(尤其是开启了路由延迟解析后性能更佳)
Route::group('blog', function () {
Route::rule(':id', 'blog/read');
Route::rule(':name', 'blog/read');
})->ext('html')->pattern(['id' => '\d+', 'name' => '\w+']);
//如果仅仅是用于对一些路由规则设置一些公共的路由参数(也称之为虚拟分组),也可以使用:
Route::group(function () {
Route::rule('blog/:id', 'blog/read');
Route::rule('blog/:name', 'blog/read');
})->ext('html')->pattern(['id' => '\d+', 'name' => '\w+']);
//路由prefix方法,prefix将自动补充到路由地址上blog/read blog/update
Route::group('blog', function () {
Route::get(':id', 'read');
Route::post(':id', 'update');
Route::delete(':id', 'delete');
})->prefix('blog/')->ext('html')->pattern(['id' => '\d+']);
关于延迟路由解析与路由规则合并解析,官方说在分组和域名路由情况下,可以提升性能,但为什么框架不自动处理了?而是默认关闭,让开发者自己开启?是有其他什么负面影响?文档里没有看到更多的描述,需要深入源码了解。
资源路由
支持设置RESTFul请求的资源路由,方式如下:
Route::resource('blog', 'Blog');
表示注册了一个名称为blog的资源路由到Blog控制器,系统会自动注册7个路由规则,如下:
标识 | 请求类型 | 生成路由规则 | 对应操作方法(默认) |
---|---|---|---|
index | GET | blog | index |
create | GET | blog/create | create |
save | POST | blog | save |
read | GET | blog/:id | read |
edit | GET | blog/:id/edit | edit |
update | PUT | blog/:id | update |
delete | DELETE | blog/:id | delete |
比如:
http://serverName/blog/,访问Blog类的index方法
http://serverName/blog/128,访问Blog类的read方法
http://serverName/blog/28/edit,访问Blog类的edit方法
当然要注意:这三各都是GET类型,save、update、delete对方的类型不是GET
注解路由
说到注解,引用PHP手册里熟悉又陌生的知识点 - 9ong中的注解小节:
php8刚引入注解,在php8之前symfony、laravel、hyperf等框架都通过反射实现了所谓的“注解”,满足AOP编程。
在官方注解未出来之前,大部分框架的注解是通过反射实现,先把类或函数的注释取到,用语法解析或者正则之类的方式匹配注解符号,假装是注解,然后处理“注解”。
官方注解的符号也一直在变:从@ 到 «» 到 @@ 再到 #[]
很需要更多的应用场景来深入理解php的注解,注解目前更多用于配置去中心化。
虽然配置去中心化看起来挺美的,但在方法的注释中使用@Route关键词定义路由规则,目前应该会存在一些意想不到的问题,不大建议用于生产环境。
路由绑定
域名路由
支持完整域名、子域名和IP部署的路由和绑定功能,同时还可以起到简化URL的作用。
-
解析路由规则
可以单独给域名设置路由规则,例如给blog和admin子域名注册单独的路由规则:
Route::domain(['blog', 'admin'], function () { // 动态注册域名的路由规则 Route::rule('new/:id', 'news/read'); Route::rule(':user', 'user/info'); });
-
支持域名绑定到路由
支持绑定的控制器、命名空间、类,甚至response对象。
MISS路由
注意设置MISS路由意味着开启强制路由
// 只有GET请求下MISS路由有效
Route::miss('public/miss', 'get');
//或者闭包
Route::miss(function() {
return '404 Not Found!';
});
//也支持分组里或域名路由,在没有匹配到路由规则时,采用miss路由
Route::group('blog', function () {
Route::rule(':id', 'blog/read');
Route::rule(':name', 'blog/read');
Route::miss('blog/miss');
})
支持跨域请求
还有待进一步完善,期待更多实践,暴露这方面的问题,官方及时完善修复。
URl生成规则
注意:如果开启了路由延迟解析,需要生成路由映射缓存才能支持全部的路由地址的反转解析。脑壳疼。
- 建议使用路由地址
- 注意URL后缀的配置url_html_suffix
- 补充域名生成,支持自动当前域名,支持指定域名
- 支持生成锚点
- 当然tp6依然会有助手函数url()来生成url地址
除了
Route::buildUrl('index/blog/read', ['id'=>5])->domain('blog.thinkphp.cn');
也支持助手函数url()
url('index/blog/read', ['id'=>5])
->suffix('html')
->domain(true)
->root('/index.php');
URL生成 · ThinkPHP6.0完全开发手册 · 看云
吐槽
我们觉得路由规则不需要搞的这么复杂,在有中间件,事件这些机制的情况下,路由可以简化,也不需要那么多别名标识之类的东西,这些本身不是路由的重点,路由功能多到,不知道哪些是重点,让开发者容易混淆,甚至有些过于灵活而不方便维护。什么都有个度,过了度也不好。比如勇敢是有一个度量的,胆子太小,我们说懦弱,胆子过大,我们说鲁莽。