Laravel 文档阅读:路由

基础路由

在 Laravel 中要简单地定义一个路由非常方便,传递一个 URI 和闭包即可。

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

默认路由文件

Laravel 中所有的路由定义在 routes 目录下,这个目录下的内容会自动被框架加载。在 routes/web.php 文件中定义的是 Web 接口路由,这些路由默认都使用了 web 中间件组过滤,它提供了 Session 会话和 CSRF 保护功能。 routes/api.php 文件用来定义 API 接口路由,这些路由都是无状态的,默认使用了 api 中间件过滤。

许多项目,基本上都是以 routes/web.php 为起点进行开发的,它能实现快速开发一个项目的需要。在 routes/web.php 中定义的路由在浏览器中是可以直接访问到的。例如,在浏览器中输入 http://your-app.dev/user 地址来访问下面的路由:

Route::get('/user', 'UsersController@index');

定义在 routes/api.php 中的路由被嵌套在了一个路由组里,这是在 RouteServiceProvider 中设定的。这个组里的路由都使用了 /api 这个 URI 前缀,这样在你定义路由的时候,就不必再手动添加这个前缀了。如果你不想使用 /api 这个前缀的话,那么可以在 RouteServiceProvider 中修改。

/**
 * Define the "api" routes for the application.
 *
 * These routes are typically stateless.
 *
 * @return void
 */
protected function mapApiRoutes()
{
    Route::prefix('api')
         ->middleware('api')
         ->namespace($this->namespace)
         ->group(base_path('routes/api.php'));
}

可用的路由方法

每一个 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);

有时一个路由需要能够匹配多个请求类型,这时可以用 match 方法。

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

如果希望一个路由能够匹配所有请求类型的话,使用 any 方法:

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

CSRF 保护

使用 Route::postRoute::putRoute::patchRoute::delete 定义的路由,要求访问它们的 HTML 表单都要传递一个 CSRF token,否则请求会被拒绝。你可以在 CSRF 文档里阅读更多这方面的内容。

<form method="POST" action="/profile">
    {{ csrf_field() }}
    ...
</form>

重定向路由

如果你要将一个路由重定向到另一个路由,可以使用 Route::redirect 方法。这就节省定义一个完整路由或者控制器来操作简单的重定向带来的一些麻烦:

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

视图路由

如果你只是要简单返回一个视图,可以使用 Route::view 方法,类似 Route::redirect 方法,也节省了一些麻烦。view 方法的第一个参数是 URI,第二个参数是视图名。另外,也可以使用可选的第三个数组类型参数向视图传递数据:

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

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

路由参数

必需参数

有时需要捕获路由 URI 片段,得到我们要用的参数值。例如,我们从一个路由 URL 里捕获用户 ID,你可以这样定义路由参数:

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

你可以按照需要在路由里定义多个路由参数。

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

在花括号({})里的单词会被解析,这些单词由字符字母组成, 不能使用中划线(-),可以使用使用下划线(_,路由参数是按照顺序注入到回调闭包或者控制器中的形参上的,与这些形参起什么名字没有关系。

可选参数

上面的路由参数,在 HTTP 请求中都是必须传递的,如果你需要路由参数是可选的路由参数,那么就要在参数名后头放上一个问好(?)标识,表示这是可选参数,不过这时要保证对应位置上的 形参必须要有默认值

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]+']);

全局约束

如果在项目里,一个路由参数名的规则都是一样的,这时就可以用 pattern 方法定义全局约束,具体是在 RouteServiceProviderboot 里使用 pattern 方法定义规则。

/**
 * Define your route model bindings, pattern filters, etc.
 *
 * @return void
 */
public function boot()
{
    Route::pattern('id', '[0-9]+');

    parent::boot();
}

全局约束规则定义好后,所有路由中匹配该参数名的路由参数值都受该规则限制。

Route::get('user/{id}', function ($id) {
    // Only executed if {id} is numeric...
});

命名路由

命名路由就是给路由起一个名字,有了名字后,我们就可以用这个名字来生成访问这个路由的 URL 地址了。在定义路由的时候,链式调用 name 方法,就给路由起了一个名字:

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

也可以为基于控制器 action 的路由起名:

Route::get('user/profile', 'UserController@showProfile')->name('profile');

生成命名路由的 URL

访问命名路由,使用全局 route 辅助函数,这个函数会生成访问这个路由的 URL 地址。

// Generating URLs...
$url = route('profile');

// Generating Redirects...
return redirect()->route('profile');

如果命名路由中包含参数,那么在调用 route 函数的时候,传递第二个参数来指定路由参数值。

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

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

检查当前路由

