翻译进度
20
分块数量
3
参与人数

路由

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。


路由

基础路由

use Illuminate\Support\Facades\Route;

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

默认路由文件

所有 Laravel 路由都定义在你的路由文件中,这些文件位于 routes 目录下。Laravel 会根据你的应用程序 bootstrap/app.php 文件中指定的配置自动加载这些文件。routes/web.php 文件定义用于你的 Web 界面的路由。这些路由会被分配到 web 中间件组,该中间件组提供诸如会话状态和 CSRF 保护等功能。

无与伦比 翻译于 6天前

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

use App\Http\Controllers\UserController;

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

API Routes

如果你的应用程序还将提供无状态 API,你可以使用 install:api Artisan 命令启用 API 路由:

php artisan install:api

install:api 命令安装了 Laravel Sanctum,它提供了一个强大而简单的 API 令牌认证守卫,可用于对第三方 API 使用者、SPAs 或移动应用程序进行身份验证。此外,install:api 命令创建了 routes/api.php 文件:

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

当然,对于应公开访问的路由,你可以自由省略 auth:sanctum 中间件。

routes/api.php 中的路由是无状态的,并分配给 api 中间件组。此外,/api URI 前缀会自动应用于这些路由,因此你无需手动将其应用于文件中的每个路由。你可以通过修改应用程序的 bootstrap/app.php 文件来更改前缀:

->withRouting(
    api: __DIR__.'/../routes/api.php',
    apiPrefix: 'api/admin',
    // ...
)

可用的路由器方法

dkp 翻译于 3周前

路由允许你注册响应任何 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 () {
    // ...
});

[!注意]
在定义共享相同 URI 配置项的多个路由时,必须优先定义使用 get, post, put, patch, deleteoptions 方法的路由, 然后再定义使用 any, matchredirect 方法。这能确保传入请求被匹配到正确路由。

依赖注入

你可以在路由回调函数的签名中为所需的依赖项添加类型提示。 Laravel 服务容器 会自动解析并注入这些声明的依赖项到回调函数中。例如,你.可以引用 Illuminate\Http\Request 类,这样当前的 HTTP 请求就会自动注入到你的路由回调函数中

use Illuminate\Http\Request;

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

CSRF 防护

请注意,任何指向 web 路由文件中定义的 POST, PUT, PATCHDELETE路由的 HTML 表单都应包含 CSRF 令牌字段。否则,请求将被拒绝。你可以在 CSRF 文档 中了解更多关于 CSRF 保护的信息:

<form method="POST" action="/profile">
    @csrf
    ...
</form>
haodudecao 翻译于 3周前

重定向路由

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

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

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

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

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

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

[!警告]
在重定向路由中使用路由参数时,以下参数由 Laravel 保留,不能使用:destinationstatus

视图路由

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

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

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

[!警告]
在视图路由中使用路由参数时,以下参数由 Laravel 保留,不能使用:viewdatastatusheaders

列出你的路由

无与伦比 翻译于 6天前

route:list Artisan 命令可以轻松提供你的应用程序所定义的所有路由的概览:

php artisan route:list

默认情况下,分配给每个路由的路由中间件不会显示在 route:list 输出中;不过,你可以通过在命令中添加 -v 选项,指示 Laravel 显示路由中间件和中间件组名称:

php artisan route:list -v

# 展开中间件组...
php artisan route:list -vv

你还可以指示 Laravel 只显示以给定 URI 开头的路由:

php artisan route:list --path=api

此外,在执行 route:list 命令时,你可以通过提供 --except-vendor 选项,指示 Laravel 隐藏任何由第三方包定义的路由:

php artisan route:list --except-vendor

同样,在执行 route:list 命令时,你也可以通过提供 --only-vendor 选项,指示 Laravel 只显示由第三方包定义的路由:

php artisan route:list --only-vendor

路由自定义

默认情况下,你的应用程序路由由 bootstrap/app.php 文件进行配置和加载:

