前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Laravel系列3.2】路由:指哪儿打哪儿

【Laravel系列3.2】路由:指哪儿打哪儿

作者头像
硬核项目经理
发布2023-03-03 13:08:52
11.7K0
发布2023-03-03 13:08:52
举报

路由:指哪儿打哪儿

在没有接触到 Laravel 之前,早先的框架基本上都没有这个完整的路由的概念。像是 TP3 、Yii1 版本的时候,都是通过入口文件对参数的解析来加载指定的 控制器 。关于这种形式的加载方式大家可以在去看一下老的这些框架是如何实现的。而在 Laravel 中,从我开始接触的时候,就使用的是自定义路由的方式来指定请求的路径。其实,传统方式可以看做是一种隐式路由,而我们需要写的这种是一种显式的路由。Laravel 也是支持隐式路由的,只是它并不推荐这么做。那么,显式路由有什么好处呢?

  • Restful 风格的 URL
  • 多个 URI 指向同一个控制器(可以在路由中区分参数)
  • URI 和 控制器 解耦,自由定义名称,不受控制器里面方法名的限制
  • 很多场景都不需要控制器,比如一个静态页面,直接路由里就可以搞定了
  • 有规范、有约定,路由配置文件写上注释就是个文档

当然,也不能说使用这种显式路由全是好处,每个请求都得过来写一行路由就是它最大的麻烦,而且这玩意多了一层路由的控制,性能自然也没有老框架里面的直接路由来得好。而且团队开发的时候,如果没有拆分路由文件的话,很容易出现冲突。事物总是有两面性的,既然现在 TP5 、 Yii2 都已经在推荐这种显式路由的方式了,那么可以看出,大家在实际的工作中还是更接受显式路由的这些优点的。

另外,在更新的一些框架中,比如需要搭配 Swoole 的 Hyperf 框架中,已经支持 注解路由 这种形式了。大家有兴趣的可以去看一下,这个也是越来越靠近 Java 的一种写法。而且个人使用之后感觉这种方式也很爽。

基本路由配置

简单地介绍一下基本的路由配置,我们可以指定路由的请求,比如使用 GET 还是 POST 。

代码语言:javascript
复制
Route::get('/get/request', function(){
    return 'get';
});

Route::post('/post/request', function(){
    return 'post';
});

除了常用的这两个之外,完整的 RESTful 格式请求也是支持的,比如 PUT 、DELETE 之类的请求。

代码语言:javascript
复制
Route::put('/put/request', function(){
    return 'put';
});

如果你在 POST 的路由上,使用 GET 的方式来进行访问的话,就会触发异常信息,就像下图这样。

从这里也能够看出,Laravel 的路由对于数据安全的好处。大部分情况下,我们使用的接口都会以 POST 为主,特别是数据提交的接口。如果是传统框架没有加任何判断的情况下,也是可以直接请求到控制器的,只是说我们从 _POST 中无法获取数据而已。这种情况下,如果处理得当一般也不会有什么问题,但有可能也会造成意外的数据泄露,比如说万一我们使用 _REQUEST 来接收数据。在 Laravel 中路由处理的时候,就会将这个问题给避免了。当然,如果你想让一个请求直接使用任何方式都可以请求,也可以直接在路由中配置。

代码语言:javascript
复制
Route::any('/any/request', function(){
    return 'any';
});

Route::match(['get', 'post'], '/match/request', function(){
    return 'match get or post';
});

any() 就是接收任意方式的请求,而 match() 则是接收指定数组内部的请求。

请求参数 RESTful 风格配置

对于请求的参数,我们可以使用依赖注入的 Request 来获取,不过还有另外一种方式,可以获取 RESTful 风格的参数,也就是 URL Path 风格的参数。

代码语言:javascript
复制
Route::get('/get/request/{id}/{name?}', function($id, $name=''){
    return 'get:' . $id . ', ' . $name;
})->where(['id'=>'[0-9]+', 'name'=>'[a-z]+']);

// http://laravel8/get/request/1/a
// http://laravel8/get/request/1

// http://laravel8/get/request/1/1 404

