百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

[ Laravel 5.6 文档 ] 基础组件 —— 路由

bigegpt 2024-10-18 04:20 4 浏览

[ Laravel 5.6 文档 ] 基础组件 —— 路由

这是一篇最重量级的文档了,laravel只有一个入口,如何用好路由,将给你的系统带来极大的灵活与方便。

【文章转自laravel学院】

路由入门

最基本的 Laravel 路由只接收一个 URI 和一个闭包,并以此为基础提供一个非常简单优雅的路由定义方法:

Route::get('hello', function () {
 return 'Hello, Welcome to LaravelAcademy.org';
});

我们以在安装配置文档中新建的 blog 应用为例,在 routes/web.php 中定义该路由:

?

在浏览器中通过 http://blog.test/hello (我使用 Valet 作为开发环境,故而对应域名是 blog.test,实际域名以自己配置的为准)即可访问我们刚刚定义的路由,页面输出内容如下:

Hello, welcome to LaravelAcademy.org

默认路由文件

所有 Laravel 路由都定义在位于 routes 目录下的路由文件中,这些文件通过框架自动加载,相应逻辑位于 app/Providers/RouteServiceProvider 类。routes/web.php 文件定义了 Web 界面的路由,这些路由被分配到了 web
中间件组,从而可以使用 Session 和 CSRF 保护等功能。routes/api.php 中的路由是无状态的,这是因为被分配到了 api 中间件组。

对大多数应用而言,都是从 routes/web.php 文件开始定义路由。定义在 routes/web.php 中的路由可以通过在浏览器地址栏输入相应的 URL 进行访问,例如,你可以通过 http://blog.test/user 访问下面的路由:

Route::get('/user', 'UsersController@index');

正如前面所提到的,定义在 routes/api.php 文件中的路由通过 app/Providers/RouteServiceProvider
的处理被嵌套在一个路由群组中, 在这个群组中,所有路由会被自动添加 /api 前缀,所以你不需要再到路由文件中为每个路由手动添加,你可以通过编辑 RouteServiceProvider 类来修改路由前缀以及其他的路由群组选项:

?

有效的路由方法

我们可以注册路由来响应任何 HTTP 请求动作:

Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

有时候还需要注册一个路由响应多种 HTTP 请求动作 —— 这可以通过 match 方法来实现。或者,可以使用 any 方法注册一个路由来响应所有 HTTP 请求动作:

Route::match(['get', 'post'], 'foo', function () {
 return 'This is a request from get or post';
});
Route::any('bar', function () {
 return 'This is a request from any HTTP verb';
});

测试 GET 请求的时候直接在浏览器中输入请求地址即可,测试 POST 请求可以通过客户端工具,比如 Advanced REST Client,该工具可以在 Chrome 应用商店下载到,此外如果上面的路由是定义在 routes/web.php
的话,在测试 POST 请求之前,需要将对应路由取消 CSRF 保护检查,否则会返回 419 状态码导致无法请求成功,取消的方法是在 app/Http/Middleware/VerifyCsrfToken 中设置排除检查路由:

?

下面我们来测试下 POST 请求:

?

如果路由是定义在 routes/api.php 的话,则无需关注 CSRF 保护问题,比如我们在 routes/api.php 定义 bar 路由,并且在 VerifyCsrfToken 的 $except 属性数组中移除 bar,然后我们测试下对 http://blog.test 的 POST 请求:

正如我们所预测的,完全没有任何问题,背后的原因是因为 web 路由文件中定义的路由都位于 web 中间件群组,该群组默认启用 CSRF 保护检查,而 api 路由文件位于 api 路由群组,该群组下的路由主要用于 第三方 API 请求,没办法进行 CSRF 检查,所以不需要做任何处理。

CSRF 保护

在 routes/web.php 路由文件中所有请求方式为 PUT、POST 或 DELETE 的路由对应的 HTML 表单都必须包含一个 CSRF 令牌字段,否则,请求会被拒绝。关于 CSRF 的更多细节,可以参考 CSRF文档:

<form method="POST" action="/profile">
 {{ csrf_field() }}
 ...
</form>

还是以上面的 foo 路由为例,如果我们不在 VerifyCsrfToken 中间件中排除对它的检查(事实上,这样的操作也不安全),那么就需要在表单提交中带上 csrf_token 字段:

?

这样,当我们访问 http://blog.test/form 然后在页面点击提交按钮后,页面会跳转到 http://blog.test/foo 并显示如下内容:

This is a request from get or post

路由重定向

如果你需要定义一个重定向到其他 URI 的路由,可以使用 Route::redirect 方法,该方法非常方便,以至于你不需要再定义额外的路由或控制器来执行简单的重定向逻辑:

