路由

未匹配的标注
本文档最新版为 10.x,旧版本可能放弃维护,推荐阅读最新版!

路由

基本路由

最基本的 Laravel 路由接受一个 URI 和一个闭包,它提供了一种非常简单和有表现力的方法来定义路由和行为,而不需要复杂的路由配置文件:

use Illuminate\Support\Facades\Route;

Route::get('/greeting', function () {
    return 'Hello World';
});

默认路由文件

所有 Laravel 路由都定义在路由文件中,这些文件位于 routes 目录下. 这些文件由应用程序的 App\Providers\RouteServiceProvider服务自动加载。 routes/web.php 文件定义用于 web 界面的路由。 这些路由被分配给 web 中间件组, 该组提供会话状态和 CSRF 保护等功能。 routes/api.php 中的路由是无状态的,这些路由被分配给 api 中间件组。

对于大多数应用程序, 都是以在 routes/web.php 文件定义路由开始的。 可以通过在浏览器中输入定义的路由 URL 来访问 routes/web.php 的路由。 例如, 你可以在浏览器中输入 http://example.com/user 来访问以下路由:

use App\Http\Controllers\UserController;

Route::get('/user', [UserController::class, 'index']);

routes/api.php 文件中定义的路由通过 RouteServiceProvider 被嵌套到一个路由组里面。在这个路由组中,会自动添加 URL 前缀 /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'], '/', function () {
    //
});

Route::any('/', function () {
    //
});

依赖注入

你可以在路由的回调方法中使用类型提示,用来获取路由所需的任何依存关系。 声明的依赖项将由Laravel 服务容器 自动解析并注入到回调中。 例如,您可以使用 Illuminate\Http\Request 类型提示,将当前的HTTP请求自动注入到路由回调中:

use Illuminate\Http\Request;

Route::get('/users', function (Request $request) {
    // ...
});

CSRF 保护

指向 web 路由文件中定义的 POST, PUT, PATCH, 或 DELETE 路由的任何 HTML 表单都应该包含一个 CSRF 令牌字段,否则,这个请求将会被拒绝。可以在 CSRF 文档 中阅读有关 CSRF 保护的更多信息:

<form method="POST" action="/profile">
    @csrf
    ...
</form>

重定向路由

如果要定义重定向到另一个 URI 的路由,可以使用 Route::redirect 方法。这个方法提供了一种方便的快捷方式,因此你不必为执行简单的重定向而定义完整的路由或控制器:

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

默认情况, Route::redirect 返回的状态码是 302 。 但你可以使用第三个可选参数自定义状态码:

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

你也可以使用 Route::permanentRedirect 方法返回 301 状态码:

Route::permanentRedirect('/here', '/there');

注意:在重定向路由中使用路由参数时,以下参数由 Laravel 保留,不能使用: destinationstatus.

视图路由

如果你的路由只需要返回一个 view,你可以使用 Route::view 方法。 像 redirect 方法一样, 此方法提供了一个简单的快捷方式,因此您不必定义完整的路由或控制器。 该 view 方法接受URI作为其第一个参数,并接受视图名称作为其第二个参数。另外,您可以提供一个数据数组作为可选的第三个参数传递给视图:

Route::view('/welcome', 'welcome');

Route::view('/welcome', 'welcome', ['name' => 'Taylor']);

注意:当在视图路由使用路由参数,下面的参数是由Laravel保留,不能使用: view, data, status, 和 headers.

路由参数

必填参数

有时您将需要捕获路由内的URI段。例如,您可能需要从URL中捕获用户的ID。您可以通过定义路由参数来做到这一点:

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

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

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

路由的参数通常都会被放在 {} ,并且参数名只能为字母。 下划线(_)也可以用于路由参数名中。路由参数会按路由定义的顺序依次注入到路由回调或者控制器中 - ,而不受回调或者控制器的参数名称的影响。

参数和依赖注入

如果您的路由具有依赖关系,而您希望 Laravel 服务容器自动注入到路由的回调中,则应在依赖关系之后列出路由参数:

