# 路由初体验 一个典型的`ThinkPHP`应用的URL请求的执行过程通常是: >[info]### 用户请求 -> 路由解析 -> 调度请求 -> 执行操作 -> 响应输出 路由在框架中的作用打个比方的话,路由好比是WEB应用的总调度室,对于访问的URL地址,路由可以拒绝或者接受某个URL请求,并进行分发调度,而且还有一个副作用是因为路由规则可以随意定义,因此可以让你的URL请求地址更优雅,因为不会暴露实际的URL地址,也就意味着更安全,5.0的路由不仅仅只是支持路由到控制器的操作方法,甚至可以路由到任何的类或者闭包。 和`Laravel`等不同,框架本身不强制定义路由,默认情况下的如果没有定义任何路由规则或者没有匹配到任何的路由规则,则按照模块/控制器/操作的URL规范来解析(也就是系统默认的解析规则,这点看过`ThinkPHP5.0`快速入门的用户已经很清楚了,在此就不再多说)。 > 在本文后面的示例中,为了方便讲解,我们统一设置`vhost`访问,以`apache`为例的话定义如下(其它环境请自行百度): > ~~~ > <VirtualHost *:80> > DocumentRoot "/home/www/tp5/public" > ServerName tp5.com > </VirtualHost> > ~~~ > 把DocumentRoot修改为你本机tp5的`public`目录,并注意修改本机的`hosts`文件把`tp5.com`指向本地`127.0.0.1`。 在没有定义任何的路由规则之前,你的URL访问地址可能会是下面这样: ~~~ http://tp5.com/module/controller/action/name/value ~~~ > 如果你还不了解如何隐藏URL地址中的`index.php`可以参考官方开发手册的[URL重写](http://www.kancloud.cn/manual/thinkphp5/177576)。 如果需要定义应用的路由规则,直接在`application/route.php`文件中添加路由规则即可(虽然你也可以在应用的公共文件中定义路由,但并不建议,后面会理解为什么)。 > 如果你自定义了`CONF_PATH`常量的话,路由配置文件的位置可能有所区别,请自行调整。 >[danger]### 【5.1须知】 > * * * * * > `5.1`版本的路由目录位于根目录下的`route`目录,在该目录下的任何一个路由定义文件都是有效的。通常可以直接在`route/route.php`中定义路由。 本节用一个`Hello,world!`的例子来体验下路由的使用,定义一个控制器类命名为`application/index/controller/Index.php`: ~~~ <?php namespace app\index\controller; class Index { public function hello() { return 'Hello,World!'; } } ~~~ 要访问`Index`控制器的`hello`操作方法的话,没有定义路由规则之前,访问的URL地址应该是: ~~~ http://tp5.com/index/index/hello ~~~ 访问后浏览器输出结果为: ~~~ Hello,World! ~~~ 下面我们来定义一个路由先: ~~~ use think\Route; Route::rule('hello','index/Index/hello'); ~~~ 路由定义文件的开头需要加上下面一句表示引入Route类(后面不再强调) ~~~ use think\Route; ~~~ >[danger]### 【5.1须知】 > * * * * * > 路由定义文件开头不需要添加 `use think\Route;` > 定义后,我们就可以直接访问下面的地址: ~~~ http://tp5.com/hello ~~~ 访问后浏览器输出结果和之前是一样的。 > 如果你够细心的话,会发现原来的URL地址`http://tp5.com/index/index/hello`已经禁止访问,这是由于5.0遵循唯一的URL地址设计。 很多时候操作方法都会有变量传入需要,例如给`hello`方法添加一个`name`变量,方法代码修改如下: ~~~ <?php namespace app\index\controller; class Index { public function hello($name) { return 'Hello,' . $name . '!'; } } ~~~ 原来的URL地址变成是(记得先删除前面定义的路由规则,否则会禁止访问下面的URL地址): ~~~ http://tp5.com/index/index/hello/name/thinkphp ~~~ 访问后浏览器输出结果为: ~~~ Hello,thinkphp! ~~~ 现在给该URL地址定义一个新的路由规则如下: ~~~ Route::rule('hello/:name','index/Index/hello'); ~~~ 现在我们来分析下`rule`方法的参数,第一个参数称为路由规则(通过路由访问的地址),第二个参数为该规则对应的路由地址(也就是原来定义路由之前访问的URL地址)。 路由规则通常可以包含变量(例如其中的`:name`就是一个路由变量),路由规则中包含变量(包括可选变量)的就称该条路由规则为动态路由,没有包含任何变量的路由我们称之为静态路由。 前面我们定义过两条路由规则就分别是静态路由和动态路由: ~~~ // 静态路由规则 Route::rule('hello','index/Index/hello'); // 动态路由规则 Route::rule('hello/:name','index/Index/hello'); ~~~ > 静态路由和动态路由的解析过程是完全不同的,简单点说的话,动态路由需要遍历所有规则依次匹配(除非第一条就匹配成功),是一个多次匹配的过程,而静态路由是一次快速匹配(当然特殊情况下也会有静态路由无效的情况)。 定义好路由规则后,URL地址就可以简化为: ~~~ http://tp5.com/hello/thinkphp ~~~ 路由规则中的静态规则部分不区分大小写,所以 ~~~ Route::rule('Hello/:name','index/index/hello'); ~~~ 和前面定义的路由是相同的,虽然是两条不同的路由规则。 我们知道一个URL请求类型有很多,常用的包括`GET`/`POST`/`PUT`/`DELETE`等,我们使用`rule`方法注册的路由,默认是支持任意请求类型访问的,不过你可以通过第三个参数来限定请求类型: ~~~ Route::rule('hello/:name','index/index/hello','GET'); ~~~ 则表示,只有通过`GET`请求的访问,该路由才会生效。 为了方便定义,系统给每个请求类型的路由规则增加了独立的方法,所以你可以使用下面的方法注册一条相同的路由: ~~~ Route::get('hello/:name','index/index/hello'); ~~~ 如果你希望路由可以支持所有的请求类型,也可以使用: ~~~ Route::any('hello/:name','index/index/hello'); ~~~ `any`方法其实和`rule`方法是一样的,区别在于不用写第三个参数。 ThinkPHP5支持的请求类型包括 ~~~ ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] ~~~ 不过只支持了 `get`/`post`/`put`/`delete`/`patch` 常用请求类型的路由注册方法,其它两个类型由于不常用,需要自己使用`rule`方法注册。 一个路由规则可以设置匹配的条件参数(其实请求类型也是一个匹配条件,单独出来一个参数是从性能考虑),匹配条件是一个数组,可以包含多个匹配条件,例如: ~~~ Route::any('hello/:name', 'index/index/hello', [ 'ext' => 'html', 'method' => 'get', ]); ~~~ 上面的路由规则表示,必须是`GET`请求访问,并且URL后缀为`html`的地址。 >[danger]### 【5.1须知】 > * * * * * > 上面的路由可以改成更清晰的方式 > ~~~ > Route::any('hello/:name', 'index/index/hello') > ->ext('html') > ->method('get'); > ~~~ 因此,访问下面的URL地址就会抛出异常 ~~~ http://tp5.com/hello/thinkphp ~~~ 正确的URL地址应该是: ~~~ http://tp5.com/hello/thinkphp.html ~~~ > 如果没有定义路由的后缀参数,表示路由规则本身不限制URL后缀访问(但仍然受应用配置参数`url_html_suffix`影响)。 更多的路由参数后续还会提到,除了路由参数外,我们还可以对路由变量设置规则,这个时候就需要用到第四个参数。 ~~~ Route::any('hello/:name', 'index/index/hello', [ 'ext' => 'html', 'method' => 'get', ], ['name' => '[A-Za-z0-9]+']); ~~~ >[danger]### 【5.1须知】 > * * * * * > 可以更直观的改为 > ~~~ > Route::any('hello/:name', 'index/index/hello') > ->ext('html') > ->method('get') > ->pattern(['name' => '[A-Za-z0-9]+']); > ~~~ > 这里定义了路由变量`name`的变量规则(正则表达式),在实际正则表达式检测的时候,最终的正式表达式其实是 `/^[A-Za-z0-9]+$/`,系统会自动加上起始和结束符号。当URL里面的变量不符合正则表达式匹配要求的时候,路由规则就不会生效,因此下面的URL地址访问就会出错: ~~~ http://tp5.com/hello/think-php ~~~ 对于`any`方法定义的路由规则,系统支持直接在路由配置文件中直接返回数组的方式定义,例如: ~~~ return [ 'hello/:name' => ['index/index/hello', ['ext' => 'html', 'method' => 'get'], ['name' => '[A-Za-z0-9]+']], ]; ~~~ 如果没有路由参数和变量规则定义的话,可以直接使用: ~~~ return [ 'hello/:name' => 'index/index/hello', ]; ~~~ >[danger]### 【5.1须知】 > * * * * * > 5.1不推荐使用返回数组定义路由的方式,请尽量使用方法注册路由 > 通常,我们把这种方式的路由注册称之为静态注册,反之,使用Route类的方法注册的我们称之为动态注册。 虽然静态注册也能满足需求,但因为静态注册路由存在一定的局限性(不够灵活,而且会影响路由检测的性能),本指南大多数情况下都使用动态注册路由的方式讲解,也是5.0推荐的方式。 当然,路由方法注册和配置定义可以同时使用: ~~~ <?php use think\Route; Route::get('blog/:name', 'index/blog/read'); return [ 'hello/:name' => 'index/index/hello', ]; ~~~ 路由配置文件定义的路由规则,会由`Route`类的`import`方法完成注册过程(换句话说,你也可以调用`import`方法批量注册路由规则)。 现在你已经了解如何定义一个路由规则了,你已经开启了路由这扇大门,但是路由之旅才刚刚开始^_^ ## 小结 回想下,你现在是否已经明白了静态路由和动态路由的区别,以及什么是静态注册和动态注册路由。 再复杂的路由也是由最基本的路由规则、路由地址、路由参数和变量规则几个部分组成,所谓万变不离其宗,复杂的路由只是用了一些更多的技巧来完成注册或者更方便的使用,我们来复习下路由的几个概念: >[info]### Route::rule('路由规则','路由地址','请求类型','路由参数(数组)','变量规则(数组)'); **路由规则**:URL访问规则(包括静态规则和动态规则),只有符合规则的路由才能正确访问; **路由地址**:实际访问的地址(可以是控制器操作、类的方法或者闭包); **请求类型**:表示当前路由生效使用的请求类型,包括`GET`/`POST`/`PUT`/`DELETE`等,如果希望任何请求都能访问使用`*`号(默认值)。 **路由参数**:路由匹配的条件约束或设置参数(用于检测或者解析); **路由变量**:路由规则里面的动态变量以及`PATH_INFO`里面的参数都称之为路由变量; **变量规则**:路由规则中的变量的匹配规则(正则表达式); 下一节我会给大家讲解路由的执行流程。