Route::redirect('/here', '/there', 301);

其中 here 表示原路由,there 表示重定向之后的路由,301 是一个 HTTP 状态码,用于标识重定向。

路由视图

如果你的路由需要返回一个视图,可以使用 Route::view 方法,和 redirect 方法类似,这个方法也很方便,以至于你不需要在额外定义一个路由或控制器。

view 方法接收一个 URI 作为第一个参数,以及一个视图名称作为第二个参数,此外,你还可以提供一个数组数据传递到该视图方法作为可选的第三个参数,该数组数据可用于视图中的数据渲染:

Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => '学院君']);

我们在 routes/web.php 定义一个路由视图如下:

?

为了保证可以共用 welcome.blade.php 这个视图文件,我们也对默认提供的 / 路由做了调整,接下来,我们需要修改 resources/views/welcome.blade.php 代码以支持 website 数据变量:

?

我们将原来写死的 Laravel 文本调整为支持变量传入的方式,这样,我们就可以在浏览器中通过 http://blog.test/view 访问路由视图了:

?

路由参数

必选参数

有时我们需要在路由中获取 URI 请求参数。例如,如果要从 URL 中获取用户ID,需要通过如下方式定义路由参数:

Route::get('user/{id}', function ($id) {
 return 'User ' . $id;
});

这样我们在浏览器中访问 http://blog.test/user/1,就会得到以下输出:

User 1

可以根据需要在路由中定义多个路由参数:

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
 return $postId . '-' . $commentId;
});

根据上面的示例,路由参数需要通过花括号 {} 进行包裹并且是拼音字母,这些参数在路由被执行时会被传递到路由的闭包。路由参数名称不能包含 - 字符,如果需要的话可以使用 _ 替代,比如如果某个路由参数定义成 {post-id} 则访问路由会报错,应该修改成 {post_id} 才行。路由参数被注入到路由回调/控制器取决于它们的顺序,与回调/控制器名称无关。

可选参数

有必选参数就有可选参数,这可以通过在参数名后加一个 ? 标记来实现,这种情况下需要给相应的变量指定默认值,当对应的路由参数为空时,使用默认值:

Route::get('user/{name?}', function ($name = null) {
 return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
 return $name;
});

这时如果定义的路由是下面这个的话,访问 http://blog.test/user 会返回 John。

正则约束

可以通过路由实例上的where方法来约束路由参数的格式。where方法接收参数名和一个正则表达式来定义该参数如何被约束:

