Skip to content

HTTP 路由

基本路由

您将在 app/Http/routes.php 文件中定义应用程序的大多数路由,该文件由 App\Providers\RouteServiceProvider 类加载。最基本的 Laravel 路由只需接受一个 URI 和一个 Closure

基本 GET 路由

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

其他基本路由

php
Route::post('foo/bar', function()
{
	return 'Hello World';
});

Route::put('foo/bar', function()
{
	//
});

Route::delete('foo/bar', function()
{
	//
});

为多个动词注册路由

php
Route::match(['get', 'post'], '/', function()
{
	return 'Hello World';
});

注册响应任何 HTTP 动词的路由

php
Route::any('foo', function()
{
	return 'Hello World';
});

通常,您需要生成指向路由的 URL,可以使用 url 辅助函数:

php
$url = url('foo');

CSRF 保护

Laravel 使得保护应用程序免受跨站请求伪造变得简单。跨站请求伪造是一种恶意攻击,未经授权的命令在经过身份验证的用户的名义下执行。

Laravel 自动为应用程序管理的每个活动用户会话生成一个 CSRF "令牌"。此令牌用于验证经过身份验证的用户确实是向应用程序发出请求的用户。

将 CSRF 令牌插入表单

php
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">

当然,使用 Blade 模板引擎

php
<input type="hidden" name="_token" value="{{ csrf_token() }}">

您不需要手动验证 POST、PUT 或 DELETE 请求的 CSRF 令牌。VerifyCsrfToken HTTP 中间件 将验证请求输入中的令牌是否与会话中存储的令牌匹配。

X-CSRF-TOKEN

除了将 CSRF 令牌作为 "POST" 参数查找外,中间件还会检查 X-CSRF-TOKEN 请求头。您可以,例如,将令牌存储在 "meta" 标签中,并指示 jQuery 将其添加到所有请求头中:

php
<meta name="csrf-token" content="{{ csrf_token() }}" />

$.ajaxSetup({
		headers: {
			'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
		}
	});

现在,所有 AJAX 请求将自动包含 CSRF 令牌:

php
$.ajax({
   url: "/foo/bar",
})

X-XSRF-TOKEN

Laravel 还将 CSRF 令牌存储在 XSRF-TOKEN cookie 中。您可以使用 cookie 值设置 X-XSRF-TOKEN 请求头。一些 JavaScript 框架,如 Angular,会自动为您执行此操作。

注意:X-CSRF-TOKENX-XSRF-TOKEN 的区别在于,前者使用纯文本值,而后者使用加密值,因为 Laravel 中的 cookie 始终是加密的。如果您使用 csrf_token() 函数提供令牌值,您可能希望使用 X-CSRF-TOKEN 头。

方法欺骗

HTML 表单不支持 PUTPATCHDELETE 操作。因此,当定义从 HTML 表单调用的 PUTPATCHDELETE 路由时,您需要在表单中添加一个隐藏的 _method 字段。

发送的 _method 字段的值将用作 HTTP 请求方法。例如:

php
<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 的段:

基本路由参数

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

路由参数不能包含 - 字符。请使用下划线 (_) 代替。

可选路由参数

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

带默认值的可选路由参数

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

正则表达式参数约束

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

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

传递约束数组

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

定义全局模式

如果您希望路由参数始终受给定正则表达式的约束,可以使用 pattern 方法。您应该在 RouteServiceProviderboot 方法中定义这些模式:

php
$router->pattern('id', '[0-9]+');

一旦定义了模式,它将应用于使用该参数的所有路由:

php
Route::get('user/{id}', function($id)
{
	// 仅在 {id} 为数字时调用。
});

访问路由参数值

如果您需要在路由之外访问路由参数值,请使用 input 方法:

php
if ($route->input('id') == 1)
{
	//
}

您还可以通过 Illuminate\Http\Request 实例访问当前路由参数。可以通过 Request facade 访问当前请求的请求实例,或者通过在依赖项注入时类型提示 Illuminate\Http\Request

php
use Illuminate\Http\Request;

Route::get('user/{id}', function(Request $request, $id)
{
	if ($request->route('id'))
	{
		//
	}
});

命名路由

命名路由允许您方便地为特定路由生成 URL 或重定向。您可以使用 as 数组键为路由指定名称:

php
Route::get('user/profile', ['as' => 'profile', function()
{
	//
}]);

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