在这个路由中,我们指定了两个参数,一个是 id 一个是 name ,当使用 /get/request/1/a 或者 /request/1 这两种形式的链接访问的时候,都是可以正常访问的,但使用 /get/request/1/1 则无法访问。name 参数后面的冒号表明这个参数是一个可选的参数,而 where 中则指定了参数的规则,比如 id 必须是数字,而 name 必须是小写的 a 到 z 。注意,这个 where 里面的是正则表达式,它还有一些其它的类似函数可以方便地指定规则而不需要我们手写正则,这个大家可以自行查阅相关的文档。

路由命名与跳转

在原始的 PHP 中,如果我们需要跳转链接,一般使用的是 header() 方法,并在参数里使用 Location:url 这种方式。在 Laravel 中,可以比较方便地在路由中实现跳转。

代码语言:javascript
复制
Route::get('/get/request/{id}/{name?}', function($id, $name=''){
    return 'get:' . $id . ', ' . $name;
})->name('get/request/params')->where(['id'=>'[0-9]+', 'name'=>'[a-z]+']);

Route::get('/jump', function(){
    $url = route('get/request/params', ['id'=>2, 'name'=>'b']);
    echo $url; // http://laravel8/get/request/2/b
    return redirect()->route('get/request/params', ['id'=>2, 'name'=>'b']);
});

在这段代码中,我们先给之前测试的那个带参数的路由命名,使用的是 name() 这个方法。然后使用 redirect()->route() 方法就可以实现路由的跳转重定向。其中直接使用 route() 方法可以生成一个指定路由的链接字符串。如果需要使用 header() 方法的话,直接使用这个生成的链接字符串就可以了。

路由组配置

什么是路由组呢?比如我们有一组链接,都归属于同一个资源下,比如下面这几个链接:

代码语言:javascript
复制
http://laravel8/temp/
http://laravel8/temp/{id}
http://laravel8/temp/edit
http://laravel8/temp/delete
http://laravel8/temp/insert

就可以把这五个链接都放到 temp 这个资源路径下,一般这种类似的资源也会只使用一个控制器。这种情况下,我们就可以使用路由组来规范管理这一组链接。

代码语言:javascript
复制
Route::group(['prefix'=>'temp'], function(){
    Route::get('/', function(){
        return '根列表';
    });
    Route::get('/{id}', function($id){
        return '详情页,id:' . $id;
    });
    Route::post('/insert', function(){
        return '添加';
    });
    Route::put('/edit', function(){
        return '修改';
    });
    Route::delete('/delete', function(){
        return '删除';
    });
});

路由与控制器、模型

对于路由操作来说,我们在日常开发中其实很少会在路由文件中写代码,真实的情况其实是路由去指向控制器,实现控制器和路由的关联。这样实现的最大好处其实就是路由和控制器的解耦,也就是前面所说过的路由的好处之一。传统框架中的控制器就是路由,如果想要动态地改变这个路由链接,那么要么改控制器的名字,要么去 nginx 上配置转发重写,都远不如框架中自带路由来得方便快捷。

首先,我们使用在 Http/Controller 下面创建一个控制器 RouteController 。

代码语言:javascript
复制
namespace App\Http\Controllers;


class RouteController extends Controller
{
    public function test($id){
        return 'test ' . $id;
    }
}

然后在路由文件中定义指向这个控制器的路由。

代码语言:javascript
复制
Route::get('route/test/{id}', 'App\Http\Controllers\RouteController@test');
// http://laravel8/route/test/1

Route::get('route/t/{id}', 'App\Http\Controllers\RouteController@test');
// http://laravel8/route/t/1

这样就实现了路由和控制器的关联。当然,这个链接名我们怎么写都可以了,而且都指向同一个控制器中的方法也没有什么问题。需要注意的这个控制器写的格式是完整的 命名空间 名,然后接一个 @ 符号后面是方法名。当然,如果一个控制器是一个 RESTful 资源文件的话,直接使用 Route::resource() 去指向一个控制器就可以了,可以省略掉方法名,这种方式在文档中叫做 资源型控制器 。这个功能大家可以自己参考文档实现,我们也将在下篇文章学习控制器的时候进行简单的演示。