如果你想知道,当前请求是否是发送到一个特定的命名路由上的,用 Route 实例的 named 方法就可以做到。例如,我们在一个路由中间件里定义了这个逻辑:

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
   // 如果当前请求是发往名字是“profile”的路由的,就会执行 if 里面的逻辑 
    if ($request->route()->named('profile')) {
        //
    }

    return $next($request);
}

路由组

当我们定义的路由很多的时候,就会发现有些路由是有一些共同特征的,比如用了同一个中间件、有相同的命名空间、有一样的 URI 前缀地址等等。这时为了方便,我们就不要给每个路由重复、单独地定义这些内容了,使用 Route::group 方法就可以轻松解决这个麻烦, Route::group 方法让具有共同特征的路由放在一个组里面,我们称这个组叫「路由组」。

中间件

给一个路由组应用中间件,是在 group 方法之前调用 middleware 方法,中间件会按照中间件在数组里的出现顺序以此执行:

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // Uses first & second Middleware
    });

    Route::get('user/profile', function () {
        // Uses first & second Middleware
    });
});

命名空间

另一个路由组的常用用处是使用 namespace 方法为添加统一的命名空间:

Route::namespace('Admin')->group(function () {
    // Controllers Within The "App\Http\Controllers\Admin" Namespace
});

需要注意的是,在 RouteServiceProvider 中已经为 routes/web.phproutes/api.php 文件中定义的路由添加了 App\Http\Controllers 的命名空间前缀。所以在定义路由时,你只需要指定 App\Http\Controllers 命名空间的后面部分就行了。


子域路由

路由组也可用于处理子域地址,在子域上也可以使用参数,路由能捕获子域地址里的参数值。在 group 方法前调用 domain 方法就可以使用这个功能了。

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

路由前缀

prefix 方法用来给路由组添加 URI 前缀。例如,我们给所有路由 URL 添加 admin 前缀:

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

路由模型绑定

我们通常在路由或者控制器 action 中获得一个模型 ID 值,然后通过这个 ID 找到对应的模型实例。Laravel 路由模型绑定让这个过程更加简单,它会自动在你的路由中注入模型实例。例如,传递一个用户 ID ,得到对应的用户实例。

隐形绑定

Laravel 会自动解析路由或控制 action 中的形参变量,但前提是该变量名与路由参数名一样、且有类型(指 Eloquent Model)提示的。

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

在这里,$user 变量的类型是 App\User 这个 Eloquent Model,并且这个变量名跟 URL 路由参数 {user} 是一样的,所以 Laravel 会自动将拥有该 ID 的 App\User 实例对象赋值给 $user 变量。如果实例对象在数据库中未找到,就会抛出一个 404 响应。

自定义模型在路由中主键名

如果要让路由绑定从数据库中获得模型实例使用的默认字段不是 id,而是其他字段,这时候就要在 Eloquent Model 中重写 getRouteKeyName 方法:

/**
 * Get the route key for the model.
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}

精确绑定

RouteServiceProvider 类的 boot 方法中,使用 Route::model 方法为特定的路由参数名绑定 Eloquent Model 类:

public function boot()
{
    parent::boot();

    Route::model('user', App\User::class);
}

接下来,在路由中使用路由参数 {user}

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

我们把所有路由参数是 {user} 的都绑定给了 App\User 模型,最后对应的 User 模型实例就会注入到路由中。例如,profile/1 的请求地址最终会从数据库中获得 ID 是 1 的那个用户数据。如果在数据库里没有找到匹配的用户,就会自动返回 404 响应。

自定义解析逻辑

如果希望解析路由参数时,使用自己的一套更加个性化的逻辑,使用 Route::bind 方法就行了。

public function boot()
{
    parent::boot();

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

表单方法伪造

HTML 表单不支持PUTPATCHDELETE 方式的 HTTP 请求。为了能与后台的 PUTPATCHDELETE 类型的路由匹配,就需要添加一个名为 _method 的隐藏表单域。 _method 这个隐藏表单域的值会被 Laravel 看做 HTTP 请求的标识:

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

你也可以使用 method_field 辅助函数产生这个 _method 隐藏表单域:

{{ method_field('PUT') }}

访问当前路由

使用 Route 门面的 currentcurrentRouteNamecurrentRouteAction 方法获得处理当前请求的路由信息:

$route = Route::current();

$name = Route::currentRouteName();

$action = Route::currentRouteAction();

更多关于 Route 门面的底层类Route 实例 的所有可用方法请参考 API 文档。

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 1

laravel 5.5 的
Route::current():
Method current does not exist.
是怎么回事

6年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!