Laravel 路由只接收一个 URI 和一个闭包,并以此为基础提供一个非常简单优雅的路由定义方法:
Route::get('hello', function () {
return 'Hello Laravel8!';
});
访问此路由:http://blog.laravel8.cn/index.php/hello得到如下:
第一参数: /或hello 代表入口文件index.php后面的部分
第二参数:为一个闭包函数,用来处理业务逻辑的,可以返回数据,也可以返回视图文件如return view('welcome')表示引用/resources/views/welcome.blade.php这个视图文件。返回的数据就是我们请的内容。
理论上此闭包函数,可以处理所有的业务逻辑,并不需要控制器,但是这样写将来业务逻辑多了,就显得太臃肿了,因此约定俗成,都使用控制器来代替第二参数的闭包函数。
所有 Laravel 路由都定义在位于 routes 目录下的路由文件中,这些文件通过框架自动加载,相应逻辑位于 app/Providers/RouteServiceProvider 类。
web.php 文件定义了 Web 界面的路由,这些路由被分配到了web中间件组,从而可以使用 Session 和 CSRF 保护等功能。
api.php 中的路由是无状态的,这是因为被分配到了 api 中间件组。
其中定义在api.php中的路由,请求时必须添加/api/前缀,如图
我们可以通过路由器注册路由来响应任何 HTTP 请求动作:
Route::get($uri, $callback); //处理get请求
Route::post($uri, $callback); //处理post请求
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
有时候还需要注册一个路由响应多个 HTTP 请求动作 —— 这可以通过 match
方法来实现。或者,可以使用 any
方法注册一个路由来响应所有 HTTP 请求动作:
Route::match(['get', 'post'], 'foo', function () {
return 'This is a request from get or post';
});
Route::any('bar', function () {
return 'This is a request from any HTTP verb';
});
测试 GET 请求的时候直接在浏览器中输入请求地址即可,测试 POST 请求可以通过客户端工具,比如 Postman,
4.1如果上面的路由是定义在 routes/web.php
在测试 POST 请求之前,需要将对应路由取消 CSRF 保护检查,否则会返回 419
状态码导致无法请求成功,
取消的方法是在 app/Http/Middleware/VerifyCsrfToken
中设置排除检查路由:
再次请求
4.2如果上面的路由是定义在 routes/api.php
则无需关注 CSRF 保护问题,比如我们在 routes/api.php
定义 bar
路由,并且在 VerifyCsrfToken
的 $except
属性数组中移除 bar,如在api.php中定义如下路由
Route::post('barer', function (){
return '世间安得两全法,不负如来不负卿;万般皆是命,半点不由人';
});
请求如下:
正如我们所预测的,完全没有任何问题,背后的原因是因为 Web 路由文件中定义的路由都位于 web
中间件群组,该群组默认启用 CSRF 保护检查,而 API 路由文件位于 api
路由群组,该群组下的路由主要用于 第三方 API 请求,没办法进行 CSRF 检查,所以不需要做任何处理。
在 routes/web.php
路由文件中所有请求方式为 PUT
、POST
或 DELETE
的路由对应的 HTML 表单都必须包含一个 CSRF 令牌字段,否则,请求会被拒绝。
如果我们不在 VerifyCsrfToken
中间件中排除对它的检查(事实上,这样的操作也不安全),那么就需要在表单提交中带上 csrf_token
字段:
我们在web.php中创建一个表单路由:
Route::get('formpost', function (){
return '';
});
点击提交就可以直接请求/foo方法了。 如果我们不加入此csrf_field(),直接提交,同样会报419错误。
关于 CSRF 的更多细节,可以参考 CSRF 文档:
如果你需要定义一个重定向到其他 URI 的路由,可以使用 Route::redirect
方法,该方法非常方便,以至于你不需要再定义额外的路由或控制器来执行简单的重定向逻辑:
Route::redirect('/here', '/there', '301');
//访问http://blog.laravel8.cn/here 时,直接跳转到 http://blog.laravel8.cn/there了
如果你的路由需要返回一个视图,可以使用 Route::view
方法,和 redirect
方法类似,这个方法也很方便,以至于你不需要在额外定义一个路由或控制器。
// 视图路由
Route::view('/welcome', 'welcome', ['username' => '暮云归']);
view
方法接收一个 URI 作为第一个参数,以及一个视图名称作为第二个参数,此外,你还可以提供一个数组数据传递到该视图方法作为可选的第三个参数,该数组数据可用于视图中的数据渲染:
// 把模板定义在./views/article/目录中
Route::view('/article', 'article/index', ['username' => '暮云归']);
模板文件为
Title
作者: {{ $username }}
这是/resources/views/article/index.blade.php模板
必选参数
//路由参数
Route::get('posts/{userid}/username/{username}', function ($userId, $username){
return 'userID:'.$userId. '-- username: '. $username;
});
//http://blog.laravel8.cn/posts/1/username/niuxitong
根据上面的示例,路由参数需要通过花括号 {}
进行包裹并且是拼音字母,这些参数在路由被执行时会被传递到路由的闭包。路由参数名称不能包含 -
字符,如果需要的话可以使用 _
替代,比如如果某个路由参数定义成 {post-id}
则访问路由会报错,应该修改成 {post_id}
才行。路由参数被注入到路由回调/控制器取决于它们的顺序,与回调/控制器名称无关。
可选参数
通过在参数名后加一个 ?
标记来实现,这种情况下需要给相应的变量指定默认值,当对应的路由参数为空时,使用默认值:
// 可选参数
//http://blog.laravel8.cn/user 如果不指定参数,就用默认值John
//http://blog.laravel8.cn/user/niuxiong 如果给定值,就用给定的值
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
参数顺序
如:路由为 Route::get('edit/{id}/{action}', [IndexController::class, 'edit']);
控制器代码:
public function edit( $action = 'a', $id = 0) {
echo '路由参数id='.$id . '--action='. $action;
}
访问结果:
路由参数中的第一个参数对应控制器中的第一个路由参数;以此类推;在控制器中路由参数跟参数名是没关系的;只跟顺序有关;
参数验证:
如约束id只能输入数字。使用->where('id', '[0-9]+')即可;
Route::get('edit/{id}/{action}', [IndexController::class, 'edit'])->where('id', '[0-9]+');
其实大多的表都是用 id 做主键的;
如果每个路由都手动定义一遍约束;
那也是挺麻烦的;
laravel 又一次方方面面都为我们考虑到了;
找到 app/Providers/RouteServiceProvider.php 这个文件;
在 boot 方法中可以定义全局约束;
public function boot()
{
Route::pattern('id', '[0-9]+');
parent::boot();
}