<?php

use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )->create();

然而,有时你可能想要定义一个全新的文件来包含应用程序路由的一个子集。要实现这一点,你可以向 withRouting 方法提供一个 then 闭包。在这个闭包中,你可以注册应用程序所需的任何其他路由:

use Illuminate\Support\Facades\Route;

->withRouting(
    web: __DIR__.'/../routes/web.php',
    commands: __DIR__.'/../routes/console.php',
    health: '/up',
    then: function () {
        Route::middleware('api')
            ->prefix('webhooks')
            ->name('webhooks.')
            ->group(base_path('routes/webhooks.php'));
    },
)
无与伦比 翻译于 6天前

或者,你甚至可以通过向 withRouting 方法提供一个 using 闭包来完全控制路由注册。当传入此参数时,框架将不会注册任何 HTTP 路由,你需要负责手动注册所有路由:

use Illuminate\Support\Facades\Route;

->withRouting(
    commands: __DIR__.'/../routes/console.php',
    using: function () {
        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));

        Route::middleware('web')
            ->group(base_path('routes/web.php'));
    },
)

路由参数

必填参数

有时你需要在路由中捕获 URI 的片段。例如,你可能需要从 URL 中捕获用户的 ID。你可以通过定义路由参数来实现:

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

你可以根据路由需要定义任意数量的路由参数:

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

路由参数始终包裹在 {} 大括号中,并且应由字母字符组成。路由参数名称中也可以使用下划线(_)。路由参数会根据它们的顺序被注入到路由回调 / 控制器中——路由回调 / 控制器参数的名称并不重要。

参数和依赖注入

如果你的路由有一些依赖项,希望 Laravel 服务容器自动将它们注入到路由回调中,则应将路由参数列在依赖项之后:

use Illuminate\Http\Request;

Route::get('/user/{id}', function (Request $request, string $id) {
    return 'User '.$id;
});
无与伦比 翻译于 6天前

可选参数

有时你可能需要指定一个不一定总是出现在 URI 中的路由参数。你可以通过在参数名称后放置一个 ? 标记来实现。请确保为该路由对应的变量提供一个默认值:

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

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

正则表达式约束

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

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

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

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

为方便起见,一些常用的正则表达式模式提供了辅助方法,使你可以快速为路由添加模式约束:

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

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

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

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

Route::get('/category/{category}', function (string $category) {
    // ...
})->whereIn('category', ['movie', 'song', 'painting']);

Route::get('/category/{category}', function (string $category) {
    // ...
})->whereIn('category', CategoryEnum::cases());
无与伦比 翻译于 6天前

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

全局约束

如果你希望某个路由参数始终受给定正则表达式的约束,可以使用 pattern 方法。你应该在应用程序的 App\Providers\AppServiceProvider 类的 boot 方法中定义这些模式:

use Illuminate\Support\Facades\Route;

/**
 * 引导任何应用程序服务。
 */
public function boot(): void
{
    Route::pattern('id', '[0-9]+');
}

一旦定义了该模式,它就会自动应用到所有使用该参数名称的路由:

Route::get('/user/{id}', function (string $id) {
    // 仅当 {id} 为数字时才会执行...
});

编码的正斜杠

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

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

[!警告]
编码的正斜杠仅在最后一个路由段中受支持。

命名路由

命名路由允许你方便地为特定路由生成 URL 或重定向。你可以通过在路由定义后链式调用 name 方法来为路由指定名称:

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

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

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

[!警告]
路由名称应始终保持唯一。

无与伦比 翻译于 5天前

为命名路由生成 URL

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

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

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

return to_route('profile');

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

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

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

如果你在数组中传递了额外的参数,这些键 / 值对会自动添加到生成 URL 的查询字符串中:

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

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

// http://example.com/user/1/profile?photos=yes

[!注意]
有时,你可能希望为 URL 参数指定请求范围内的默认值,例如当前语言环境。要实现这一点,可以使用 URL::defaults 方法

