路由
这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。
路由
基础路由
use Illuminate\Support\Facades\Route;
Route::get('/greeting', function () {
return 'Hello World';
});
默认路由文件
所有 Laravel 路由都定义在你的路由文件中,这些文件位于 routes 目录下。Laravel 会根据你的应用程序 bootstrap/app.php 文件中指定的配置自动加载这些文件。routes/web.php 文件定义用于你的 Web 界面的路由。这些路由会被分配到 web 中间件组,该中间件组提供诸如会话状态和 CSRF 保护等功能。
对于大多数应用程序,你将首先在 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',
// ...
)
可用的路由器方法
路由允许你注册响应任何 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,delete和options方法的路由, 然后再定义使用any,match和redirect方法。这能确保传入请求被匹配到正确路由。
依赖注入
你可以在路由回调函数的签名中为所需的依赖项添加类型提示。 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 保留,不能使用:destination和status。
视图路由
如果你的路由只需要返回一个视图,可以使用 Route::view 方法。与 redirect 方法一样,该方法提供了一个简单的快捷方式,这样你就不必定义完整的路由或控制器。view 方法接受一个 URI 作为第一个参数,视图名称作为第二个参数。此外,你还可以提供一个数据数组作为可选的第三个参数,用于传递给视图:
Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => 'Taylor']);
[!警告]
在视图路由中使用路由参数时,以下参数由 Laravel 保留,不能使用:view、data、status和headers。
列出你的路由
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'));
},
)
或者,你甚至可以通过向 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;
});
可选参数
有时你可能需要指定一个不一定总是出现在 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());
如果传入的请求不匹配路由模式约束,将返回一个 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');
[!警告]
路由名称应始终保持唯一。
为命名路由生成 URL
一旦你为某个路由指定了名称,就可以在通过 Laravel 的 route 和 redirect 辅助函数生成 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);
}
路由组
路由组允许你在大量路由之间共享路由属性,例如中间件,而无需在每个单独的路由上定义这些属性。
嵌套路由组会尝试智能地与其父组“合并”属性。中间件和 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) {
// ...
});
});
路由前缀
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;
});
Since the $user variable is type-hinted as the App\Models\User Eloquent model and the variable name matches the {user} URI segment, Laravel will automatically inject the model instance that has an ID matching the corresponding value from the request URI. If a matching model instance is not found in the database, a 404 HTTP response will automatically be generated.
Of course, implicit binding is also possible when using controller methods. Again, note the {user} URI segment matches the $user variable in the controller which contains an App\Models\User type-hint:
use App\Http\Controllers\UserController;
use App\Models\User;
// Route definition...
Route::get('/users/{user}', [UserController::class, 'show']);
// Controller method definition...
public function show(User $user)
{
return view('user.profile', ['user' => $user]);
}
Soft Deleted Models
Typically, implicit model binding will not retrieve models that have been soft deleted. However, you may instruct the implicit binding to retrieve these models by chaining the withTrashed method onto your route's definition:
use App\Models\User;
Route::get('/users/{user}', function (User $user) {
return $user->email;
})->withTrashed();
Customizing the Key
Sometimes you may wish to resolve Eloquent models using a column other than id. To do so, you may specify the column in the route parameter definition:
use App\Models\Post;
Route::get('/posts/{post:slug}', function (Post $post) {
return $post;
});
If you would like model binding to always use a database column other than id when retrieving a given model class, you may override the getRouteKeyName method on the Eloquent model:
/**
* Get the route key for the model.
*/
public function getRouteKeyName(): string
{
return 'slug';
}
Custom Keys and Scoping
When implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the previous Eloquent model. For example, consider this route definition that retrieves a blog post by slug for a specific user:
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
return $post;
});
When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. In this case, it will be assumed that the User model has a relationship named posts (the plural form of the route parameter name) which can be used to retrieve the Post model.
If you wish, you may instruct Laravel to scope "child" bindings even when a custom key is not provided. To do so, you may invoke the scopeBindings method when defining your route:
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
})->scopeBindings();
Or, you may instruct an entire group of route definitions to use scoped bindings:
Route::scopeBindings()->group(function () {
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
});
});
Similarly, you may explicitly instruct Laravel to not scope bindings by invoking the withoutScopedBindings method:
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
return $post;
})->withoutScopedBindings();
Customizing Missing Model Behavior
Typically, a 404 HTTP response will be generated if an implicitly bound model is not found. However, you may customize this behavior by calling the missing method when defining your route. The missing method accepts a closure that will be invoked if an implicitly bound model cannot be found:
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');
});
Implicit Enum Binding
PHP 8.1 introduced support for Enums. To complement this feature, Laravel allows you to type-hint a string-backed Enum on your route definition and Laravel will only invoke the route if that route segment corresponds to a valid Enum value. Otherwise, a 404 HTTP response will be returned automatically. For example, given the following Enum:
<?php
namespace App\Enums;
enum Category: string
{
case Fruits = 'fruits';
case People = 'people';
}
You may define a route that will only be invoked if the {category} route segment is fruits or people. Otherwise, Laravel will return a 404 HTTP response:
use App\Enums\Category;
use Illuminate\Support\Facades\Route;
Route::get('/categories/{category}', function (Category $category) {
return $category->value;
});
Explicit Binding
You are not required to use Laravel's implicit, convention based model resolution in order to use model binding. You can also explicitly define how route parameters correspond to models. To register an explicit binding, use the router's model method to specify the class for a given parameter. You should define your explicit model bindings at the beginning of the boot method of your AppServiceProvider class:
use App\Models\User;
use Illuminate\Support\Facades\Route;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::model('user', User::class);
}
Next, define a route that contains a {user} parameter:
use App\Models\User;
Route::get('/users/{user}', function (User $user) {
// ...
});
Since we have bound all {user} parameters to the App\Models\User model, an instance of that class will be injected into the route. So, for example, a request to users/1 will inject the User instance from the database which has an ID of 1.
If a matching model instance is not found in the database, a 404 HTTP response will be automatically generated.
Customizing the Resolution Logic
If you wish to define your own model binding resolution logic, you may use the Route::bind method. The closure you pass to the bind method will receive the value of the URI segment and should return the instance of the class that should be injected into the route. Again, this customization should take place in the boot method of your application's AppServiceProvider:
use App\Models\User;
use Illuminate\Support\Facades\Route;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::bind('user', function (string $value) {
return User::where('name', $value)->firstOrFail();
});
}
Alternatively, you may override the resolveRouteBinding method on your Eloquent model. This method will receive the value of the URI segment and should return the instance of the class that should be injected into the route:
/**
* Retrieve the model for a bound value.
*
* @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();
}
If a route is utilizing implicit binding scoping, the resolveChildRouteBinding method will be used to resolve the child binding of the parent model:
/**
* Retrieve the child model for a bound value.
*
* @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);
}
Fallback Routes
Using the Route::fallback method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you would typically define the fallback route within your routes/web.php file, all middleware in the web middleware group will apply to the route. You are free to add additional middleware to this route as needed:
Route::fallback(function () {
// ...
});
Rate Limiting
Defining Rate Limiters
Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs.
Rate limiters may be defined within the boot method of your application's App\Providers\AppServiceProvider class:
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());
});
}
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
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
Laravel 13 中文文档
关于 LearnKu
推荐文章: