一.路由简介 1. 路由的作用就是让URL地址更加的规范和优雅,或者说更加简洁;
2. 设置路由对URL的检测、验证等一系列操作提供了极大的便利性;
3. 路由是默认开启的,如果想要关闭路由,在config/app.php配置; // 是否启用路由 'with_route' => false,
4. 路由的配置文件在config/route.php中,定义文件在route/app.php;
配置文件是指的是配置相关的属性,定义文件是定义路由规则。
5. route目录下的定义文件的文件名随机,都有效,或多个均有效果;
6. 创建一个Address控制器类,创建两个方法,具体如下:
class Address
{
public function index()
{
return 'index';
}
public function details($id)
{
return 'details 目前调用的id:'.$id;
}
}
演示:
由于address/details/id/5,想变成ds/5,就要使用路由规则:
那么,,
====================================================================
7. 为了让我们路由的课程观看更加直观,我们采用内置服务器的方式来演示;
8. 通过命令行模式键入到当前项目目录后输入命令:php think run启动;
9. 此时,public目录会自动被绑定到顶级域名:127.0.0.1:8000上;
10. 我们只要在地址栏键入:http://localhost:8000或(127.0.0.1:8000)即可;
二.路由定义
1. 在没有定义路由规则的情况下,我们访问address/details包含id的URL为: http://localhost:8000/address/details/id/5 //或者.../id/5.html
2. 将这个URL定义路由规则,在根目录route下的app.php里配置; Route::rule('details/:id', 'Address/details');
3.当配置好路由规则后,会出现非法请求的错误,我们需要用路由规则的URL访问; http://localhost:8000/details/5 //或者.../details/5.html
4. rule()方法是默认请求是any,即任何请求类型均可,第三参数可以限制:
Route::rule('details/:id','Address/xxx,'GET'); //GET
Route::rule('details/:id','Address/xxx,'POST'); //POST
Route::rule('details/:id','Address/xxx,'GET|POST'); //GET或POST
5.所有请求方式(快捷方式):GET(get)、POST(post)、DELETE(delete)、PUT(put) PATCH(patch)、*(any,任意请求方式)
Route::get(...)、Route::post(...)、Route::delete(...)...
6.快捷方式,就是直接用Route::get、Route::post等方式即可,无须第三参数;
7.当我们设置了强制路由的时候,访问首页就会报错,必须强制设置首页路由;
8.开始强制路由,需要在route.php里面进行配置,然后配置首页路由;
'url_route_must' => true,
Route::rule('/', 'Index/index'); //反斜杠就是首页默认访问的地址
9.在路由的规则表达式中,有多种地址的配置规则,具体如下:
//静态路由
Route::rule('ad','Address/index');
//静态动态结合的地址
Route::rule('details/:id','Address/details');
//多参数静态动态结合的地址
Route::rule('search/:id/:uid', 'Address/search');
//全动态地址,不限制是否search固定
Route::rule(':search/:id/:uid','Address/search');
//包含可选参数的地址
Route::rule('find/:id/[:content]', 'Address/find');
//规则完全匹配的地址
Route::rule('search/:id/:uid$','Address/search');
10.路由定义好之后,我们在控制器要创建这个路由地址,可以通过url()方法实现;
//不定义标识的做法
return url('Address/details',['id'=>10]);
//定义标识的做法
Route::rule('details/:id','Address/details')->name('det');
return url('det',['id'=>10]);
不定义标识的做法:
显示把路由放到模板。
定义标识的做法:
========================================================================
要使用Route
类注册路由必须首先在路由定义文件开头添加引用(后面不再重复说明)
use think\facade\Route;
注册路由
最基础的路由定义方法是:
Route::rule('路由表达式', '路由地址', '请求类型');
例如注册如下路由规则(假设为单应用模式):
// 注册路由到News控制器的read操作
Route::rule('new/:id','News/read');
我们访问:
http://serverName/new/5
会自动路由到:
http://serverName/news/read/id/5
并且原来的访问地址会自动失效。
可以在rule
方法中指定请求类型(不指定的话默认为任何请求类型有效),例如:
Route::rule('new/:id', 'News/update', 'POST');
请求类型参数不区分大小写。
表示定义的路由规则在POST
请求下才有效。如果要定义GET
和POST
请求支持的路由规则,可以用:
Route::rule('new/:id','News/read','GET|POST');
不过通常我们更推荐使用对应请求类型的快捷方法,包括:
类型 | 描述 | 快捷方法 |
---|---|---|
GET | GET请求 | get |
POST | POST请求 | post |
PUT | PUT请求 | put |
DELETE | DELETE请求 | delete |
PATCH | PATCH请求 | patch |
HEAD | HEAD请求 | head (V6.0.13+ ) |
* | 任何请求类型 | any |
快捷注册方法的用法为:
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'); // 所有请求都支持的路由规则
注册多个路由规则后,系统会依次遍历注册过的满足请求类型的路由规则,一旦匹配到正确的路由规则后则开始执行最终的调度方法,后续规则就不再检测。
变量规则
一.变量规则
1. 系统默认的路由变量规则为\w+,即字母、数字、中文和下划线;
2. 如果你想更改默认的匹配规则,可以修改config/route.php配置; // 默认的路由变量规则 'default_route_pattern' => '[\w\.]+',
3. 如果我们需要对于具体的变量进行单独的规则设置,则需要通过pattern()方法;
4. 将details方法里的id传值,严格限制必须只能是数字\d+;
Route::rule('details/:id', 'Address/details')->pattern(['id'=>'\d+']);
5. 也可以设置search方法的两个值的规则,通过数组的方式传递参数;
Route::rule('search/:id/:uid', 'Address/search')->pattern([
'id'
=> '\d+',
'uid' => '\d+'
]);
6. 以上两种,均为局部变量规则,也可以直接在app.php设置全局变量规则;
Route::pattern([
'id'
=> '\d+',
'uid' => '\d+'
]);
7. 也支持使用组合变量规则方式,实现路由规则;
Route::rule('details-<id>', 'address/details')->pattern('id', '\d+');
我们路由不一定要/这个符号,也可以其它的。
--------------------------------------------------------------------------
也可以把:id改成<id>
8. 动态组合的拼装,地址和参数如果都是模糊动态的,可以使用如下方法;
Route::rule('details-:name-:id', 'Hello:name/index')->pattern('id', '\d+');
====================================================================
系统默认的变量规则设置是\w+
,只会匹配字母、数字、中文和下划线字符,并不会匹配特殊符号以及其它字符,需要定义变量规则或者调整默认变量规则。
可以在路由配置文件中自定义默认的变量规则,例如增加中划线字符的匹配:
'default_route_pattern' => '[\w\-]+',
支持在规则路由中指定变量规则,弥补了动态变量无法限制具体的类型问题,并且支持全局规则设置。使用方式如下:
局部变量规则
局部变量规则,仅在当前路由有效:
// 定义GET请求路由规则 并设置name变量规则
Route::get('new/:name', 'News/read')
->pattern(['name' => '[\w|\-]+']);
不需要开头添加^
或者在最后添加$
,也不支持模式修饰符,系统会自动添加。
全局变量规则
设置全局变量规则,全部路由有效:
// 支持批量添加
Route::pattern([
'name' => '\w+',
'id' => '\d+',
]);
组合变量
如果你的路由规则比较特殊,可以在路由定义的时候使用组合变量。
例如:
Route::get('item-<name>-<id>', 'product/detail')
->pattern(['name' => '\w+', 'id' => '\d+']);
组合变量的优势是路由规则中没有固定的分隔符,可以随意组合需要的变量规则和分割符,例如路由规则改成如下一样可以支持:
Route::get('item<name><id>', 'product/detail')
->pattern(['name' => '[a-zA-Z]+', 'id' => '\d+']);
Route::get('item@<name>-<id>', 'product/detail')
->pattern(['name' => '\w+', 'id' => '\d+']);
使用组合变量的情况下如果需要使用可选变量,则可以使用下面的方式:
Route::get('item-<name><id?>', 'product/detail')
->pattern(['name' => '[a-zA-Z]+', 'id' => '\d+']);
动态路由
可以把路由规则中的变量传入路由地址中,就可以实现一个动态路由,例如:
// 定义动态路由
Route::get('hello/:name', 'index/:name/hello');
name
变量的值作为路由地址传入。
动态路由中的变量也支持组合变量及拼装,例如:
Route::get('item-<name>-<id>', 'product_:name/detail')
->pattern(['name' => '\w+', 'id' => '\d+']);
路由地址
一.路由地址 1. 路由的地址一般为:控制器/操作方法构成;
//默认Index控制器
Route::rule('/', 'index');
//控制器/操作方法
Route::rule('details/:id', 'Address/details');
2. 支持多级控制器,并且支持路由到相应的地址;
//目录为:app\controller\group
namespace app\controller\group;
//地址为:app\controller\group
http://localhost:8000/group.blog/details/id/5
//支持多级路由
Route::rule('details/:id', 'group.Blog/details');
3. 对于地址,还有一种完整路径的方式去执行操作方法:完整类名@操作方法;
4. 另一种静态方式:完整路径支持这种方法的路由地址:完整类名::静态方法;
Route::rule('ds/:id', '\app\controller\Address@details');
5. 路由可以通过::redirect()方法实现重定向跳转,第三参数为状态码;
Route::redirect('ds/:id', 'http://localhost/', 302);
路由到控制器/操作
这是最常用的一种路由方式,把满足条件的路由规则路由到相关的控制器和操作,然后由系统调度执行相关的操作,格式为:
控制器/操作
解析规则是从操作开始解析,然后解析控制器,例如:
// 路由到blog控制器
Route::get('blog/:id','Blog/read');
Blog类定义如下:
<?php
namespace app\index\controller;
class Blog
{
public function read($id)
{
return 'read:' . $id;
}
}
路由地址中支持多级控制器,使用下面的方式进行设置:
Route::get('blog/:id','group.Blog/read');
表示路由到下面的控制器类,
index/controller/group/Blog
还可以支持路由到动态的应用、控制器或者操作,例如:
// action变量的值作为操作方法传入
Route::get(':action/blog/:id', 'Blog/:action');
路由到类的方法
这种方式的路由可以支持执行任何类的方法,而不局限于执行控制器的操作方法。
路由地址的格式为(动态方法):
\完整类名@方法名
或者(静态方法)
\完整类名::方法名
例如:
Route::get('blog/:id','\app\index\service\Blog@read');
执行的是 \app\index\service\Blog
类的read
方法。
也支持执行某个静态方法,例如:
Route::get('blog/:id','\app\index\service\Blog::read');
重定向路由
可以直接使用redirect
方法注册一个重定向路由
Route::redirect('blog/:id', 'http://blog.thinkphp.cn/read/:id', 302);
路由到模板
支持路由直接渲染模板输出。
// 路由到模板文件
Route::view('hello/:name', 'index/hello');
表示该路由会渲染当前应用下面的view/index/hello.html
模板文件输出。
模板文件中可以直接输出当前请求的param
变量,如果需要增加额外的模板变量,可以使用:
Route::view('hello/:name', 'index/hello', ['city'=>'shanghai']);
在模板中可以输出name
和city
两个变量。
Hello,{$name}--{$city}!
路由到闭包
我们可以使用闭包的方式定义一些特殊需求的路由,而不需要执行控制器的操作方法了,例如:
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;
});
规则路由中定义的动态变量的名称 就是闭包函数中的参数名称,不分次序。
因此,如果我们访问的URL地址是:
http://serverName/hello/thinkphp
则浏览器输出的结果是:
Hello,thinkphp
依赖注入
可以在闭包中使用依赖注入,例如:
Route::rule('hello/:name', function (Request $request, $name) {
$method = $request->method();
return '[' . $method . '] Hello,' . $name;
});
路由到调度对象(V6.0.3+
)
// 路由到自定义调度对象
Route::get('blog/:id',\app\route\BlogDispatch::class);
namespace app\route;
use think\route\Dispatch;
use think\route\Rule;
use think\Request;
class BlogDispatch extends Dispatch
{
public function exec()
{
// 自定义路由调度
}
}
具体调度类的实现可以参考内置的几个调度类的实现。
路由分组
1. 路由分组,即将相同前缀的路由合并分组,这样可以简化路由定义,提高匹配效率;
2. 使用group()方法,来进行分组路由的注册;
Route::group('address', function () {
Route::rule(':id', 'Address/details');
Route::rule(':name', 'Address/search');
})->ext('html')->pattern(['id'=>'\d+', 'name'=>'\w+']);
pattern
方法能够为路由参数指定正则表达式匹配规则,以此确保只有符合特定规则的参数值才能匹配对应的路由。
'id'=>'\d+'
:规定 id
参数只能是一个或多个数字。这意味着,只有当 id
为纯数字时,ds/:id
这条路由规则才会匹配成功。例如,ds/123.html
可以匹配,但 ds/abc.html
就无法匹配。
'name'=>'\w+'
:规定 name
参数只能是一个或多个单词字符(包含字母、数字和下划线)。所以,sr/john.html
能够匹配,而 sr/@#$.html
则不能匹配。
3. 也可以省去第一参数,让分组路由更灵活一些;
Route::group(function () {
Route::rule('ds/:id', 'Address/details');
Route::rule('sr/:name', 'Address/search');
})->ext('html')->pattern(['id'=>'\d+', 'name'=>'\w+']);
4. 使用prefix()方法,可以省略掉分组地址里的控制器;
Route::group('address', function () {
Route::rule(':id', 'details');
Route::rule(':name', 'search');
})->ext('html')->prefix('Address/')->pattern(['id'=>'\d+', 'name'=>'\w+']);
5. 使用append()方法,可以额外传入参数;
Route::group()...->append(['status'=>1]);
6. 路由规则(主要是分组和域名路由)定义的文件,加载时会解析消耗较多的资源;
7. 尤其是规则特别庞大的时候,延迟解析开启让你只有在匹配的时候才会注册解析;
8. 我们在route.php中开启延迟解析,多复制几组规则,然后来查看内存占用;
'url_lazy_route'
=> true,
MISS路由
1. 全局MISS,类似开启强制路由功能,匹配不到相应规则时自动跳转到MISS;
Route::miss('public/miss');
2. 分组MISS,可以在分组中使用miss方法,当不满足匹配规则时跳转到这里;
Route::miss('miss');
====================================================================
路由分组功能允许把相同前缀的路由定义合并分组,这样可以简化路由定义,并且提高路由匹配的效率,不必每次都去遍历完整的路由规则(尤其是开启了路由延迟解析后性能更佳)。
使用Route
类的group
方法进行注册,给分组路由定义一些公用的路由设置参数,例如:
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+']);
路由分组支持嵌套,例如:
Route::group(function () {
Route::group('blog', function () {
Route::rule(':id', 'blog/read');
Route::rule(':name', 'blog/read');
});
})->ext('html')->pattern(['id' => '\d+', 'name' => '\w+']);
如果使用了嵌套分组的情况,子分组会继承父分组的参数和变量规则,而最终的路由规则里面定义的参数和变量规则为最优先。
可以使用prefix
方法简化相同路由地址的定义,例如下面的定义
Route::group('blog', function () {
Route::get(':id', 'blog/read');
Route::post(':id', 'blog/update');
Route::delete(':id', 'blog/delete');
})->ext('html')->pattern(['id' => '\d+']);
可以简化为
Route::group('blog', function () {
Route::get(':id', 'read');
Route::post(':id', 'update');
Route::delete(':id', 'delete');
})->prefix('blog/')->ext('html')->pattern(['id' => '\d+']);
路由完全匹配
如果希望某个分组下面的路由都采用完全匹配,可以使用
Route::group('blog', function () {
Route::get(':id', 'read');
Route::post(':id', 'update');
Route::delete(':id', 'delete');
})->completeMatch()->prefix('blog/')->ext('html')->pattern(['id' => '\d+']);
延迟路由解析
支持延迟路由解析,也就是说你定义的路由规则(主要是分组路由和域名路由规则)在加载路由定义文件的时候并没有实际注册,而是在匹配到路由分组或者域名的情况下,才会实际进行注册和解析,大大提高了路由注册和解析的性能。
默认是关闭延迟路由解析的,你可以在路由配置文件中设置:
// 开启路由延迟解析
'url_lazy_route' => true,
开启延迟路由解析后,如果你需要生成路由反解URL,需要使用命令行指令
php think optimize:route
来生成路由缓存解析。
通过路由分组或者域名路由来定义路由才能发挥延迟解析的优势。
一旦开启路由的延迟解析,将会对定义的域名路由和分组路由进行延迟解析,也就是说只有实际匹配到该域名或者分组后才会进行路由规则的注册,避免不必要的注册和解析开销。
路由规则合并解析
同一个路由分组下的路由规则支持合并解析,而不需要遍历该路由分组下的所有路由规则,可以大大提升路由解析的性能。
对某个分组单独开启合并规则解析的用法如下
Route::group('user', function () {
Route::rule('hello/:name','hello');
Route::rule('think/:name','think');
})->mergeRuleRegex();
这样该分组下的所有路由规则无论定义多少个都只需要匹配检查一次即可(实际上只会合并检查符合当前请求类型的路由规则)。
mergeRuleRegex
方法只能用于路由分组或者域名路由(域名路由其实是一个特殊的分组)。
或者在路由配置文件中设置开启全局合并规则(对所有分组有效)
// 开启路由合并解析
'route_rule_merge' => true,
传入额外参数
可以统一给分组路由传入额外的参数
Route::group('blog', [
':id' => 'Blog/read',
':name' => 'Blog/read',
])->ext('html')
->pattern(['id' => '\d+'])
->append(['group_id' => 1]);
上面的分组路由统一传入了group_id
参数,该参数的值可以通过Request
类的param
方法获取。
指定分组调度
V6.0.8+
版本开始,可以给路由分组单独指定调度类,例如:
Route::group('blog', [
':id' => 'Blog/read',
':name' => 'Blog/read',
])->dispatcher(GroupDispatcher::class);