目录

整理摘选自: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对象。

    域名路由 · ThinkPHP6.0完全开发手册 · 看云

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完全开发手册 · 看云

吐槽

我们觉得路由规则不需要搞的这么复杂,在有中间件,事件这些机制的情况下,路由可以简化,也不需要那么多别名标识之类的东西,这些本身不是路由的重点,路由功能多到,不知道哪些是重点,让开发者容易混淆,甚至有些过于灵活而不方便维护。什么都有个度,过了度也不好。比如勇敢是有一个度量的,胆子太小,我们说懦弱,胆子过大,我们说鲁莽。