检查当前路由

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

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * 处理传入的请求。
 *
 * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
 */
public function handle(Request $request, Closure $next): Response
{
    if ($request->route()->named('profile')) {
        // ...
    }

    return $next($request);
}
无与伦比 翻译于 5天前

路由组

路由组允许你在大量路由之间共享路由属性,例如中间件,而无需在每个单独的路由上定义这些属性。

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

中间件

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

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

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

控制器

如果一组路由都使用同一个控制器,你可以使用 controller 方法为组内所有路由定义通用控制器。然后,在定义路由时,你只需要提供它们所调用的控制器方法:

use App\Http\Controllers\OrderController;

Route::controller(OrderController::class)->group(function () {
    Route::get('/orders/{id}', 'show');
    Route::post('/orders', 'store');
});

子域名路由

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

Route::domain('{account}.example.com')->group(function () {
    Route::get('/user/{id}', function (string $account, string $id) {
        // ...
    });
});
无与伦比 翻译于 5天前

路由前缀

prefix 方法可用于为组中的每个路由添加给定的 URI 前缀。例如,你可能想为组内所有路由 URI 添加 admin 前缀:

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

路由名称前缀

name 方法可用于为组中的每个路由名称添加给定字符串作为前缀。例如,你可能想为组内所有路由的名称添加 admin 前缀。给定字符串会按照指定的内容原样添加到路由名称前,因此我们需要确保在前缀中提供尾随的 . 字符:

Route::name('admin.')->group(function () {
    Route::get('/users', function () {
        // Route assigned name "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;
});
无与伦比 翻译于 5天前

由于 $user 变量被类型提示为 App\Models\User Eloquent 模型,并且变量名称与 {user} URI 片段相匹配,因此 Laravel 会自动注入与请求 URI 中对应值匹配的 ID 的模型实例。如果在数据库中找不到匹配的模型实例,将会自动生成 404 HTTP 响应。

当然,在使用控制器方法时,也可以进行隐式绑定。同样,请注意 {user} URI 片段与控制器中的 $user 变量相匹配,而该变量包含 App\Models\User 类型提示:

use App\Http\Controllers\UserController;
use App\Models\User;

// 路由定义...
Route::get('/users/{user}', [UserController::class, 'show']);

// 控制器方法定义...
public function show(User $user)
{
    return view('user.profile', ['user' => $user]);
}

软删除模型

通常,隐式模型绑定不会检索已被软删除的模型。但是,你可以通过在路由定义上链式调用 withTrashed 方法,指示隐式绑定检索这些模型:

use App\Models\User;

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

自定义键

有时,你可能希望使用 id 以外的列来解析 Eloquent 模型。为此,你可以在路由参数定义中指定该列:

use App\Models\Post;

Route::get('/posts/{post:slug}', function (Post $post) {
    return $post;
});

如果你希望在检索给定模型类时,模型绑定始终使用 id 以外的数据库列,则可以在 Eloquent 模型上重写 getRouteKeyName 方法:

/**
 * Get the route key for the model.
 */
public function getRouteKeyName(): string
{
    return 'slug';
}
无与伦比 翻译于 13小时前

自定义键和作用域限定

当在单个路由定义中隐式绑定多个 Eloquent 模型时,你可能希望对第二个 Eloquent 模型进行作用域限定,使其必须是前一个 Eloquent 模型的子级。例如,请看下面这个路由定义,它会根据特定用户的 slug 检索一篇博客文章:

use App\Models\Post;
use App\Models\User;

Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
    return $post;
});

当使用自定义键的隐式绑定作为嵌套路由参数时,Laravel 会自动限定查询范围,通过父模型来检索嵌套模型,并使用约定来猜测父模型上的关系名称。在这个例子中,Laravel 会假定 User 模型有一个名为 posts 的关系,即路由参数名称的复数形式,该关系可用于检索 Post 模型。

