HTTP 路由
基本路由
您将在 app/Http/routes.php
文件中定义应用程序的大多数路由,该文件由 App\Providers\RouteServiceProvider
类加载。最基本的 Laravel 路由只需接受一个 URI 和一个 Closure
:
基本 GET 路由
Route::get('/', function()
{
return 'Hello World';
});
其他基本路由
Route::post('foo/bar', function()
{
return 'Hello World';
});
Route::put('foo/bar', function()
{
//
});
Route::delete('foo/bar', function()
{
//
});
为多个动词注册路由
Route::match(['get', 'post'], '/', function()
{
return 'Hello World';
});
注册响应任何 HTTP 动词的路由
Route::any('foo', function()
{
return 'Hello World';
});
通常,您需要生成指向路由的 URL,可以使用 url
辅助函数:
$url = url('foo');
CSRF 保护
Laravel 使得保护应用程序免受跨站请求伪造变得简单。跨站请求伪造是一种恶意攻击,未经授权的命令在经过身份验证的用户的名义下执行。
Laravel 自动为应用程序管理的每个活动用户会话生成一个 CSRF "令牌"。此令牌用于验证经过身份验证的用户确实是向应用程序发出请求的用户。
将 CSRF 令牌插入表单
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
当然,使用 Blade 模板引擎:
<input type="hidden" name="_token" value="{{ csrf_token() }}">
您不需要手动验证 POST、PUT 或 DELETE 请求的 CSRF 令牌。VerifyCsrfToken
HTTP 中间件 将验证请求输入中的令牌是否与会话中存储的令牌匹配。
X-CSRF-TOKEN
除了将 CSRF 令牌作为 "POST" 参数查找外,中间件还会检查 X-CSRF-TOKEN
请求头。您可以,例如,将令牌存储在 "meta" 标签中,并指示 jQuery 将其添加到所有请求头中:
<meta name="csrf-token" content="{{ csrf_token() }}" />
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
现在,所有 AJAX 请求将自动包含 CSRF 令牌:
$.ajax({
url: "/foo/bar",
})
X-XSRF-TOKEN
Laravel 还将 CSRF 令牌存储在 XSRF-TOKEN
cookie 中。您可以使用 cookie 值设置 X-XSRF-TOKEN
请求头。一些 JavaScript 框架,如 Angular,会自动为您执行此操作。
注意:
X-CSRF-TOKEN
和X-XSRF-TOKEN
的区别在于,前者使用纯文本值,而后者使用加密值,因为 Laravel 中的 cookie 始终是加密的。如果您使用csrf_token()
函数提供令牌值,您可能希望使用X-CSRF-TOKEN
头。
方法欺骗
HTML 表单不支持 PUT
、PATCH
或 DELETE
操作。因此,当定义从 HTML 表单调用的 PUT
、PATCH
或 DELETE
路由时,您需要在表单中添加一个隐藏的 _method
字段。
发送的 _method
字段的值将用作 HTTP 请求方法。例如:
<form action="/foo/bar" method="POST">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
</form>
路由参数
当然,您可以在路由中捕获请求 URI 的段:
基本路由参数
Route::get('user/{id}', function($id)
{
return 'User '.$id;
});
路由参数不能包含 -
字符。请使用下划线 (_
) 代替。
可选路由参数
Route::get('user/{name?}', function($name = null)
{
return $name;
});
带默认值的可选路由参数
Route::get('user/{name?}', function($name = 'John')
{
return $name;
});
正则表达式参数约束
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]+']);
定义全局模式
如果您希望路由参数始终受给定正则表达式的约束,可以使用 pattern
方法。您应该在 RouteServiceProvider
的 boot
方法中定义这些模式:
$router->pattern('id', '[0-9]+');
一旦定义了模式,它将应用于使用该参数的所有路由:
Route::get('user/{id}', function($id)
{
// 仅在 {id} 为数字时调用。
});
访问路由参数值
如果您需要在路由之外访问路由参数值,请使用 input
方法:
if ($route->input('id') == 1)
{
//
}
您还可以通过 Illuminate\Http\Request
实例访问当前路由参数。可以通过 Request
facade 访问当前请求的请求实例,或者通过在依赖项注入时类型提示 Illuminate\Http\Request
:
use Illuminate\Http\Request;
Route::get('user/{id}', function(Request $request, $id)
{
if ($request->route('id'))
{
//
}
});
命名路由
命名路由允许您方便地为特定路由生成 URL 或重定向。您可以使用 as
数组键为路由指定名称:
Route::get('user/profile', ['as' => 'profile', function()
{
//
}]);
您还可以为控制器操作指定路由名称:
Route::get('user/profile', [
'as' => 'profile', 'uses' => 'UserController@showProfile'
]);
现在,您可以在生成 URL 或重定向时使用路由的名称:
$url = route('profile');
$redirect = redirect()->route('profile');
currentRouteName
方法返回处理当前请求的路由名称:
$name = Route::currentRouteName();
路由组
有时,您的许多路由将共享共同的要求,例如 URL 段、中间件、命名空间等。您可以使用路由组来将属性应用于许多路由,而不是在每个路由上单独指定这些选项。
共享属性以数组格式作为 Route::group
方法的第一个参数指定。
中间件
通过在组属性数组中使用 middleware
参数定义中间件列表,可以将中间件应用于组内的所有路由。中间件将按照您定义的顺序执行:
Route::group(['middleware' => ['foo', 'bar']], function()
{
Route::get('/', function()
{
// 具有 Foo 和 Bar 中间件
});
Route::get('user/profile', function()
{
// 具有 Foo 和 Bar 中间件
});
});
命名空间
您可以在组属性数组中使用 namespace
参数为组内的所有控制器指定命名空间:
Route::group(['namespace' => 'Admin'], function()
{
// "App\Http\Controllers\Admin" 命名空间内的控制器
Route::group(['namespace' => 'User'], function()
{
// "App\Http\Controllers\Admin\User" 命名空间内的控制器
});
});
默认情况下,RouteServiceProvider
在命名空间组中包含您的 routes.php
文件,允许您注册控制器路由而无需指定完整的 App\Http\Controllers
命名空间前缀。
子域路由
Laravel 路由还处理通配符子域,并将通配符参数从域传递:
注册子域路由
Route::group(['domain' => '{account}.myapp.com'], function()
{
Route::get('user/{id}', function($account, $id)
{
//
});
});
路由前缀
可以通过在组的属性数组中使用 prefix
选项为一组路由添加前缀:
Route::group(['prefix' => 'admin'], function()
{
Route::get('users', function()
{
// 匹配 "/admin/users" URL
});
});
您还可以利用 prefix
参数将常用参数传递给路由:
在路由前缀中注册 URL 参数
Route::group(['prefix' => 'accounts/{account_id}'], function()
{
Route::get('detail', function($account_id)
{
//
});
});
您甚至可以为前缀中的命名参数定义参数约束:
Route::group([
'prefix' => 'accounts/{account_id}',
'where' => ['account_id' => '[0-9]+'],
], function() {
// 在此处定义路由
});
路由模型绑定
Laravel 模型绑定提供了一种方便的方法将类实例注入到路由中。例如,您可以注入与给定 ID 匹配的整个 User 类实例,而不是注入用户的 ID。
首先,使用路由器的 model
方法为给定参数指定类。您应该在 RouteServiceProvider::boot
方法中定义模型绑定:
将参数绑定到模型
public function boot(Router $router)
{
parent::boot($router);
$router->model('user', 'App\User');
}
接下来,定义一个包含 {user}
参数的路由:
Route::get('profile/{user}', function(App\User $user)
{
//
});
由于我们已将 {user}
参数绑定到 App\User
模型,因此将 User
实例注入到路由中。例如,请求 profile/1
将注入 ID 为 1 的 User
实例。
如果在数据库中找不到匹配的模型实例,将抛出 404 错误。
如果您希望指定自己的 "未找到" 行为,请将闭包作为第三个参数传递给 model
方法:
Route::model('user', 'User', function()
{
throw new NotFoundHttpException;
});
如果您希望使用自己的解析逻辑,应使用 Route::bind
方法。传递给 bind
方法的闭包将接收 URI 段的值,并应返回要注入到路由中的类实例:
Route::bind('user', function($value)
{
return User::where('name', $value)->first();
});
抛出 404 错误
有两种方法可以从路由手动触发 404 错误。首先,您可以使用 abort
辅助函数:
abort(404);
abort
辅助函数只是抛出一个具有指定状态码的 Symfony\Component\HttpKernel\Exception\HttpException
。
其次,您可以手动抛出 Symfony\Component\HttpKernel\Exception\NotFoundHttpException
的实例。
有关处理 404 异常和为这些错误使用自定义响应的更多信息,请参阅文档的错误部分。