除了字符串的写法外,还可以使用类名的方式。

代码语言:javascript
复制
Route::get('route/tt/{id}', [\App\Http\Controllers\RouteController::class, 'test']);
// http://laravel8/route/tt/1

路由与模型的绑定

对于模型来说,我们也可以直接进行路由的关联,不过这种操作也是比较少见的。

代码语言:javascript
复制
Route::get('route/user/{user}', function(\App\Models\User $user){
    return $user->name;
});
// http://laravel8/route/user/1
// Erwin Ortiz

上述代码中,我们需要参数名称和模型对象的参数名称相同,然后在 URL 中传递对应数据的 ID ,这样就可以直接查询到模型对象对应的数据信息。对于这个功能,大家也是以了解为主,实际的业务开发中,除了真的只是要一些简单的数据,否则不会直接通过路由就去操作模型,毕竟少了控制器的一些参数整合过滤,还是略有一些风险的。

路由分析

在研究路由的源码路径之前,我们先看一个命令。

代码语言:javascript
复制
php artisan route:list 

它的作用是使用呢?可以看到我们现在已经定义的路由都有哪些。

接下来,我们以 http://laravel8/temp/ 这个链接为例,使用调试工具看一下路由是如何调用分派的。

首先当然还是请求的封装,也就是我们上篇文章中的 Request 对象的生成。接下来,使用这个 Request 对象,并根据它的 pathInfo 属性来找到对应的路由信息。

通过 laravel/framework/src/Illuminate/Foundation/Http/Kernel.php 内核文件中 dispatchToRouter() 方法,开始进入路由分派的操作。

dispatchToRouter() 方法中的 this->router->dispatch(

这个 \this->routes->match() 的对象是 laravel/framework/src/Illuminate/Routing/RouteCollection.php 文件中的这个 RouteCollection 类,其实也就是调用的这个类的 match() 方法。在 match() 方法里面,

代码语言:javascript
复制
// laravel/framework/src/Illuminate/Routing/RouteCollection.php
public function match(Request $request)
{
    $routes = $this->get($request->getMethod());

    // First, we will see if we can find a matching route for this current request
    // method. If we can, great, we can just return it so that it can be called
    // by the consumer. Otherwise we will check for routes with another verb.
    $route = $this->matchAgainstRoutes($routes, $request);

    return $this->handleMatchedRoute($request, $route);
}

接着,这个 route 对象继续向上并通过一系列的中间件管道来到 laravel/framework/src/Illuminate/Routing/Route.php 文件的 run() 方法中,在这个方法里面,框架会判断这个路由是走控制器还是走回调函数。在这里我们定义的路由走的是回调函数。

代码语言:javascript
复制
// laravel/framework/src/Illuminate/Routing/Route.php
public function run()
{
    $this->container = $this->container ?: new Container;

    try {
        if ($this->isControllerAction()) {
            return $this->runController();
        }

        return $this->runCallable();
    } catch (HttpResponseException $e) {
        return $e->getResponse();
    }
}

也就是走的 return $this->runCallable(); 这一行。接下来,就回到路由文件中 temp 这个路由的回调函数中。之后就是响应的输出了。

整个路由功能的调用路径就是这样,其实相对来说没有请求响应的路径长,毕竟它只是请求响应路径中的一部分而已。

总结

关于路由还有很多可以配置的技巧与功能,在这里就不一一列出了,毕竟我们是以应用和源码分析为主,很多小技巧其实也并不是很常用。比如说路由绑定模型这个就从来没有用过,但是,存在即合理,总会有它的应用场景,否则框架的作者也不会保留着这个功能,您说是不是。

参考文档:

https://learnku.com/docs/laravel/8.x/routing/9365

https://learnku.com/laravel/t/3614/similar-to-the-laravel-framework-such-explicit-routing-is-really-good#reply1

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农老张 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 路由:指哪儿打哪儿
    • 基本路由配置
      • 请求参数 RESTful 风格配置
      • 路由命名与跳转
    • 路由组配置
      • 路由与控制器、模型
        • 路由与模型的绑定
      • 路由分析
        • 总结
        相关产品与服务
        消息队列 TDMQ
        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档