如果你愿意,也可以指示 Laravel 即使在未提供自定义键的情况下,也对“子级”绑定进行作用域限定。为此,你可以在定义路由时调用 scopeBindings 方法:

use App\Models\Post;
use App\Models\User;

Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
    return $post;
})->scopeBindings();

或者,你也可以指示整个路由定义组使用作用域绑定:

Route::scopeBindings()->group(function () {
    Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
        return $post;
    });
});

类似地,你也可以通过调用 withoutScopedBindings 方法,明确指示 Laravel 不要对绑定进行作用域限定:

Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
    return $post;
})->withoutScopedBindings();
无与伦比 翻译于 13小时前

自定义缺失模型的行为

通常,如果找不到隐式绑定的模型,将会生成 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');
    });

隐式枚举绑定

PHP 8.1 引入了对 Enums 的支持。为了配合这一特性,Laravel 允许你在路由定义中对字符串支持的 Enum 进行类型提示,并且 Laravel 只有在该路由片段对应有效的 Enum 值时才会调用该路由。否则,将自动返回 404 HTTP 响应。例如,给定以下 Enum:

<?php

namespace App\Enums;

enum Category: string
{
    case Fruits = 'fruits';
    case People = 'people';
}

你可以定义一个路由,只有当 {category} 路由片段为 fruitspeople 时才会被调用。否则,Laravel 将返回 404 HTTP 响应:

use App\Enums\Category;
use Illuminate\Support\Facades\Route;

Route::get('/categories/{category}', function (Category $category) {
    return $category->value;
});

显式绑定

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

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

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Route::model('user', User::class);
}
无与伦比 翻译于 13小时前

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

use App\Models\User;

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

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

如果在数据库中找不到匹配的模型实例,将会自动生成 404 HTTP 响应。

自定义解析逻辑

如果你希望定义自己的模型绑定解析逻辑,可以使用 Route::bind 方法。你传递给 bind 方法的闭包将接收 URI 片段的值,并应返回应该被注入到路由中的类实例。同样,此自定义操作应该在你的应用程序 AppServiceProviderboot 方法中进行:

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

/**
 * 引导任何应用服务。
 */
public function boot(): void
{
    Route::bind('user', function (string $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();
}
无与伦比 翻译于 13小时前

如果路由正在使用隐式绑定作用域限定,则会使用 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” 页面。不过,由于你通常会在 routes/web.php 文件中定义 fallback 路由,因此 web 中间件组中的所有中间件都会应用到该路由。你可以根据需要自由地向该路由添加额外的中间件:

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

速率限制

定义速率限制器

Laravel 包含强大且可自定义的速率限制服务,你可以使用它们来限制给定路由或路由组的流量。首先,你应该定义符合应用程序需求的速率限制器配置。

速率限制器可以在你的应用程序 App\Providers\AppServiceProvider 类的 boot 方法中定义:

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

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });
}
无与伦比 翻译于 13小时前

Rate limiters are defined using the RateLimiter facade's for method. The for method accepts a rate limiter name and a closure that returns the limit configuration that should apply to routes that are assigned to the rate limiter. Limit configuration are instances of the Illuminate\Cache\RateLimiting\Limit class. This class contains helpful "builder" methods so that you can quickly define your limit. The rate limiter name may be any string you wish:

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

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    RateLimiter::for('global', function (Request $request) {
        return Limit::perMinute(1000);
    });
}

If the incoming request exceeds the specified rate limit, a response with a 429 HTTP status code will automatically be returned by Laravel. If you would like to define your own response that should be returned by a rate limit, you may use the response method:

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

Since rate limiter callbacks receive the incoming HTTP request instance, you may build the appropriate rate limit dynamically based on the incoming request or authenticated user:

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

Segmenting Rate Limits

Sometimes you may wish to segment rate limits by some arbitrary value. For example, you may wish to allow users to access a given route 100 times per minute per IP address. To accomplish this, you may use the by method when building your rate limit:

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

