闭包 语法

  1. // routes/web.php
  2. Route::get('/', function () {
  3. return 'Hello, World!';
  4. });
  5. Route::get('about', function () {
  6. return view('about');
  7. });

非静态, ::get() 并不是确切的静态方法, 而是用了 Laravel’s facades.

http 动词

Route::post('/', function () {
// Handle someone sending a POST request to this route
});

Route::put('/', function () {
// Handle someone sending a PUT request to this route
});

Route::delete('/', function () {
// Handle someone sending a DELETE request to this route
});

Route::any('/', function () {
// Handle any verb request to this route
});

Route::match(['get', 'post'], '/', function () {
// Handle GET or POST requests to this route
});

闭包路由的不足

闭包: 简单且快速,但是对于大型的系统,闭包会把所有路由逻辑都放到一个文件里,也没法使用 laravel 的路由缓存来提高网站响应速度。

控制器@操作 语法

// 这个请求发送到 App\Http\Controllers\WelcomeController 控制器的 index() 操作.
Route::get('/', 'WelcomeController@index');

Route::get('/', [ WelcomeController::class, 'index']); // laravel5.7 之后的版本

路由参数

Route::get('users/{id}/friends', function ($id) {
//
});

Route::get('members/{id}', 'MembersController@show'); // show方法中接受参数

// 可选参数
Route::get('users/{id?}/friends', function ($id='test') {
//
});

// 正则校验过滤参数
Route::get('users/{id}', function ($id) {
//
})->where('id', '[0-9]+');

Route::get('posts/{id}/{slug}', function ($id, $slug) {
//
})->where(['id' => '[0-9]+', 'slug' => '[A-Za-z]+']);

ps: 路由中参数名 和 闭包中或者控制器方法中的参数名 不一定必须一样,下面也是被允许的(但是不建议)

Route::get('users/{userId}/comments/{commentId}', function ($uid, $cid) {
  //
});

路由名字

自定义路由名字

Route::get('members/{id}', 'MembersController@show')->name('members.show');

url() 和 route() 帮助方法

在视图中你可以使用这两个函数来跳转指定的路由

<!-- url(): 使用路径   -->
<a href="<?=url('/members/20') ?>">
// Outputs <a href="http://myapp.com/members/20">

<!-- route(): 使用名称 -->
<a href="<?=route('members.show', 20) ?>">
// Outputs <a href="http://myapp.com/members/20">

<a href="<?=route('members.show', ['id' => 20]) ?>">
// Outputs <a href="http://myapp.com/members/20">

<a href="<?=route('members.show', ['id' => 20,'key' => 'str']) ?>">
// Outputs <a href="http://myapp.com/members/20?key=str">

路由组

把有相同配置的路由放到一起配置,就是路由组,比如这些路由都要登录授权后才能访问等

语法如下:

// 闭包实现路由组
Route::group(function () {
    Route::get('hello', function () {
        return 'Hello';
    });
    Route::get('world', function () {
        return 'World';
    });
});

基本应用场景

中间件
  1. 授权中间件
Route::middleware('auth')->group(function(){
    Route::get('dashboard',function(){
        return view('dashboard');
    });

    Route::get('account','User@account')->name('user.own');
});
  1. 速率限制中间件
Route::middleware('auth:api','throttle:60,1')->group(function(){
    Route::get('/profile',function(){
        //
    })
})

// 动态速率控制
  1. 控制器中使用中间件
class DashController extends Controller{

    public function __construct(){
        $this->middleware('auth');

        $this->middleware('admin-auth')->only('editUsers');

        $this->middleware('team-member')->except('editUsers');
    }
}

tip: 如果使用了太多 onlyexcept ,通常可以考虑新建一个控制器了

路径前缀
Route::prefix('shy')->group(function () {
    Route::get('/', function () { // http://myapp.com/shy
        return '/shy';
    });

    Route::get('kk', function () { // http://myapp.com/shy/kk
        return 'shy/kk';
    });
});

回退路由

放在路由文件最后,捕获未定义的路由

Route::fallback(function () {
    return '200 && 404';
});

子域路由

子域名路由和路由前缀一样,只不过用子域名代替了路径前缀

  1. 展示应用的不同部分或者完全不同的应用 eg: https://translate.google.cn/
