基本路由定义
参考内容 | laravel 5.7
- (前缀) prefix/uri 路由前缀 <=>
- (别名) 路由别名 <=> <=>
- (中间件) <=>
- (命名空间) <=>
- (控制器定义) 参看一下示例
prefix 前缀
# prefix 用于添加路由前缀
# 请求路径地址 /pr/index
# 示例一
Route::get( '/index', ['prefix'=>'pr',function(){
return "get";
}] );
# 示例二
# prefix( string $prefix)
Route::get('/index',function(){
return "get";
})->prefix('pr');
as 别名
# as 别名 as()函数支持静态 name()支持静态和实例访问
# 示例一
Route::post('index',['as'=>'foo.index','uses'=>'Index\\IndexController@index']);
# 示例二
# name(string $value)
Route::post('index','Index\\IndexController@index')->name('foo.index');
# 访问形式 $url = route('foo.index');
middleware 中间件
- 更多中间件使用 参看中间件文档
# middleware 中间件 当前路由需加载的中间件
# 示例一
Route::get('index', ['middleware'=>'api',function(){
return "get";
}]);
Route::get('index', ['middleware'=>['api','token'],function(){
return "get";
}]);
# middleware(array|string|null $middleware)
# 示例二
# 中间件
Route::get('index', function(){
return "get";
})->middleware( CheckToken::class );
# 示例三
# 一个或多个中间件
Route::get('index', function(){
return "get";
})->middleware('api','auth');
Route::get('index', function(){
return "get";
})->middleware(['api','auth']);
# 示例四
# 中间件参数 在定义路由时通过 : 分隔中间件名和参数名来指定,多个中间件参数可以通过逗号分隔
Route::get('index', ['uses'=>'IndexController@index'])->middleware('throttle:60,1');
namespace 命名空间
# 命名空间在 系统的 App\Http\Controller 后续加
# namespace App\Http\Controller\Index
Route::post('/index',[
'namespace' => 'Index',
'uses' => 'IndexController@index'
]);
# namespace(string $value); 添加后续空间名称
Route::namespace('Index')->post('/index',[
'uses' => 'IndexController@index'
]);
Route::namespace('Index')->post('/index', 'Index\\IndexController@index');
Route 支持方法
# Route::get( $uri, $action ); GET请求(包含 get、head 两种请求类别)
# get:获取指定的页面信息,并返回实体主体
# head:用于获取报头(类似于GET;不会返回页面具体内容)
@method static \Illuminate\Routing\Route
get(string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::get('/', function(){
return view('welcome');
});
# 示例二 namespace App\Http\Controllers\Index
Route::get('/', 'Index\\IndexController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# Route::post( $uri, $action ); POST请求
# post:新增数据
@method static \Illuminate\Routing\Route
post(string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::post('/', function(){
return "post";
});
# 示例二 namespace App\Http\Controllers\Index
Route::post('/', 'Index\\IndexController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# Route::put( $uri, $action ); PUT请求
# put:更新数据
@method static \Illuminate\Routing\Route
put(string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::put('/', function(){
return "put";
});
# 示例二 namespace App\Http\Controllers
Route::put('/', 'IndexController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# Route::delete( $uri, $action ); DEFAULT 请求
# delete:删除服务器指定页面
@method static \Illuminate\Routing\Route
delete(string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::delete('/', function(){
return "delete";
});
# 示例二 namespace App\Http\Controllers
Route::delete('/', 'IndexController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# Route::patch( $uri, $action); PATCH请求
# patch:更新部分数据(例如更新某个字段的值)
@method static \Illuminate\Routing\Route
patch(string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::patch('/', function(){
return "patch";
});
# 示例二 namespace App\Http\Controllers
Route::patch('/', 'IndexController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# Route::options( $uri, $action); OPTIONS请求
# 获取服务器支持的HTTP请求方法、用来检查服务器的性能;
@method static \Illuminate\Routing\Route
options(string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::options('/', function(){
return "options";
});
# 示例二 namespace App\Http\Controllers
Route::options('/', 'IndexController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# Route::any( $uri, $action ); 注册所有的http动作
@method static \Illuminate\Routing\Route
any(string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::any('/', function(){
return "any";
});
# 示例二 namespace App\Http\Controllers
Route::any('/', 'IndexController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# Route::match( $methods, $uri, $action ); 允许指定动作通过
@method static \Illuminate\Routing\Route
match(array|string $methods, string $uri, \Closure|array|string|null $action = null)
# 示例一
Route::match(['get','post','delete'],'/', function(){
return "match";
});
# 示例二 namespace App\Http\Controllers
Route::match(['get','post','delete'],'/', 'IndexController@index');
Route 连贯操作
# prefix 路由前缀
@method static \Illuminate\Routing\RouteRegistrar
prefix(string $prefix)
# 中间件
@method static \Illuminate\Routing\RouteRegistrar
middleware(array|string|null $middleware)
# 路由别名
@method static \Illuminate\Routing\RouteRegistrar
as(string $value)
@method static \Illuminate\Routing\RouteRegistrar
name(string $value)
# 命名空间
@method static \Illuminate\Routing\RouteRegistrar
namespace(string $value)
路由群组
- 路由群组 参数
- 组前缀 使用和基本路由使用一致 参看前处
- 子域名 <=>
- 组命名空间 使用和基本路由使用一致 参看前处
- 组中间件 使用和基本路由使用一致 参看前处
@method static \Illuminate\Routing\Router|\Illuminate\Routing\RouteRegistrar
group(array|\Closure|string $attributes, \Closure|string $routes)
# 示例
Route::group(['namespace'=>'Admin','prefix'=>'admin','middleware'=>'throttle:60,1'], function($router){
$router->get('index','IndexController@index')->name('foo.index');
});
# 示例
# 使用该子域名路由参数:需要改子域名存在的情况下
Route::group(['namespace'=>'Admin','prefix'=>'admin','domain'=>'{account}.bymyside.dv'],function ($route){
$route->get('p','PhotoController@index')->name('p.index');
//> 子域名
$route->get('domain', function($account){
echo $account;
});
});
路由重定向
# uri 原路由
# destination 重定向之后路由
# status 301 重定向标识 http响应码
@method static \Illuminate\Routing\Route
redirect(string $uri, string $destination, int $status = 301)
# 示例
Route::redirect( '/here', 'there', 301 );
路由视图
# uri 路由
# view 视图名称
# data 视图数据
@method static \Illuminate\Routing\Route
view(string $uri, string $view, array $data = [])
# 示例
Route::view('/index', 'welcome');
Route::view('/index', 'welcome', ['name' => 'ss']);
路由参数
# 路由参数使用{}(花括号)包裹,该路由在执行时会一一对应传递到闭包函数或控制器中
# 注意:路由参数不能包含 - (中划线),使用 _ (下划线)代替
Route::get('photos/{user_id}','User\\UserController@index');
# >> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <<
# 可选参数(路由参数)
# 可选参数 使用花括号({})包裹;在参数后面添加一个?(问号):表示该参数可选
# 可选参数 我们需要在闭包或控制器中设置默认值
# 必选参数放在可选参数的前面(这是由于默认参数一般放在后面)
# 必选参数和可选参数 和 闭包或控制器动作 (一一对应(和参数名字不必保持一致))
Route::post('photos/{id}/edit/{value?}', function($id, $value=null){});
where 正则验证
- 使用正则约束还有一个好处就是避免了 user/{id} 和 user/{name} 的混淆
# where 方法约束路由参数
@method static \Illuminate\Routing\RouteRegistrar
where(array $where)
# 示例
Route::get('user/{name}', function( $name ){
return $name;
})->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
// $id 必须是数字
})->where('id', '[0-9]+');
Route::get('user/{id}/{name}', function ($id, $name) {
// 同时指定 id 和 name 的数据格式
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
全局约束
pattern(string $key, string $pattern)
# 使用全局约束 在提供者 RouteServiceProvider 类的boot中定义
public function boot()
{
Route::pattern('id', '[0-9]+');
parent::boot();
}
检查当前路由
# 判断当前路由名称是否已设置路由别名 (使用as或name函数)
/**
* Determine whether the route's name matches the given patterns.
*
* @param dynamic $patterns
* @return bool
*/
public function named(...$patterns)
{
// 获取当前环境设置的所有路由别名 as
if (is_null($routeName = $this->getName())) {
return false;
}
// 判断当前参数是否存在路由别名中
foreach ($patterns as $pattern) {
if (Str::is($pattern, $routeName)) {
return true;
}
}
return false;
}
/**
* Determine if a given string matches a given pattern.
*
* @param string|array $pattern
* @param string $value
* @return bool
*/
public static function is($pattern, $value)
{
// 转换成数组
$patterns = Arr::wrap($pattern);
if (empty($patterns)) {
return false;
}
foreach ($patterns as $pattern) {
// If the given value is an exact match we can of course return true right
// from the beginning. Otherwise, we will translate asterisks and do an
// actual pattern match against the two strings to see if they match.
if ($pattern == $value) {
return true;
}
// 给pattern字符串#添加反斜杠 \#
$pattern = preg_quote($pattern, '#');
// Asterisks are translated into zero-or-more regular expression wildcards
// to make it convenient to check if the strings starts with the given
// pattern such as "library/*", making any string check convenient.
$pattern = str_replace('\*', '.*', $pattern);
// # 边界符
// ^ 匹配行开始
// \z 匹配行结尾
if (preg_match('#^'.$pattern.'\z#u', $value) === 1) {
return true;
}
}
return false;
}
资源路由
# 创建一个资源控制器命令(该命令会在app/http/Controllers/下创建PhotoController.php控制器)
php artisan make:controller PhotoController --resource
# 注册资源路由:
Route::resource('photos','PhotoController');
# 资源路由,资源控制器:自动创建多个控制器方法
# 比如:在app/http/Controllers/Admin/目录下创建 PhotoController.php资源路由
php artisan make:controller Admin\PhotoController --resoucre
# 该命令会自动创建 index()、create()、store()、show()、edit()、update()、destory()方法
- 资源路由注册 - 更多参看控制器
@method static \Illuminate\Routing\PendingResourceRegistration
esource(string $name, string $controller, array $options = [])
# 创建部分资源路由(PhotoController.php文件部分动作生效)
# only : 表示仅仅需要的动作
Route::resource('photo','Admin\\PhotoController',[
//> 仅index和show动作生效
'only' => ['index','show']
]);
# except : 排除指定动作外的其他动作
Route::resource('photo','Admin\\PhotoController',[
//> except 排除指定动作
'execpt' => ['create','store','update','destory']
])
# 命名资源路由:在使用route()辅助函数时(相当于name()方法)
Route::resource('photos','Admin\\PhotoController',[
//> 修改资源路由名称
'names' => [
'index' => 'b.index',
'create'=>'b.create'
]
])
# 命名资源路由参数
# 资源路由参数:会基于资源名称的单数格式为资源路由创建路由参数
# 比如:PhotoController控制器的/photos/{photo}/edit
# 使用parameters参数覆盖Laravel资源路由这一默认设置
Route::resource('photos','Admin\\PhotoController',[
'parameters' => [
'photos' => 'bm'
]
]);
# 上面的路由也就会修改为/photo/{bm}这种形式
# 资源路由器 在使用 默认动作之外的时候
# 在使用资源路由中其他方法时,路由注册在资源路由之前
Route::get('photos/foo', 'Admins\\PhotoController@method'); //资源路由里添加动作method
Route::resource('photos', 'Admin\\PhotoController');
API资源路由
# api资源路由参看 - 资源路由(上)
# 资源路由 apiResource 自动排除 create 和 edit 动作
@method static \Illuminate\Routing\PendingResourceRegistration
apiResource(string $name, string $controller, array $options = [])
@method static void apiResources(array $resources)
# 注册多个资源控制器
Route::apiResources([
'posts' => 'PostController',
'photos' => 'PhotoController'
]);
# 资源路由 API控制器生成
php artisan make:controller API/PostController --api
- 表单伪造、CSRF防护
# 表单伪造
# 由于HTML表单不能发起PUT、PATCH、DELETE请求
# 笔者已经测试:伪造的请求;在请求头中还是以get或post请求后台服务器
{{ method_field('PUT') }} # 表单隐藏域;伪造HTTP请求时使用
# POST请求需要添加
{{ csrf_field() }}
# CSRF防护 - 更多用法参考CSRF保护
# web.php路由中PUT、POST、DELETE的HTML表单需要一个CSRF令牌字段
当前路由信息
# current():获取当前访问路由的详细信息(Route对象)
@method static \Illuminate\Routing\Route current()
# currentRouteName():获取当前路由的名称(就是name()函数或as()函数设置的值)
@method static string|null currentRouteName()
# currentRouteAction():获取当前路由的命名空间以及控制器及方法
@method static string|null currentRouteAction()
# 示例
Route::as('s.s')->get('/s', function (){
dump(Route::current()); // route对象
dump(Route::currentRouteName()); // s.s
dump(Route::currentRouteAction()); // null 当前无命名空间以及控制器及方法
});
路由模型绑定
/**
* Register a model binder for a wildcard.
*
* @param string $key
* @param string $class
* @param \Closure|null $callback
* @return void
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function model($key, $class, Closure $callback = null)
{
$this->bind($key, RouteBinding::forModel($this->container, $class, $callback));
}
/**
* Add a new route parameter binder.
*
* @param string $key
* @param string|callable $binder
* @return void
*/
public function bind($key, $binder)
{
$this->binders[str_replace('-', '_', $key)] = RouteBinding::forCallback(
$this->container, $binder
);
}
- 隐式绑定
- 由于类型声明了 Eloquent 模型 App\User,对应的变量名 $user 会匹配路由片段中的 {user},
- 这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。
- 如果匹配模型实例在数据库中不存在,会自动生成 404 响应
# Id注入 依赖注入 User类 user参数默认匹配users表中的id字段 id = user隐
# 隐式绑定 {user} 对应 $user 变量
Route::get('users/{user}', function (App\User $user) {
dump( $user->email ); // 获取id=user 数据中的 email字段
dump( $user->getAttribute('email') ); // 获取id=user 数据中的 email字段
dump( $user->getAttribute('name') ); // 获取name值
dump( $user->getRouteKeyName() ); // 获取主键key 默认id getRouteKeyName()可修该该值
// $user->getRouteKey() === $user->getAttribute($user->getRouteKeyName())
dump( $user->getRouteKey() ); // 获取主键值 及user值
});
# 如果你想要在隐式模型绑定中使用数据表的其它字段而不是 id 字段,
#可以重写 Eloquent 模型类的 getRouteKeyName 方法,
# 以 User 模型为例,可以在该模型类中添加这个方法
# Model 模型 中添加 users表中 getRouteKeyName = user 参数
public function getRouteKeyName()
{
// parent::getRouteKeyName() <=> 主键 id
return parent::getRouteKeyName(); // TODO: Change the autogenerated stub
}
- 显式绑定
# 显式绑定 不在遵循 {user} 对应 $user变量
# 显式绑定使用Route::model方法指定参数绑定
# 在服务提供者RouteServiceProvider类的boot方法中定义绑定
public function boot(){
parent::boot();
// 绑定 user_model参数 到 App\User
Route::model('user_model', App\User::class);
}
// 路由绑定
Route::get('users/{user_model}', function(\App\User $user){
dump( $user->email );
});
# 如果匹配的模型实例在数据库不存在,会自动生成并返回 HTTP 404 响应
- 自定义逻辑
# 使用Route::bind方法绑定
# 传递到 bind 方法的闭包会获取到 URI 请求参数中的值,并且返回你想要在该路由中注入的类实例
# 服务提供者 RouteServiceProvider 类的boot方法中
public function boot(){
parent::boot();
Route::bind('user', function($value){
// $value = {user} 参数
// return 返回值 Route路由值
return User::where('id',$value)->first() ?? abort(404);
});
}
# 路由文件中定义 Route::bind() user 对应 Route::get() {user}
Route::get('users/{user}', function($user){
// {user} => Route::bind() -> $user
// $user 来自 Route::bind()方法返回的值
dump( $user->toArray() ); // 对象转换为数组
});
路由参数转换
# 一下方法处理路由参数转化
@method static \Illuminate\Routing\Route
substituteBindings(\Illuminate\Support\Facades\Route $route)
@method static void
substituteImplicitBindings(\Illuminate\Support\Facades\Route $route)
# 参考 中间件 \Illuminate\Routing\Middleware\SubstituteBindings
# https://laravel-china.org/articles/5515/want-to-change-the-routing-parameters-to-the-type-you-want-the-use-of-substitutebindings-middleware-and-source-code-analysis?order_by=vote_count&
兜底路由
- 当所有其他路由都未能匹配请求 URL 时所执行的路由
# 访问路由地址不存在时,执行以下内容
Route::fallback(function (){
echo "fallback";
});
路由缓存
- 路由缓存不会作用于基于闭包的路由。
- 要使用路由缓存,必须将闭包路由转化为控制器路由
# 生成路由缓存
php artisan route:cache
# 移除缓存路由文件
php artisan route:clear
路由文件自定义
- App\Providers\RouteServiceProvider 路由服务提供者
mapApiRoutes();
// 加载web.php路由文件
$this->mapWebRoutes();
// 路由文件加载 - 自定义
$this->mapWebsRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
{
// 加载web中间件组
// $this->namespace 命名空间
// group加载 routes/web.php 文件路由
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
// prefix 路由前缀
// api 中间件
// $this->namespace 命名空间
// group 加载 routes/api.php 路由文件
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
/**
* 自定义 路由规则
* @return void
*/
protected function mapWebsRoutes(){
// 获取routes/routes目录下所有.php文件 返回数组
foreach ( glob(base_path('routes'.DIRECTORY_SEPARATOR.'web'.DIRECTORY_SEPARATOR).'*.php' ) as $fileName ) {
// basename() 函数路径中的文件名部分 xxxx.php
$fileName = basename( $fileName );
// 获取 xxxx.php 中的 xxxx内容
$prefix = substr( $fileName,0,strrpos($fileName,'.') );
// ucfirst(string) 将string首字母转换成大写 并返回
$nameSpace = ucfirst($prefix);
// 注册当前路由
// 注册 web 中间件组
// 设置命名空间 $this->namespace."\\{$nameSpace}"
// group 加载路由文件
Route::middleware('web')
->namespace( $this->namespace."\\{$nameSpace}" )
->group( base_path('routes'.DIRECTORY_SEPARATOR.'web'.DIRECTORY_SEPARATOR."{$fileName}") );
}
}
}
- 路由文件
- 对应控制器