Laravel学习教程之路由模块


Posted in PHP onAugust 18, 2017

前言

本文主要给大家介绍的是关于Laravel路由模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

备注:本文是基于Laravel 5.4版本的路由模块代码进行分析书写;

模块组成

下图展示了路由模块中各个文件的关系,并进行简要说明;

Laravel学习教程之路由模块

剖析

服务提供者

看Laravel模块,首先找ServiceProvider文件,这是模块与IOC容器交互的入口,从这个文件,可以看出该模块提供向系统提供了哪些服务;

public function register() {
 // 注册路由管理,提供路由注册,路由匹配的功能
 $this->registerRouter();
 // 注册 Url 生成器实例
 $this->registerUrlGenerator();
 // 注册跳转器
 $this->registerRedirector();
 // 绑定 PSR-7 请求实现到 ServerRequestInterface 接口
 $this->registerPsrRequest();
 // 绑定 PSR-7 Response 实现到 ResponseInterface 接口
 $this->registerPsrResponse();
 // 注册 ReponseFactory,提供各式各样的 Response,比如视图响应、Json响应、Jsonp响应、文件下载等
 $this->registerResponseFactory();
}

路由管理

“路由管理”服务有以下元素需要了解:

  • Route:路由;会记录 Url、Http 动作、Action (路由要执行的具体对象,可能是 Closure,也可以是某个 Controller 中的方法),路由参数,路由参数的约束;
  • RouteCollection:路由集,用来存储所有Route对象的“盒子”;
  • RouteGroup:路由组;只有路由注册过程中会临时用到;存储一批路由公共的一些属性,属性包括domain、prefix、as、middleware、namespace、where;
  • Resource:资源路由;资源路由是一套路由的统称,包含列表(index)、显示增加(create)、保存增加(store)、显示详情(show)、显示编辑详情(edit)、更新编辑(update)、删除详情(destory);同时可以通过调用only或except方法或参数的形式只生成部分路由;
  • Action:路由要执行的对象;有两种表现形式,一是Closure函数,二是类似['uses' => 'FooController@method', 'as' => 'name']这样的字符串;对于不同的表现形式,路由在执行时会调用不同的处理;

注册流程

在项目启动后,会执行所有ServiceProvider的loadRoutes方法,也就是调用map方法,一般情况下map方法如下

public function map(Router $router){
 require __DIR__.'/routes.php';
}

这时候,项目就会执行很多Route::getRoute::postRoute::group方法;

当遇到Route::group方法时,会实例化一个RouteGroup对象,put进Router管理类的路由组栈头部;而后当执行get、post这类具体的注册路由方法时,会把当前路由组栈中所有组的属性合并进新路由中,将新路由存储在RouteCollection这个大盒子里;当Route::group的Closure执行完毕时,会把头部的RouteGroup实例pull出去;

当执行Route::resource时,Router管理类会调用ResourceRegister类来完成批量注册路由;

对于 Router::get这类注册方法,Illuminate\Foudation\helpers提供了简写;

  • Router::get 简化成 get,
  • Router::post 简化成 post,
  • Router::put 简化成 put,
  • Router::patch 简化成 patch,
  • Router::delete 简化成 delete,
  • Router::resource简化成 resource,

至此,RouteCollection大盒子就存放了所有要注册的路由;

request 请求匹配流程

首先,request请求会经过Foundation/Http/Kernel的handle方法,在这个方法中,请求会执行以下语句

$this->router->dispatch($request)

这里的$this->router,就是Router管理类;dispatch方法如下

public function dispatch(Request $request) {
 $this->currentRequest = $request;
 return $this->dispatchToRoute($request);
}

