HTTP 路由


1、基本路由 

大部分路由都定义在bootstrap/app.php文件载入的app/Http/routes.php中。

最基本的Lumen路由接收一个URI和一个闭包:

$app->get('/', function () {
    return 'Hello World';
});

$app->post('foo/bar', function () {
    return 'Hello World';
});

$app->put('foo/bar', function () {
    //
});

$app->delete('foo/bar', function () {
    //
});

生成指向路由对应的URL

可以使用帮助函数url来生成路由对应的URL:

$url = url('foo');

2、路由参数

2.1 必选参数

有时我们需要在路由中捕获URI片段,比如,如果想要从URL中捕获用户ID,可以通过如下方式定义路由参数:

$app->get('user/{id}', function ($id) {
    return 'User '.$id;
});

可以按需要定义在路由中定义多个路由参数:

$app->get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

路由参数总是通过花括号进行包裹,参数在路由被执行时会被传递到路由的闭包。

注意:路由参数不能包含’-‘字符,需要的话可以使用_替代。

2.2 正则约束

可以通过在路由中定义定义正则表达式来约束路由参数格式:

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

3、命名路由

命名路由使生成URL或者重定向到指定路由变得很方便,在定义路由时指定路由名称,然后使用数组键as指定路由别名:

$app->get('user/profile', ['as' => 'profile', function () {
    //
}]);

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

$app->get('user/profile', [
    'as' => 'profile', 'uses' => 'UserController@showProfile'
]);

生成指向命名路由的URL

一旦你为给定路由分配了名字,通过route函数生成URL时就可以使用路由名字:

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

如果路由定义了参数,可以将路由参数作为第二个参数传递给route函数。给定的路由参数将会自动插入URL中:

$app->get('user/{id}/profile', ['as' => 'profile', function ($id) {
    //
}]);
$url = route('profile', ['id' => 1]);

4、路由分组

路由分组允许我们在多个路由中共享路由属性,比如中间件和命名空间等,这样的话一大波共享属性的路由就不必再各自定义这些属性。共享属性以数组的形式被作为第一个参数传递到$app->group方法中。

想要了解更多路由分组,我们希望通过几个简单的应用实例来展示其特性。

4.1 中间件

要分配中间件给分组中的所有路由,可以在分组属性数组中使用middleware键。中间件将会按照数组中定义的顺序依次执行:

$app->group(['middleware' => 'auth'], function () {
    $app->get('/', function ()    {
        // 使用 Auth 中间件
    });

    $app->get('user/profile', function () {
        // 使用 Auth 中间件
    });
});

4.2 命名空间

另一个通用的例子是路由分组分配同一个PHP命名空间给多个控制器,可以在分组属性数组中使用namespace参数来指定分组中控制器的命名空间:

$app->group(['namespace' => 'Admin'], function(){
    // 控制器在 "App\Http\Controllers\Admin" 命名空间下

    $app->group(['namespace' => 'User'], function()
    {
        // 控制器在 "App\Http\Controllers\Admin\User" 命名空间下
    });
});

默认情况下,RouteServiceProvider包含routes.php并指定其所在命名空间为App\Http\Controllers,因此,我们只需要指定App\Http\Controllers之后的相对命名空间即可。

4.3 路由前缀

属性prefix可以用来为分组中每个给定URI添加一个前缀,比如,你想要为所有路由URI前面添加前缀admin

$app->group(['prefix' => 'admin'], function () {
    $app->get('users', function ()    {
        // 匹配 "/admin/users" URL
    });
});

你还可以使用prefix参数为分组路由指定公共参数:

$app->group(['prefix' => 'accounts/{account_id}'], function () {
    $app->get('detail', function ($account_id)    {
        // 匹配 accounts/{account_id}/detail URL
    });
});

5、CSRF攻击及保护

注意:使用Lumen的该特性之前必须开启session。

5.1 简介

Lumen使得防止应用遭到跨站请求伪造攻击变得简单。跨站请求伪造是一种通过伪装授权用户的请求来利用授信网站的恶意漏洞。

Lumen自动为每一个被应用管理的有效用户Session生成一个CSRF“令牌”,该令牌用于验证授权用户和发起请求者是否是同一个人。想要生成包含CSRF令牌的隐藏输入字段,可以使用帮助函数csrf_field来实现:

<?php echo csrf_field(); ?>

帮助函数csrf_field生成如下HTML:

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

当然还可以使用Blade模板引擎提供的方式:

{!! csrf_field() !!}

你不需要了解在POST、PUT或者DELETE请求时CSRF令牌是如何进行验证的,HTTP中间件VerifyCsrfToken会为我们做这项工作:将请求中输入的token值和session中的存储的作对比。

5.2 X-CSRF-Token

除了将CSRF令牌作为一个POST参数进行检查,Lumen的VerifyCsrfToken中间件还会检查X-CSRF-TOKEN请求头,你可以将令牌保存在”meta”标签中:

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

创建完这个meta标签后,就可以在js库如jQuery中添加该令牌到所有请求头,这为基于AJAX的应用提供了简单、方便的方式来避免CSRF攻击:

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

5.3 X-XSRF-Token

Lumen还将CSRF令牌保存到了名为XSRF-TOKEN的cookie中,你可以使用该cookie值来设置X-XSRF-TOKEN请求头。一些JavaScript框架,比如Angular,将会为你自动进行设置,基本上你不太会手动设置这个值。

6、表单方法伪造

HTML表单不支持PUTPATCH或者DELETE动作,因此,当定义被HTML表单调用的PUTPATCHDELETE路由时,需要添加一个隐藏的_method字段到给表单中,其值被用作HTTP请求方法名:

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

7、抛出404错误

有两者方法手动从路由触发404错误。

第一种,使用帮助函数abortabort函数会抛出一个指定状态码的Symfony\Component\HttpFoundation\Exception\HttpException

abort(404);

第二种,手动抛出Symfony\Component\HttpKernel\Exception\NotFoundHttpException.的实例。

更多关于处理404异常的信息以及如何自定义视图显示这些错误信息,请查看错误文档一节。