Route::get('user/{name}', function ($name) {
 // $name 必须是字母且不能为空
})->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
 // $id 必须是数字
})->where('id', '[0-9]+');
Route::get('user/{id}/{name}', function ($id, $name) {
 // 同时指定 id 和 name 的数据格式
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

使用正则约束还有一个好处就是避免了user/{id}user/{name}的混淆。

全局约束

如果想要路由参数在全局范围内被给定正则表达式约束,可以使用pattern方法。需要在RouteServiceProvider
类的boot方法中定义这种约束模式:

/**
* 定义路由模型绑定,模式过滤器等
*
* @param \Illuminate\Routing\Router $router
* @return void
* @translator http://laravelacademy.org
*/
public function boot()
{
 Route::pattern('id', '[0-9]+');
 parent::boot();
}

一旦模式被定义,将会自动应用到所有包含该参数名的路由中:

Route::get('user/{id}', function ($id) {
 // 只有当 {id} 是数字时才会被调用
});

除此之外,该模式还会被应用到诸如下面这些路由参数上:

Route::get('post/{id}', function ($id) {
 // 只有当 {id} 是数字时才会被调用
});
Route::get(`product/{id}', function ($id) {
 // 只有当 {id} 是数字时才会被调用
});

很显然这种方式让代码更简洁,也为我们实现同一参数统一约束带来了方便。

命名路由

命名路由为生成 URL 或重定向提供了方便,实现起来也很简单,在路由定义之后使用name方法链的方式来定义该路由的名称:

Route::get('user/profile', function () {
 // 通过路由名称生成 URL
 return 'my url: ' . route('profile');
})->name('profile');

还可以为控制器动作指定路由名称:

Route::get('user/profile', 'UserController@showProfile')->name('profile');

这样我们就可以通过以下方式定义重定向:

Route::get('redirect', function() {
 // 通过路由名称进行重定向
 return redirect()->route('profile');
});

为命名路由生成 URL

正如上面代码所展示的,为给定路由分配名称之后,就可以通过辅助函数route为该命名路由生成 URL 或者通过redirect函数进行重定向:

// 生成URL
$url = route('profile');
// 生成重定向
return redirect()->route('profile');

如果命名路由定义了参数,可以将该参数作为第二个参数传递给route函数。给定的路由参数将会自动插入到 URL 中:

Route::get('user/{id}/profile', function ($id) {
 $url = route('profile', ['id' => 1]);
 return $url;
})->name('profile');

这样,当我们访问http://blog.test/user/123/profile页面输出内容也是http://blog.test/user/123/profile

检查当前路由

如果你想要判断当前请求是否被路由到给定命名路由,可以使用 Route 实例上的named方法,例如,你可以从路由中间件中检查当前路由名称:

/**
* 处理输入请求
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
 if ($request->route()->named('profile')) {
 //
 }
 return $next($request);
}

路由分组

路由分组的目的是让我们在多个路由中共享相同的路由属性,比如中间件和命名空间等,这样的话我们定义了大量的路由时就不必为每一个路由单独定义属性。共享属性以数组的形式作为第一个参数被传递给Route::group方法。

中间件

要给某个路由分组中定义的所有路由分配中间件,可以在定义分组之前使用middleware方法。中间件将会按照数组中定义的顺序依次执行:

Route::middleware(['first', 'second'])->group(function () {
 Route::get('/', function () {
 // Uses first & second Middleware
 });
 Route::get('user/profile', function () {
 // Uses first & second Middleware
 });
});

关于中间件的使用我们在后面单独讲中间件时再进行示例演示,这里我们先了解这样使用就行。

命名空间

路由分组另一个通用的例子是使用namespace方法分配同一个 PHP 命名空间给该分组下的多个控制器:

Route::namespace('Admin')->group(function () {
 // Controllers Within The "App\Http\Controllers\Admin" Namespace
});

默认情况下,RouteServiceProvider在一个命名空间分组下引入所有路由文件,并指定所有控制器类所在的默认命名空间是App\Http\Controllers,因此,我们在定义控制器的时候只需要定命名空间App\Http\Controllers之后的部分即可。

关于命名空间后面我们单独讲控制器的时候还会再详细演示,这里先了解用法即可。

子域名路由

路由分组还可以被用于处理子域名路由,子域名可以像 URI 一样被分配给路由参数,从而允许捕获子域名的部分用于路由或者控制器,子域名可以在定义分组之前调用domain方法来指定:

Route::domain('{account}.blog.dev')->group(function () {
 Route::get('user/{id}', function ($account, $id) {
 return 'This is ' . $account . ' page of User ' . $id;
 });
});

比如我们设置会员子域名为account.blog.test,那么就可以通过 http://account.blog.test/user/1 访问用户ID为1的会员信息了:

This is account page of User 1

路由前缀

prefix方法可以用来为分组中每个路由添加一个给定 URI 前缀,例如,你可以为分组中所有路由 URI 添加admin前缀 :

Route::prefix('admin')->group(function () {
 Route::get('users', function () {
 // Matches The "/admin/users" URL
 });
});

这样我们就可以通过http://blog.test/admin/users访问路由了。

路由名称前缀

name方法可通过传入字符串为分组中的每个路由名称设置前缀,例如,你可能想要在所有分组路由的名称前添加admin前缀,由于给定字符串和指定路由名称前缀字符串完全一样,所以需要在前缀字符串末尾后加上.字符:

Route::name('admin.')->group(function () {
 Route::get('users', function () {
 // 新的路由名称为 "admin.users"...
 })->name('users');
});

路由模型绑定

注入模型 ID 到路由或控制器动作时,通常需要查询数据库才能获取相应的模型数据。Laravel 路由模型绑定让注入模型实例到路由变得简单,例如,你可以将匹配给定 ID 的整个User类实例注入到路由中,而不只是注入用户 ID。

隐式绑定

Laravel 会自动解析定义在路由或控制器动作(变量名匹配路由片段)中的 Eloquent 模型类型声明,例如(我们将这个路由定义在routes/api.php文件中):

Route::get('users/{user}', function (App\User $user) {
 return $user->email;
});

在这个例子中,由于类型声明了 Eloquent 模型App\User,对应的变量名$user会匹配路由片段中的{user}
,这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。如果匹配模型实例在数据库中不存在,会自动生成 404 响应。

在演示本功能之前,我们需要先创建数据表,由于我是在 Valet 开发环境中开发,需要自己创建数据库,我们将数据库命名为valet,本地的数据库用户名为root,密码为空,对应地,修改.env文件配置如下:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=valet
DB_USERNAME=root
DB_PASSWORD=

具体配置值以你自己的开发环境设置为准。我们将基于 Laravel 强大的数据库迁移功能创建users表,关于数据库迁移后面在数据库部分会详细讨论,这里我们通过以下命令来生成users表即可:

php artisan migrate

进入数据库可以看到该表已经生成:

?

这时,users数据表还没有任何记录,如果数据库中找不到对应的模型实例,会自动生成 HTTP 404 响应,提示页面不存在,所以我们需要在这张表中插入一条记录,这里我们基于 Laravel 强大的数据库填充器来快速完成数据填充功能,首先通过如下命令生成users对应的数据表填充器:

php artisan make:seeder UsersTableSeeder

该命令会在database/seeds目录下生成一个UsersTableSeeder文件,编辑该文件内容如下:

然后编辑同目录下的DatabaseSeeder.php文件如下(取消调用数据表填充器前的注释):

?

最后执行php artisan db:seed即可插入对应数据到users表了,这样我们在浏览器中再次访问 http://blog.test/api/users/1 的时候就会显示User模型数据了:

?

接下来,你就可以在应用代码中直接拿$user模型去做你想做的事情了,而不需要自己去数据库查询,从而提高了开发的效率。

自定义键名

如果你想要在隐式模型绑定中使用数据表的其它字段而不是id字段,可以重写 Eloquent 模型类的getRouteKeyName
方法,以User模型为例,可以在该模型类中添加这个方法 :

/**
* Get the route key for the model.
*
* @return string
*/
public function getRouteKeyName()
{
 return 'name';
}

这样我们就可以通过http://blog.test/api/users/jroJoGP71W访问同一个模型实例了。这里需要注意的点是如果该字段不是唯一键,则会返回结果集的第一条记录,对应的底层实现在这里:

显式绑定

有隐式绑定,就有显式绑定。要注册显式绑定,可以使用路由器的model方法来为给定参数指定绑定类。你需要在 RouteServiceProvider类的boot方法中定义显式模型绑定:

public function boot()
{
 parent::boot();
 Route::model('user_model', App\User::class);
}

接下来,在routes/api.php中定义一个包含{user}参数的路由:

$router->get('profile/{user_model}', function(App\User $user) {
 dd($user);
});

由于我们已经绑定{user_model}参数到App\User模型,User实例会被注入到该路由。因此,如果请求 URL 是http://blog.test/api/profile/1,就会注入一个用户 ID 为1User实例。

如果匹配的模型实例在数据库不存在,会自动生成并返回 HTTP 404 响应。

自定义解析逻辑

如果你想要使用自定义的解析逻辑,可以在RouteServiceProvider类的boot方法中使用Route::bind方法,传递到bind
方法的闭包会获取到 URI 请求参数中的值,并且返回你想要在该路由中注入的类实例:

public function boot()
{
 parent::boot();
 Route::bind('user', function($value) {
 return App\User::where('name', $value)->first() ?? abort(404);
 });
}

有了这些方法,基本上可以满足你对路由模型绑定的各种需求了。

频率限制

Laravel 自带了一个中间件用于限制对应用路由的访问频率。开始使用该功能之前,分配throttle中间件到某个路由或路由分组,throttle中间件接收两个参数用于判断给定时间内(单位:分钟)的最大请求次数。例如,我们指定登录用户每分钟只能访问下面的分组路由 60 次:

Route::middleware('auth:api', 'throttle:60,1')->group(function () {
 Route::get('/user', function () {
 //
 });
});

超出访问次数后,会返回429状态码并提示”Too many requests”。

动态频率限制

此外,还可以基于User模型的属性来动态设置最大请求次数。例如,如果User模型包含rate_limit属性,就可以将其这个属性名传递到throttle中间件,这样就可以将属性值作为计算最大请求次数的数据来源:

Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
 Route::get('/user', function () {
 //
 });
});

表单方法伪造

HTML 表单不支持PUTPATCH或者DELETE请求方法,因此,在 HTML 表单中调用PUTPATCHDELETE路由时,需要添加一个隐藏的_method字段,其值被用作该表单的 HTTP 请求方法:

<form action="/foo/bar" method="POST">
 <input type="hidden" name="_method" value="PUT">
 <input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>

还可以直接使用 Blade 指令@method来生成_method字段:

<form action="/foo/bar" method="POST">
 @method('PUT')
 @csrf
</form>

访问当前路由

你可以使用Route门面上的currentcurrentRouteNamecurrentRouteAction方法来访问处理当前输入请求的路由信息:

// 获取当前路由实例
$route = Route::current();
// 获取当前路由名称
$name = Route::currentRouteName();
// 获取当前路由action属性
$action = Route::currentRouteAction();

参考API文档了解路由门面底层类以及Route实例的更多可用方法。

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...