use Illuminate\Http\Request;

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

可选参数

有时,你可能需要指定一个路由参数,但你希望这个参数是可选的。你可以在参数后面加上 ? 标记来实现,但前提是要确保路由的相应变量有默认值:

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

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

正则表达式约束

你可以使用路由实例上的 where 方法约束路由参数的格式。where 方法接受参数名称和定义参数应如何约束的正则表达式:

Route::get('/user/{name}', function ($name) {
    //
})->where('name', '[A-Za-z]+');

Route::get('/user/{id}', function ($id) {
    //
})->where('id', '[0-9]+');

Route::get('/user/{id}/{name}', function ($id, $name) {
    //
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

为了方便起见,一些常用的正则表达式模式具有帮助程序方法,这些方法可以使你快速向路由添加模式约束:

Route::get('/user/{id}/{name}', function ($id, $name) {
    //
})->whereNumber('id')->whereAlpha('name');

Route::get('/user/{name}', function ($name) {
    //
})->whereAlphaNumeric('name');

Route::get('/user/{id}', function ($id) {
    //
})->whereUuid('id');

如果传入请求与路由模式约束不匹配,则将返回404 HTTP响应。

全局约束

如果你希望某个具体的路由参数都遵循同一个正则表达式的约束,就使用 pattern 方法. 你应该在 App\Providers\RouteServiceProvider 类的 boot 方法中定义这些:

/**
 * 定义你的路由模型绑定, pattern 过滤器等
 *
 * @return void
 */
public function boot()
{
    Route::pattern('id', '[0-9]+');
}

一旦定义好之后,便会自动应用这些规则到所有使用该参数名称的路由上:

Route::get('/user/{id}', function ($id) {
    // 只有在 id 为数字时才执行...
});

编码正斜杠字符

Laravel 路由组件允许除 / 之外的所有字符。你必须使用 where 条件正则表达式显式地允许 / 成为占位符的一部分:

Route::get('/search/{search}', function ($search) {
    return $search;
})->where('search', '.*');

注意:正斜杠字符仅在最后一个路由段中是支持的。

路由命名

路由命名可以方便地为指定路由生成 URL 或者重定向。通过在路由定义上链式调用 name 方法可以指定路由名称:

Route::get('/user/profile', function () {
    //
})->name('profile');

你还可以指定控制器行为的路由名称:

Route::get(
    '/user/profile',
    [UserProfileController::class, 'show']
)->name('profile');

注意:路由命名必须是唯一的

生成指定路由的 URL

一旦为路由指定了名称,你可以在生成url或者通过 Laravel 的 routeredirect 辅助函数重定向时使用路由的名字:

// 生成链接...
$url = route('profile');

// 生成重定向...
return redirect()->route('profile');

如果有定义参数的命名路由,可以把参数作为 route 函数的第二个参数传入,指定的参数将会自动插入到 URL 中对应的位置:

Route::get('/user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = route('profile', ['id' => 1]);

如果在数组中传递其他参数,这些键或值对将自动添加到生成的URL查询字符串中:

Route::get('/user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = route('profile', ['id' => 1, 'photos' => 'yes']);

// /user/1/profile?photos=yes

技巧:有时候,你可能希望为某些 URL 参数的请求范围指定默认值,例如在本地环境,你可以使用 URL::defaults 方法.

检查当前路由

如果你想判断当前请求是否指向了某个命名过的路由,你可以调用路由实例上的 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);
}

路由组

路由组允许你共享路由属性,例如中间件,这样不需要在每个单独的路由上定义那些属性。

嵌套组会尝试智能的“合并”他们的父组的属性。中间件和“where”条件会被合并,而名称和前缀会被追加。URI前缀中的名称空间分隔符和斜杠会在适当的地方自动添加。

中间件

想把 中间件 分配给组内所有的路由,你可以在定义组之前使用middleware方法。中间件将按照它们在数组中列出的顺序执行:

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // 使用 first 和 second 中间件...
    });

    Route::get('/user/profile', function () {
        // 使用 first 和 second 中间件...
    });
});