To illustrate this feature using another example, we can limit access to the route to 100 times per minute per authenticated user ID or 10 times per minute per IP address for guests:

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

Multiple Rate Limits

If needed, you may return an array of rate limits for a given rate limiter configuration. Each rate limit will be evaluated for the route based on the order they are placed within the array:

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

If you're assigning multiple rate limits segmented by identical by values, you should ensure that each by value is unique. The easiest way to achieve this is to prefix the values given to the by method:

RateLimiter::for('uploads', function (Request $request) {
    return [
        Limit::perMinute(10)->by('minute:'.$request->user()->id),
        Limit::perDay(1000)->by('day:'.$request->user()->id),
    ];
});

Response-Based Rate Limiting

In addition to rate limiting incoming requests, Laravel allows you to rate limit based on the response using the after method. This is useful when you only want to count certain responses toward the rate limit, such as validation errors, 404 responses, or other specific HTTP status codes.

The after method accepts a closure that receives the response and should return true if the response should be counted toward the rate limit, or false if it should be ignored. This is particularly useful for preventing enumeration attacks by limiting consecutive 404 responses, or allowing users to retry requests that fail validation without exhausting their rate limit on an endpoint that should only throttle successful operations:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Symfony\Component\HttpFoundation\Response;

RateLimiter::for('resource-not-found', function (Request $request) {
    return Limit::perMinute(10)
        ->by($request->user()?->id ?: $request->ip())
        ->after(function (Response $response) {
            // Only count 404 responses toward the rate limit to prevent enumeration...
            return $response->status() === 404;
        });
});

Attaching Rate Limiters to Routes

Rate limiters may be attached to routes or route groups using the throttle middleware. The throttle middleware accepts the name of the rate limiter you wish to assign to the route:

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

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

Throttling With Redis

By default, the throttle middleware is mapped to the Illuminate\Routing\Middleware\ThrottleRequests class. However, if you are using Redis as your application's cache driver, you may wish to instruct Laravel to use Redis to manage rate limiting. To do so, you should use the throttleWithRedis method in your application's bootstrap/app.php file. This method maps the throttle middleware to the Illuminate\Routing\Middleware\ThrottleRequestsWithRedis middleware class:

->withMiddleware(function (Middleware $middleware): void {
    $middleware->throttleWithRedis();
    // ...
})

Form Method Spoofing

HTML forms do not support PUT, PATCH, or DELETE actions. So, when defining PUT, PATCH, or DELETE routes that are called from an HTML form, you will need to add a hidden _method field to the form. The value sent with the _method field will be used as the HTTP request method:

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

For convenience, you may use the @method Blade directive to generate the _method input field:

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

Accessing the Current Route

You may use the current, currentRouteName, and currentRouteAction methods on the Route facade to access information about the route handling the incoming request:

use Illuminate\Support\Facades\Route;

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

You may refer to the API documentation for both the underlying class of the Route facade and Route instance to review all of the methods that are available on the router and route classes.

Cross-Origin Resource Sharing (CORS)

Laravel can automatically respond to CORS OPTIONS HTTP requests with values that you configure. The OPTIONS requests will automatically be handled by the HandleCors middleware that is automatically included in your application's global middleware stack.

Sometimes, you may need to customize the CORS configuration values for your application. You may do so by publishing the cors configuration file using the config:publish Artisan command:

php artisan config:publish cors

This command will place a cors.php configuration file within your application's config directory.

[!NOTE]
For more information on CORS and CORS headers, please consult the MDN web documentation on CORS.

Route Caching

When deploying your application to production, you should take advantage of Laravel's route cache. Using the route cache will drastically decrease the amount of time it takes to register all of your application's routes. To generate a route cache, execute the route:cache Artisan command:

php artisan route:cache

After running this command, your cached routes file will be loaded on every request. Remember, if you add any new routes you will need to generate a fresh route cache. Because of this, you should only run the route:cache command during your project's deployment.

You may use the route:clear command to clear the route cache:

php artisan route:clear

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

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

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:3