Laravel学习教程之路由模块

前言

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

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

模块组成

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

剖析

服务提供者

看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方法

总结

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

(0)

相关推荐

  • Laravel学习教程之广播模块详解

    前言 本文主要给大家介绍了关于Laravel广播模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 注意:本文是基于Laravel 5.4版本的路由模块代码进行分析书写: 简介 广播是指发送方发送一条消息,订阅频道的各个接收方都能及时收到消息:比如 A同学写了一篇文章,这时候 B同学在文章底下评论了,A同学在页面上是不用刷新就能收到提示有文章被评论了,这个本质上就是A同学收到了广播消息,这个广播消息是由B同学评论这个动作触发了发送广播消息: 在整个广播行为中,有一个重

  • Laravel学习教程之View模块详解

    前言 本文主要是给大家介绍了关于Laravel中View模块的相关资料,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 本文是基于Laravel 5.4版本的路由模块代码进行分析书写: 文件结构 View模块的文件格局及功能如下图所示: 视图化呈现时的大概流程: 1.通过view()方法的调用,开始视图的呈现: 2.首先,查找视图文件: (1)依次遍历路径,如果文件名带命名空间(也就是::之前的部分),则采用命名空间对应注册的路径数组,否则采用全局路径数组(在Illuminat

  • Laravel中的Auth模块详解

    前言 本文主要给大家介绍的是关于Laravel中Auth模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 本文是基于Laravel 5.4 版本的本地化模块代码进行分析书写: 模块组成 Auth模块从功能上分为用户认证和权限管理两个部分:从文件组成上,Illuminate\Auth\Passwords目录下是密码重置或忘记密码处理的小模块,Illuminate\Auth是负责用户认证和权限管理的模块,Illuminate\Foundation\Auth提供了登录.

  • Laravel学习教程之本地化模块

    前言 本文主要给大家介绍了关于Laravel本地化模块的相关内容,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍吧. 本文是基于Laravel 5.4版本的本地化模块代码进行分析书写: 模块组成 下图展示了本地化模块各个文件的关系,并进行简要说明: TranslationServiceProvider 本地化模块的服务提供者,既是一个模块的入口,也是与IOC容器交互的中心:注册翻译器实例translation.loader,注册翻译管理实例translator,并声明延迟加载服务: T

  • 基于Laravel实现的用户动态模块开发

    前言 相信大家都知道,几乎所有的社区应用都有用户动态这个部分,用户可以通过好友动态获能取到更多感兴趣的内容,从而提高社区活跃度和用户粘性.它的实现相对来讲比普通的内容发布要复杂一些,主要体现在内容多样性上. 为了解决这个问题,我们得把这些不同类型的内容抽象,提取共性,使用相同的结构来处理,开发起来就会简单很多. 概念抽象 用户动态,顾名思义,动态的产生,就是一系列事件的历史记录,所以首先关注"事件"这个名词,它有哪些属性: 触发者,基于社区所有的事件几乎都是由用户触发的 事件主体,事件

  • Laravel学习教程之路由模块

    前言 本文主要给大家介绍的是关于Laravel路由模块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 备注:本文是基于Laravel 5.4版本的路由模块代码进行分析书写: 模块组成 下图展示了路由模块中各个文件的关系,并进行简要说明: 剖析 服务提供者 看Laravel模块,首先找ServiceProvider文件,这是模块与IOC容器交互的入口,从这个文件,可以看出该模块提供向系统提供了哪些服务: public function register() { //

  • laravel学习教程之关联模型

    Eloquent: 关联模型 简介 数据库中的表经常性的关联其它的表.比如,一个博客文章可以有很多的评论,或者一个订单会关联一个用户.Eloquent 使管理和协作这些关系变的非常的容易,并且支持多种不同类型的关联:     一对一     一对多     多对多     远程一对多     多态关联     多态多对多关联 定义关联 Eloquent 关联可以像定义方法一样在 Eloquent 模型类中进行定义.同时,它就像 Eloquent 模型自身一样也提供了强大的查询生成器.这允许关联模

  • laravel学习教程之存取器

    前言 Laravel是一个简单优雅的PHP Web开发框架,可以将开发者从意大利面条式的代码中解放出来,通过简单.高雅.表达式语法开发出很棒的Web应用,那下面跟着小编一起来学习laravel中的存取器吧. Eloquent: 存取器简介 访问器和存储器允许你在获取或者设置 Eloquent 模型属性值时对其进行格式化操作.比如,你可能希望当一个值存储进数据库之前先对其进行 Laravel encrypter 进行加密操作,并且可以在你通过模型访问的时候自动的进行该属性的解密. 除了可自定义的的

  • Laravel学习教程之从入口到输出过程详解

    本文主要给大家介绍了关于Laravel从入口到输出过程的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. I. 预备知识 Laravel 目录 /path/to 后面简称 Laravel Web 目录 Laravel/public 后面简称 Web 入口文件 Web/index.php II. 过程详解 范例: http://la.com/test/yueshu/female/20?name=chenxuelong 1. 定义 web 路由 文件路径: Laravel/

  • Laravel学习教程之model validation的使用示例

    前言 本文主要给大家介绍了关于Laravel学习之model validation使用的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 在对database进行写操作前,需要对数据进行validation,如type-check 每一个 model column 的定义('type' 这个column必须是enum('card','loan')) ,这里使用model event来做. 在EventServiceProvider(或自定义一个ValidationSer

  • 值得分享和收藏的Bootstrap学习教程

    首先先感谢朋友们对小编的支持,关注小编的文章,和小编一起学习进步,一直的陪伴,小编也一定会再接再厉,为大家分享更多精彩的文章. 在分享的过程中遇到了一些问题,这也给了我很大的启发,大家在搜索相关文章时都没有针对此类问题进行的系统整理文章,这也是小编需要在今后进行改进的. 所以本文就吸取这个经验,针对Bootstrap教程进行系统整理,希望对大家的学习有所帮助,便于大家学习. 小编的水平能力都很有限,还望大牛多给一些建议和看法,帮助小编进行改正和提高,如果文中确有不妥之处,还请谅解.希望能和大家共

  • Java正则表达式学习教程

    本教程旨在帮助你驾驭Java正则表达式,同时也帮助我复习正则表达式. 什么是正则表达式? 正则表达式定义了字符串的模式.正则表达式可以用来搜索.编辑或处理文本.正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别.Java正则表达式和Perl的是最为相似的. Java正则表达式的类在 java.util.regex 包中,包括三个类:Pattern,Matcher 和 PatternSyntaxException. Pattern对象是正则表达式的已编译版本.他没有任何公共构造器,我们通

  • MyBatis学习教程(六)-调用存储过程

    一.提出需求 查询得到男性或女性的数量, 如果传入的是0就女性否则是男性 二.准备数据库表和存储过程 create table p_user( id int primary key auto_increment, name varchar(), sex char() ); insert into p_user(name,sex) values('A',"男"); insert into p_user(name,sex) values('B',"女"); insert

随机推荐