子域路由

路由组也可以用来处理子域路由。子域可以像路由uri一样被分配路由参数,允许您捕获子域的一部分以便在路由或控制器中使用。 子域可以在定义组之前调用domain方法来指定:

Route::domain('{account}.example.com')->group(function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

注意:为了确保子域路由是可以访问的,你应该在注册根域路由之前注册子域路由。这将防止根域路由覆盖具有相同URI路径的子域路由。

路由前缀

prefix方法可以用给定的URI为组中的每个路由做前缀。例如,你可能想要在组内的所有路由uri前面加上admin前缀:

Route::prefix('admin')->group(function () {
    Route::get('/users', function () {
        // 匹配 「/admin/users」URL
    });
});

路由名称前缀

name 方法可以用给定字符串作为组中的每个路由名的前缀。 例如,你可能想要用 admin 作为所有分组路由的前缀。因为给定字符串的前缀与指定的路由名完全一致,所以我们一定要提供末尾 . 字符在前缀中:

Route::name('admin.')->group(function () {
    Route::get('/users', function () {
        // 指定路由名称「admin.users」...
    })->name('users');
});

路由模型绑定

当向路由或控制器动作注入模型ID时,您通常会查询数据库来检索与该ID对应的模型。Laravel路由模型绑定提供了一种方便的方式,可以自动将模型实例直接注入到路由中。例如,你可以注入与给定ID匹配的整个 User 模型实例,而不是注入用户的ID。

隐式绑定

Laravel自动解析在路由或控制器动作中定义的Eloquent模型,这些模型的类型暗示变量名称与路由段名称相匹配。例如:

use App\Models\User;

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

由于 $user 变量的类型暗示与 App\Models\user Eloquent模型一样,且变量名与 {user} 的URI片段相匹配,Laravel会自动注入与请求URI中对应值ID匹配的模型实例。 如果在数据库中没有找到匹配的模型实例,则会自动生成一个404HTTP响应。

定制缺失的模型行为

通常,如果没有找到隐式绑定模型,将生成 404 HTTP 响应。但是,你可以在定义路由时调用 missing 的方法来定制此行为。missing 的方法接受一个闭包,如果无法找到隐式绑定的模型,该闭包将被调用:

use App\Http\Controllers\LocationsController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;

Route::get('/locations/{location:slug}', [LocationsController::class, 'show'])
        ->name('locations.view')
        ->missing(function (Request $request) {
            return Redirect::route('locations.index');
        });

显式绑定

为了使用模型绑定,你不需要使用 Laravel 的隐式的、基于约定的模型解析。你还可以显式地定义路由参数如何对应于模型。要注册显式绑定,使用路由器的 model 方法为给定的参数指定类。你应该在 RouteServiceProvider 类的 boot 方法的开头定义显式的模型绑定:

use App\Models\User;
use Illuminate\Support\Facades\Route;

/**
 * 定义你的路由模型绑定,模式过滤等等。
 *
 * @return void
 */
public function boot()
{
    Route::model('user', User::class);

    // ...
}

接着,定义一个包含 {user} 参数的路由:

use App\Models\User;

Route::get('/users/{user}', function (User $user) {
    //
});

由于我们已将所有 {user} 参数绑定到 App\Models\User 模型,该类的实例将被注入到路由中。 因此,例如,对 users/1 的请求将从 ID 为 1 的数据库中注入 User 实例。

如果在数据库中没有找到匹配的模型实例,则会自动生成一个 404 HTTP 响应。

定制解析逻辑

如果你希望去定义你自己的模型绑定逻辑,你可以使用 Route::bind 方法。你传递给 bind 方法的闭包将接收 URI 段的值,并且应该返回应该注入路由的类的实例。此外,这种定制应当位于你的应用程序 RouteServiceProviderboot 方法中:

use App\Models\User;
use Illuminate\Support\Facades\Route;

/**
 * 定义你自己的路由模型绑定,模式过滤等等。
 *
 * @return void
 */
public function boot()
{
    Route::bind('user', function ($value) {
        return User::where('name', $value)->firstOrFail();
    });

    // ...
}

另外,你也可以在你的 Eloquent 模型上重写 resolveRouteBinding 方法。该方法将接收 URI 段的值,并返回应该注入路由的类的实例:

/**
 * 检索一个绑定值的模型。
 *
 * @param  mixed  $value
 * @param  string|null  $field
 * @return \Illuminate\Database\Eloquent\Model|null
 */
public function resolveRouteBinding($value, $field = null)
{
    return $this->where('name', $value)->firstOrFail();
}

如果路由使用 隐式绑定作用域,则 resolveChildRouteBinding 方法将用于解析父模型的子绑定:

/**
 * 检索一个绑定值的子模型。
 *
 * @param  string  $childType
 * @param  mixed  $value
 * @param  string|null  $field
 * @return \Illuminate\Database\Eloquent\Model|null
 */
public function resolveChildRouteBinding($childType, $value, $field)
{
    return parent::resolveChildRouteBinding($childType, $value, $field);
}

备选路由

使用 Route :: fallback 方法,你可以定义一个路由,当没有其他路由与传入请求匹配时将执行该路由。通常,未处理的请求将通过你的应用程序的异常处理程序自动渲染『404』页面。但是,由于通常会在 route/web.php 文件中定义 fallback 路由,因此 web 中间件组中的所有中间件都将应用于该路由。你可以根据需要随意向此路由添加其他中间件:

Route::fallback(function () {
    //
});

注意:备选路由应始终是你应用程序注册的最后一个路由。

限流

定义限流器

Laravel 包含功能强大并可定制的限流服务,你可以利用这些服务来限制给定的路由或一组路由的流量。首先,你应该定义满足应用需求的限流器配置。通常,这可以在你的应用程序的 RouteServiceProvider 类的 configureRateLimiting 方法中完成。

限流器可以使用 RateLimiter 门面的 for 方法定义。 该 for 方法接受一个速率限制器名称和一个闭包会返回限制配置,该配置可以应用于分配了该限流器的路由。限制配置是 Illuminate\Cache\RateLimiting\Limit 类的实例。这个类包含有用的『builder 』方法,以便于你可以快速定义你的限制。限流器名称可以是你希望的任何字符串:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

/**
 * 为应用程序配置限流器。
 *
 * @return void
 */
protected function configureRateLimiting()
{
    RateLimiter::for('global', function (Request $request) {
        return Limit::perMinute(1000);
    });
}

如果传入的请求超出了指定的限流,一个带有 429 HTTP 状态码的响应将自动通过 Laravel 返回。如果你想定义应当由一个限流返回的你自己的响应,你可以使用 response 方法:

RateLimiter::for('global', function (Request $request) {
    return Limit::perMinute(1000)->response(function () {
        return response('Custom response...', 429);
    });
});

由于限流器回调接收传入的 HTTP 请求实例,你可以基于传入的请求或经过身份验证的用户动态构建适当的限流:

RateLimiter::for('uploads', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(100);
});