Route::domain('api.myapp.com')->group(function(){
    Route::get('/',function(){
        //
    });

    // more code
});
  1. 使用子域名作为参数 eg: tighten.slack.co | myteam.slack.co
Route::domain('{account}.myapp.com')->group(function () {
    Route::get('/', function ($account) {
        //
    });
    Route::get('users/{id}', function ($account, $id) {
        //
    });
});

ps: 子域名的参数,总是作为第一个参数传递到控制器的方法中

命名空间前缀

当你使用子域名路由或者路径前缀路由时,可能把你的控制器放在相同的命名空间下面,写路由映射时,可能会很长,如 Dashboard/UsersController@indexDashboard/PurchasesController@index, 使用命名空间前缀可以避免此问题。

// 原始
Route::get('dashboard/purchases', 'Dashboard/PurchasesController@index');

// 优化 1 => 使用命名空间前缀
Route::namespace('Dashboard')->group(function(){
    // App\Http\Controllers\Dashboard\PurchasesController
    Route::get('dashboard/purchases','PurchasesController@index');
});

// 优化 2 => 结合路径前缀使用 
Route::prefix('dashboard')->group(function () {

    Route::namespace('Dashboard')->group(function () {

         // App\Http\Controllers\Dashboard\UsersController
        Route::get('users','UsersController@index');// http://myapp.com/dashboard/users

        // App\Http\Controllers\Dashboard\PurchasesController
        Route::get('purchases','PurchasesController@index'); // http://myapp.com/dashboard/purchases
    });
});

名称前缀

避免 name() 时每次都要写前缀。

// route('users.comments.show') ==> http://myapp.com/users/comments/{id}
Route::name('users.')->prefix('users')->group(function () {
    Route::name('comments.')->prefix('comments')->group(function () {
        Route::get('{id}', function ($id) {
            return 'u->m: ' . $id;
        })->name('show');

        // code more
    });
});

// route('aa.cc') ==> http://myapp.com/kk
Route::name('aa.')->group(function () {
    Route::get('kk', function () {
        return 'aa.cc';
    })->name('cc');
});

签名路由

很多应用程序都会发送一些通知用于重置密码,接受邀请等,这些操作通常一个链接,既然是链接那么很容易被不怀好意的人修改。比如我要重置id=1用户的密码,但是被修改为 id=2 了。

laravel 5.6.12 之后的版本提供了签名路由,从而避免这个问题。

假设我们要邀请一个好友。

  1. 首先我们必须要先有一个有名字的路由
Route::get('invitations/{invitation}/{answer}', 'InvitationController')
    ->name('invitations');
  1. 生成带签名的url
// 生成普通url 
URL::route('invitations', ['invitation' => 12345, 'answer' => 'yes']);
 // output http://myapp.com/invitations/12345/yes

// 生成带签名的url
URL::signedRoute('invitations', ['invitation' => 12345, 'answer' => 'yes']);
// output http://myapp.com/invitations/12345/yes?signature=b1736d7ab164bd3007c06509b3232cff0ec8ab3e4e81555ce142ef942e40efb2

// 生成有时效性的带签名的 url (临时url)
URL::temporarySignedRoute('invitations', now()->addMinutes(15), ['invitation' => 12345, 'answer' => 'yes']);
// output http://myapp.com/invitations/12345/yes?expires=1577544940&signature=b2ec1c4dcc696024c450ca34f7a0ad15d57f898fb7b0247548fe4e768545e5f0
  1. 设置签名校验 (使用中间件 或者 自定义)

当前生成的 url 是没有校验签名的,意味着可以任意被修改。

校验签名最简单的方法是使用 signed 中间件。

// 修改步骤 1 中的路由如下
Route::get('invitations/{invitation}/{answer}', 'InvitationController')
    ->name('invitations')
    ->middleware('signed');

如果你喜欢,你也可以使用 Request 对象上的 hasValidSignature() 方法手动进行验证,而不使用 signed 中间件:

class InvitationController
{
    public function __invoke(Invitation $invitation, $answer, Request $request)
    {
        if (!$request->hasValidSignature()) {
            abort(403);
        }
        //
    }
}