public function dispatchToRoute(Request $request) {
 // 根据请求的 url 找到匹配的路由
 $route = $this->findRoute($request);
 // 将路由绑定到请求上
 $request->setRouteResolver(function () use ($route) {
 return $route;
 }
 // 触发 RouteMatched 事件
 $this->events->dispatch(new Events\RouteMatched($route, $request));
 // 通过 Pipeline 流水线执行路由上绑定的中间件及对应的方法
 $response = $this->runRouteWithinStack($route, $request);
 // 根据 request 请求设置 response 的响应头
 return $this->prepareResponse($request, $response);
}

1、根据请求找匹配的路由

`RouteCollection`根据请求的`http`动作缩小要匹配的路由范围;在筛选出来的这些路由中依次遍历,找出第一个符合验证的路由(需要进行较验的验证在`Route`中的`getValidators`方法中声明);

2、将路由绑定到请求上

3、触发RouteMatched事件

初始化的`Laravel`项目没有对`RouteMatched`路由匹配事件进行任何的监听器绑定,如有需要,可以自定义监听器,在模块的`EventServiceProvider`中注册该事件监听;这样一旦请求匹配上某个路由,就可以执行自定义方法了;

4、通过 Pipeline 流水线执行路由上绑定的中间件及对应的方法

在`runRouteWithinStack`方法中,系统会判断是否需要执行中间件,如果`IOC`容器中设置了`middleware.disable`的值为`true`,则需要执行的中间件数组为空;否则会找到所有的中间件,并按照`middlewarePriority`对必要的一些中间件进行排序调整;然后执行`$route->run()`方法;

5、根据 request 请求设置 response 的响应头

项目中会用到的一些方法

  • 获取路由集合 app('router')->getRoutes()
  • 获取当前的请求 $request = app('router')->getCurrentRequest()
  • 获取当前请求所对应的路由 $route = $request->route() 或 $route = app('router')->getCurrentRoute()
  • 获取当前路由需要执行的中间件 $middlewares = app('router')->gatherRouteMiddleware($route)

Url 生成器

Url 生成器是什么?

举个例子,

$url = new UrlGenerator(
 $routes = new RouteCollection,
 $request = Request::create('http://www.foo.com/')
);

$url->to('foo/bar'); // 输出 http://www.foo.com/foo/bar

像这种基于当前请求,生成指定路径的Url;

这部分功能由两个文件完成,一个是UrlGenerator.php,另一个是RouteUrlGenerator.php;UrlGenerator.php处理根据路径名生成Url,RouteUrlGenerator.php处理根据路由生成Url;

列一些常用的使用:

根据路径名生成

使用to方法,第一个参数为路径,第二个参数是数组,implode后会接着路径名,第三个参数决定用不用https

// 路径名是 foo/bar,当前请求的根路径为 http://www.foo.com,所以输出是 http://www.foo.com/foo/bar
$url->to('foo/bar')
// 路径名是 foo/bar,当前请求的根路径为 http://www.foo.com,第三个参数决定 scheme 是 https,所以输出是 https://www.foo.com/foo/bar
$url->to('foo/bar', [], true)
// 路径名是 foo/bar,第二个参数 是补充路径名,implode 后是 /baz/boom
// 第三个参数决定 scheme 是 https,所以输出是 https://www.foo.com/foo/bar/baz/boom
$url->to('foo/bar', ['baz', 'boom'], true)
// 路径名是 foo/bar,查询参数是 ?foo=bar ,补充路径是 /baz,所以输出是 https://www.foo.com/foo/bar/baz?foo=bar
$url->to('foo/bar?foo=bar', ['baz'], true)

根据路由的 as 名生成

使用route方法,第一个参数为指定路由的 as 名,第二个参数是参数数组,第三个参数决定是否显示根目录(默认为 true)

$route = new Route(['GET'], 'foo/bar', ['as' => 'foo']);
$routes->add($route);

// 输出 'http://www.foo.com/foo/bar
$url->route('foo');

// 第三个参数为 false,表示不显示根目录,于是输出 /foo/bar
$url->route('foo', [], false)

// 路由中的 url 本身不带参数,则第二参数中所有关联数组都将作为查询参数
// 输出 /foo/bar?foo=bar
$url->route('foo', ['foo' => 'bar'], false)
$route = new Route(['GET'], 'foo/bar/{baz}/breeze/{boom}', ['as' => 'bar']);
$routes->add($route);

// 路由上的 url 带参数,根据参数名找值;剩余多余的为查询参数;
// 输出 http://www.foo.com/foo/bar/otwell/breeze/taylor?fly=wall
$url->route('bar', ['boom' => 'taylor', 'baz' => 'otwell', 'fly' => 'wall']);

// 路由上的 url 带参数,找不到对应的参数值,则按顺序作值;剩余多余的为查询参数;
// 输出 http://www.foo.com/foo/bar/taylor/breeze/otwell?fly=wall
$url->route('bar', ['taylor', 'otwell', 'fly' => 'wall']);

根据路由的 action 名生成

使用action方法,第一个参数为指定路由的 action 名,第二个参数是参数数组,第三个参数决定是否显示根目录(默认为 true)

$route = new Route(['GET'], 'foo/bam', ['controller' => 'foo@bar']);
$routes->add($route);

// 输出 http://www.foo.com/foo/bam
$url->action('foo@bar');
$route = new Route(['GET'], 'foo/invoke', ['controller' => 'InvokableActionStub']);
$routes->add($route);

// 输出 http://www.foo.com/foo/invoke
$url->action('InvokableActionStub');

设置全局默认参数

$url->defaults(['locale' => 'en']);

$route = new Route(['GET'], 'foo', ['as' => 'defaults', 'domain' => '{locale}.example.com', function() {}]);

// 路由 url 有参数,但没有传参数值,则会找全局默认参数值;输出 http://en.example.com/foo
$url->route('defaults');

设置全局命名空间

这样调用的时候,不用在 action 上省略这部分命名空间

// 设置全局命名空间
$url->setRootControllerNamespace('namespace');

// 配置添加路由
$route = new Route(['GET'], 'foo/bar', ['controller' => 'namespace\foo@bar']);
$routes->add($route);
$route = new Route(['GET'], 'foo/invoke', ['controller' => 'namespace\InvokableActionStub']);
$routes->add($route);

// 输出 http://www.foo.com/foo/bar; action 的值省略 namespace 这个命名空间
$url->action('foo@bar');
// 输出 http://www.foo.com/foo/invoke; action 的值省略 namespace 这个命名空间
$url->action('InvokableActionStub');

// 配置添加路由
$route = new Route(['GET'], 'something/else', ['controller' => 'something\foo@bar']);
$routes->add($route);

// 输出 http://www.foo.com/something/else; action 的最前面加了 `\`,全局命名空间下调用
$url->action('\something\foo@bar');

跳转器

跳转器内部提供了以下跳转;

home

通过调用app('redirect')->home()会跳转至根目录下\;

public function home($status = 302)

back

通过调用app('redirect')->back()会跳转至上一次访问页面;或者全局帮助函数back()也可以;

public function back($status = 302, $headers = [], $fallback = false)

第三个参数表示,如果没有前一次访问请求,访问哪个页面,具体源码如下:

if ($url) {
  return $url;
} elseif ($fallback) {
 return $this->to($fallback);
} else {
 return $this->to('/');
}

refresh

通过调用app('redirect')->refresh()会刷新当前访问页面;

public function refresh($status = 302, $headers = [])

to

通过调用app('redirect')->to('path')会跳转至指定路径页面;或者全局帮助函数redirect('path')也可以;

这里的 path 路径是不包含根目录的,例如(foo/bar);

public function to($path, $status = 302, $headers = [], $secure = null)

第四个参数表示是否使用https;

away

通过调用app('redirect')->away('path')会跳转至指定路径页面;

这里的 path 路径是包含根目录的,例如(http://xx.com/foo/bar);

public function away($path, $status = 302, $headers = [])

secure

通过调用app('redirect')->secure('path')会跳转至指定路径页面;这里的path路径是不包含根目录的;

public function secure($path, $status = 302, $headers = [])

其本质是调用了to方法

return $this->to($path, $status, $headers, true);

route

通过调用app('redirect')->route('route_as_name') ,根据路由的as名会跳转至与路由一致的url路径页;

public function route($route, $parameters = [], $status = 302, $headers = [])

action

通过调用app('redirect')->action('route_action') ,根据路由的action名会跳转至与路由一致的url路径页;

public function action($action, $parameters = [], $status = 302, $headers = [])

guest

跳到指定的路径页的同时,将当前url存放至session中,键名为url.intended;

public function guest($path, $status = 302, $headers = [], $secure = null)

intended

跳转至session中键名为url.intended的值所对应的Url;如果不存在,则跳转至第一个参数所传的值;

public function intended($default = '/', $status = 302, $headers = [], $secure = null)

响应工厂(ResponseFactory)

ResponseFactory文件提供了两部分 API,分别是与响应类型相关和与跳转相关;

响应

response()会返回ResponseFactory实例;

视图响应

response()->view('hello', $data, 200);

Jsop响应

response()->json(['name' => 'Abigail', 'state' => 'CA']);

Jsonp响应

response()->json(['name' => 'Abigail', 'state' => 'CA'])->withCallback($request->input('callback'));

文件响应

直接在浏览器显示文件,而不是下载,例如图片或PDF;file方法第一参数为文件路径,第二参数选填为头信息数组;

response()->file($pathToFile, $headers);

文件下载

download方法第一参数为文件路径,第二参数选填为文件名,第三参数选填为头信息数组;

return response()->download($pathToFile, $name, $headers);

跳转

这里的跳转方法,其实调用的还是跳转器中的方法,不过是在暴露更多的接口,方便调用与使用;

方法名 调用 实际调用的是跳转器中的哪个方法
redirectTo response()->redirectTo(...) to方法
redirectToRoute response()->redirectToRoute(...) route方法
redirectToAction response()->redirectToAction(...) action方法
redirectGuest response()->redirectGuest(...) guest方法
redirectToIntended response()->redirectToIntended(...) intended方法

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
PHP4实际应用经验篇(4)
Oct 09 PHP
php自动适应范围的分页代码
Aug 05 PHP
PHP去除数组中重复的元素并按键名排序函数
Aug 18 PHP
php 什么是PEAR?(第二篇)
Mar 19 PHP
解析如何屏蔽php中的phpinfo()函数
Jun 06 PHP
php+js实现图片的上传、裁剪、预览、提交示例
Aug 27 PHP
php微信公众开发之获取周边酒店信息的方法
Dec 22 PHP
php提交表单发送邮件的方法
Mar 20 PHP
PHP获取文件扩展名的方法实例总结
Jun 10 PHP
ThinkPHP5.0框架控制器继承基类和自定义类示例
May 25 PHP
Laravel Validator自定义错误返回提示消息并在前端展示
May 09 PHP
Laravel框架实现抢红包功能示例
Oct 31 PHP
Django中的cookie与session操作实例代码
Aug 17 #PHP
Django 中 cookie的使用
Aug 17 #PHP
PHP面向对象中new self()与 new static()的区别浅析
Aug 17 #PHP
通过修改Laravel Auth使用salt和password进行认证用户详解
Aug 17 #PHP
PHP实现执行外部程序的方法详解
Aug 17 #PHP
laravel通过创建自定义artisan make命令来新建类文件详解
Aug 17 #PHP
Laravel中的Auth模块详解
Aug 17 #PHP
You might like
浅析SVN常见问题及解决方法
2013/06/21 PHP
php实现文本数据导入SQL SERVER
2015/05/17 PHP
php编写简单的文章发布程序
2015/06/18 PHP
Yii框架中用response保存cookie,用request读取cookie的原理解析
2019/09/04 PHP
javascript级联下拉列表实例代码(自写)
2013/05/10 Javascript
javascript中call和apply方法浅谈
2013/09/27 Javascript
jquery访问ashx文件示例代码
2014/08/11 Javascript
javascript实现随时变化着的背景颜色
2015/04/02 Javascript
jquery实现最简单的滑动菜单效果代码
2015/09/12 Javascript
基于Jquery制作图片文字排版预览效果附源码下载
2015/11/18 Javascript
jquery+ajax实现省市区三级联动效果简单示例
2017/01/04 Javascript
微信小程序模板之分页滑动栏
2017/02/10 Javascript
BootStrap 动态表单效果
2017/06/02 Javascript
浅析 NodeJs 的几种文件路径
2017/06/07 NodeJs
jQuery正则验证注册页面经典实例
2017/06/10 jQuery
JS实现Cookie读、写、删除操作工具类示例
2018/08/28 Javascript
vue-router 手势滑动触发返回功能
2018/09/30 Javascript
Vue使用localStorage存储数据的方法
2019/05/27 Javascript
javascript触发模拟鼠标点击事件
2019/06/26 Javascript
vue-cli 项目打包完成后运行文件路径报错问题
2019/07/19 Javascript
python插入排序算法的实现代码
2013/11/21 Python
收藏整理的一些Python常用方法和技巧
2015/05/18 Python
使用pandas批量处理矢量化字符串的实例讲解
2018/07/10 Python
在ubuntu16.04中将python3设置为默认的命令写法
2018/10/31 Python
Python Socket TCP双端聊天功能实现过程详解
2020/06/15 Python
10分钟理解CSS3 Grid布局
2018/12/20 HTML / CSS
Reebok官方旗舰店:美国知名健身品牌锐步
2019/01/07 全球购物
美国家用和厨房电器销售网站:Appliances Connection
2020/01/24 全球购物
村干部培训方案
2014/05/02 职场文书
民主生活会剖析材料
2014/09/30 职场文书
大雁塔英文导游词
2015/02/10 职场文书
大国崛起日本观后感
2015/06/02 职场文书
高三教师工作总结2015
2015/07/21 职场文书
2016中秋晚会开幕词
2016/03/03 职场文书
Oracle表空间与权限的深入讲解
2021/11/17 Oracle
Ruby处理YAML和json数据
2022/04/18 Ruby