分段限流

有时,你可能希望根据某些任意值分段限流。例如,你可能希望允许用户在每个 IP 地址上每分钟访问给定路由 100 次。要实现这点,你可以在构建你的限流时使用 by 方法:

RateLimiter::for('uploads', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(100)->by($request->ip());
});

多重限流

如果需要,你可以返回一个给定限流器配置的限流数组。每个限流将根据它们在数组中放置的顺序对路由进行评估:

RateLimiter::for('login', function (Request $request) {
    return [
        Limit::perMinute(500),
        Limit::perMinute(3)->by($request->input('email')),
    ];
});

给路由系上限流器

限流器可以使用 throttle 中间件 被系到路由或路由组上。此 throttle 中间件接受你希望分配给路由的限流器名称:

Route::middleware(['throttle:uploads'])->group(function () {
    Route::post('/audio', function () {
        //
    });

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

使用 Redis 限流

通常, throttle 中间件会映射到 Illuminate\Routing\Middleware\ThrottleRequests 类。 此映射在应用程序的 HTTP 内核 (App\Http\Kernel) 中定义。 但是,如果您将 Redis 用作应用程序的缓存驱动程序,则可能需要更改此映射以使用 Illuminate\Routing\Middleware\ThrottleRequestsWithRedis 类。此类在使用 Redis 管理速率限制方面更为有效:

'throttle' => \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,

伪造表单方法

HTML 表单不支持 PUTPATCHDELETE 请求。 所以,当定义 PUTPATCHDELETE 路由用在 HTML 表单时,您将需要一个隐藏的加 _method 字段在表单中。 该 _method 字段的值将会与 HTTP 请求一起发送。

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

为了方便起见,您可以使用 @method Blade 指令 生成 _method 字段:

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

访问当前路由

您可以使用 currentcurrentRouteName, 和 currentRouteAction 对方法 Route Facade 有关处理传入的请求路由访问信息:

use Illuminate\Support\Facades\Route;

$route = Route::current(); // Illuminate\Routing\Route
$name = Route::currentRouteName(); // string
$action = Route::currentRouteAction(); // string

您可以参考 Route Facade 基础类Route 实例 的 API 文档来查看 Route 和 Route 类的所有可用方法。

跨域资源共享 (CORS)

Laravel 可以使用您配置 CORS OPTIONS 的值自动响应 HTTP 请求。可以在应用程序的 config/cors.php 配置文件中配置所有 CORS 设置。 这些 OPTIONS 请求将由默认情况下包含在全局中间件堆栈中的 HandleCors 中间件 自动处理。您的全局中间件堆栈位于应用程序的 HTTP 内核 (App\Http\Kernel) 中。

技巧:有关 CORS 和 CORS 标头的更多信息,请查阅 MDN web 文档上的 CORS 章节

路由缓存

将应用程序部署到生产环境时,应该利用 Laravel 的路由缓存。使用路由缓存将大大减少注册所有应用程序路由所花费的时间。要生成路由缓存,请执行 route:cache Artisan 命令:

php artisan route:cache

运行此命令后,缓存的路由文件将在每个请求中加载。请记住,如果添加任何新路由,则将需要生成新的路由缓存。因此,route:cache 命令最好在生产环境运行。

您可以使用以下 route:clear 命令清除路由缓存:

php artisan route:clear

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/laravel/8.5/rou...

译文地址:https://learnku.com/docs/laravel/8.5/rou...

上一篇 下一篇
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:20
讨论数量: 13
发起讨论 只看当前版本


关于路由里中间件使用的问题?
3 个点赞 | 3 个回复 | 问答 | 课程版本 5.5
xiaocao
路由命名可以加前缀吗?
2 个点赞 | 8 个回复 | 问答 | 课程版本 5.5
ldwang
显式绑定 这块是不是有问题啊?
1 个点赞 | 2 个回复 | 问答 | 课程版本 5.5
骑猪踏白菜
laravel9中config/cors的文件使用详解问题
0 个点赞 | 10 个回复 | 问答 | 课程版本 9.x
hustnzj
路由名称前缀:/admin.users 无法访问?
0 个点赞 | 3 个回复 | 问答 | 课程版本 5.6
SHYBOY
检查当前路由
0 个点赞 | 2 个回复 | 问答 | 课程版本 5.5
hustnzj
隐式绑定和显式绑定有何不同?
0 个点赞 | 2 个回复 | 问答 | 课程版本 5.7
Adolph-Wu
Laravel 5.6 路由 domain 设置 可以设置成域名加端口号吗?
0 个点赞 | 1 个回复 | 问答 | 课程版本 5.6
JohnLan
未声明模型,会报错
0 个点赞 | 0 个回复 | 分享 | 课程版本 5.8