php
Route::get('user/profile', [
	'as' => 'profile', 'uses' => 'UserController@showProfile'
]);

现在,您可以在生成 URL 或重定向时使用路由的名称:

php
$url = route('profile');

$redirect = redirect()->route('profile');

currentRouteName 方法返回处理当前请求的路由名称:

php
$name = Route::currentRouteName();

路由组

有时,您的许多路由将共享共同的要求,例如 URL 段、中间件、命名空间等。您可以使用路由组来将属性应用于许多路由,而不是在每个路由上单独指定这些选项。

共享属性以数组格式作为 Route::group 方法的第一个参数指定。

中间件

通过在组属性数组中使用 middleware 参数定义中间件列表,可以将中间件应用于组内的所有路由。中间件将按照您定义的顺序执行:

php
Route::group(['middleware' => ['foo', 'bar']], function()
{
	Route::get('/', function()
	{
		// 具有 Foo 和 Bar 中间件
	});

	Route::get('user/profile', function()
	{
		// 具有 Foo 和 Bar 中间件
	});

});

命名空间

您可以在组属性数组中使用 namespace 参数为组内的所有控制器指定命名空间:

php
Route::group(['namespace' => 'Admin'], function()
{
	// "App\Http\Controllers\Admin" 命名空间内的控制器

	Route::group(['namespace' => 'User'], function()
	{
		// "App\Http\Controllers\Admin\User" 命名空间内的控制器
	});
});
lightbulb

默认情况下,RouteServiceProvider 在命名空间组中包含您的 routes.php 文件,允许您注册控制器路由而无需指定完整的 App\Http\Controllers 命名空间前缀。

子域路由

Laravel 路由还处理通配符子域,并将通配符参数从域传递:

注册子域路由

php
Route::group(['domain' => '{account}.myapp.com'], function()
{

	Route::get('user/{id}', function($account, $id)
	{
		//
	});

});

路由前缀

可以通过在组的属性数组中使用 prefix 选项为一组路由添加前缀:

php
Route::group(['prefix' => 'admin'], function()
{
	Route::get('users', function()
	{
		// 匹配 "/admin/users" URL
	});
});

您还可以利用 prefix 参数将常用参数传递给路由:

在路由前缀中注册 URL 参数

php
Route::group(['prefix' => 'accounts/{account_id}'], function()
{
	Route::get('detail', function($account_id)
	{
		//
	});
});

您甚至可以为前缀中的命名参数定义参数约束:

php
Route::group([
	'prefix' => 'accounts/{account_id}',
	'where' => ['account_id' => '[0-9]+'],
], function() {

	// 在此处定义路由
});

路由模型绑定

Laravel 模型绑定提供了一种方便的方法将类实例注入到路由中。例如,您可以注入与给定 ID 匹配的整个 User 类实例,而不是注入用户的 ID。

首先,使用路由器的 model 方法为给定参数指定类。您应该在 RouteServiceProvider::boot 方法中定义模型绑定:

将参数绑定到模型

php
public function boot(Router $router)
{
	parent::boot($router);

	$router->model('user', 'App\User');
}

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

php
Route::get('profile/{user}', function(App\User $user)
{
	//
});

由于我们已将 {user} 参数绑定到 App\User 模型,因此将 User 实例注入到路由中。例如,请求 profile/1 将注入 ID 为 1 的 User 实例。

lightbulb

如果在数据库中找不到匹配的模型实例,将抛出 404 错误。

如果您希望指定自己的 "未找到" 行为,请将闭包作为第三个参数传递给 model 方法:

php
Route::model('user', 'User', function()
{
	throw new NotFoundHttpException;
});

如果您希望使用自己的解析逻辑,应使用 Route::bind 方法。传递给 bind 方法的闭包将接收 URI 段的值,并应返回要注入到路由中的类实例:

php
Route::bind('user', function($value)
{
	return User::where('name', $value)->first();
});

抛出 404 错误

有两种方法可以从路由手动触发 404 错误。首先,您可以使用 abort 辅助函数:

php
abort(404);

abort 辅助函数只是抛出一个具有指定状态码的 Symfony\Component\HttpKernel\Exception\HttpException

其次,您可以手动抛出 Symfony\Component\HttpKernel\Exception\NotFoundHttpException 的实例。

有关处理 404 异常和为这些错误使用自定义响应的更多信息,请参阅文档的错误部分。