路由
默认路由规则
webman 默认路由规则是 http://127.0.0.1:8787/{控制器}/{动作}。
默认控制器为app\controller\IndexController,默认动作为index。
例如访问:
http://127.0.0.1:8787将默认访问app\controller\IndexController类的index方法http://127.0.0.1:8787/foo将默认访问app\controller\FooController类的index方法http://127.0.0.1:8787/foo/test将默认访问app\controller\FooController类的test方法http://127.0.0.1:8787/admin/foo/test将默认访问app\admin\controller\FooController类的test方法 (参考 多应用)
另外 webman 从 1.4 开始支持更复杂的默认路由,例如
app
├── admin
│ └── v1
│ └── v2
│ └── v3
│ └── controller
│ └── IndexController.php
└── controller
├── v1
│ └── IndexController.php
└── v2
└── v3
└── IndexController.php
当您想改变某个请求路由时请更改配置文件 config/route.php。
如果你想关闭默认路由,在配置文件 config/route.php里最后一行加上如下配置:
Route::disableDefaultRoute();
闭包路由
config/route.php里添加如下路由代码
use support\Request;
Route::any('/test', function (Request $request) {return response('test');
});
注意
由于闭包函数不属于任何控制器,所以$request->app$request->controller$request->action全部为空字符串。
当访问地址为 http://127.0.0.1:8787/test 时,将返回 test 字符串。
注意
路由路径必须以/开头,例如
use support\Request;
// 错误的用法
Route::any('test', function (Request $request) {return response('test');
});
// 正确的用法
Route::any('/test', function (Request $request) {return response('test');
});
类路由
config/route.php里添加如下路由代码
Route::any('/testclass', [app\controller\IndexController::class, 'test']);
当访问地址为 http://127.0.0.1:8787/testclass 时,将返回 app\controller\IndexController 类的 test 方法的返回值。
注解路由
在控制器方法上使用注解定义路由,无需在 config/route.php 中配置。
注意
此功能需要 webman-framework >= v2.2.0
基本用法
namespace app\controller;
use support\annotation\route\DisableDefaultRoute;
use support\annotation\route\Get;
use support\annotation\route\Post;
use support\annotation\route\Route;
#[DisableDefaultRoute]
class UserController
{#[Get('/user/{id}')]
public function show($id)
{return "user $id";}
#[Post('/user')]
public function store()
{return 'created';}
#[Route('/user/form', ['GET', 'POST'], 'user.form')]
public function form()
{return 'form';}
}
可用注解:#[Get] #[Post] #[Put] #[Delete] #[Patch] #[Head] #[Options] #[Any](任意方法)。路径必须以 / 开头。第二参数可指定路由名,用于 route() 生成 URL。
#[DisableDefaultRoute] 表示该控制器禁用默认路由(可选),仅通过注解定义的路由可访问。
#[Route] 完整用法:#[Route(path, methods, name)]
path:路由路径,以/开头;为null时仅限制默认路由的请求方法,不注册新路由methods:HTTP 方法,字符串或字符串数组,如'GET'或['GET','POST']name:路由名,用于route('name')生成 URL,可省略
无参数注解:限制默认路由的请求方法
不带路径时,仅限制该动作可通过的 HTTP 方法,仍使用默认路由路径:
#[Post]
public function create() { ...} // 仅允许 POST,路径仍为 /user/create
#[Get]
public function index() { ...} // 仅允许 GET
可组合多个注解,允许多种请求方法:
#[Get]
#[Post]
public function form() { ...} // 允许 GET 和 POST
未在注解中声明的请求方法将返回 405。
多个带路径的注解会注册为多条独立路由:#[Get('/a')] #[Post('/b')] 会生成 GET /a 与 POST /b 两条路由。
路由组前缀
在类上使用 #[RouteGroup] 为所有方法路由添加前缀:
use support\annotation\route\RouteGroup;
use support\annotation\route\Get;
#[RouteGroup('/api/v1')]
class UserController
{#[Get('/user/{id}')] // 实际路径 /api/v1/user/{id}
public function show($id) {...}
}
自定义请求方法与路由名
#[Route] 支持自定义多种 HTTP 方法和路由名:
use support\annotation\route\Route;
#[Route('/user', ['GET', 'POST'], 'user.form')]
public function form() { ...}
// 仅指定路径和方法,不含路由名
#[Route('/user/update', 'PUT')]
public function update() { ...}
路径参数与正则
注解路由路径支持与 config/route.php 相同的参数语法,包括正则与可选参数:
#[Get('/user/{id:\d+}')] // 仅匹配数字 id
public function show($id) {...}
#[Get('/user[/{name}]')] // name 为可选,/user 或 /user/xxx
public function list($name = null) {...}
#[Any('/user/[{path:.+}]')] // 匹配 /user 及 /user/ 任意后缀
public function catchAll($path = null) {...}
详见本文档「路由参数」章节。
中间件
控制器或方法上的 #[Middleware] 会作用于注解路由,用法同 support\annotation\Middleware:
use support\annotation\Middleware;
#[Middleware(\app\middleware\AuthMiddleware::class)]
class UserController {...}
#[Get('/user/{id}')]
#[Middleware(\app\middleware\RateLimitMiddleware::class)]
public function show($id) {...}
与 route() URL 生成的配合
在注解中指定路由名后,可通过 route('name', $params) 生成 URL:
#[Get('/user/{id}', 'user.show')]
public function show($id) {...}
// 使用:route('user.show', ['id' => 123]) => /user/123
路由参数
如果路由中存在参数,通过 {key} 来匹配,匹配结果将传递到对应的控制器方法参数中(从第二个参数开始依次传递),例如:
// 匹配 /user/123 /user/abc
Route::any('/user/{id}', [app\controller\UserController::class, 'get']);
namespace app\controller;
use support\Request;
class UserController
{public function get(Request $request, $id)
{return response('接收到参数'.$id);
}
}
更多例子:
use support\Request;
// 匹配 /user/123, 不匹配 /user/abc
Route::any('/user/{id:\d+}', function (Request $request, $id) {return response($id);
});
// 匹配 /user/foobar, 不匹配 /user/foo/bar
Route::any('/user/{name}', function (Request $request, $name) {return response($name);
});
// 匹配 /user /user/123 和 /user/abc []表示可选
Route::any('/user[/{name}]', function (Request $request, $name = null) {return response($name ?? 'tom');
});
// 匹配 任意以 /user/ 为前缀的请求
Route::any('/user/[{path:.+}]', function (Request $request) {return $request->path();
});
// 匹配所有 options 请求 : 后填写正则表达式,表示此命名参数的正则规则
Route::options('[{path:.+}]', function () {return response('');
});
进阶用法总结
[]语法在 Webman 路由中主要用于处理可选路径部分或匹配动态路由,它让你能够为路由定义更复杂的路径结构和匹配规则
: 用于指定正则表达式
路由分组
有时候路由包含了大量相同的前缀,这时候我们可以用路由分组来简化定义。例如:
Route::group('/blog', function () {Route::any('/create', function (Request $request) {return response('create');});
Route::any('/edit', function (Request $request) {return response('edit');});
Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
});
等价于
Route::any('/blog/create', function (Request $request) {return response('create');});
Route::any('/blog/edit', function (Request $request) {return response('edit');});
Route::any('/blog/view/{id}', function (Request $request, $id) {return response("view $id");});
group 嵌套使用
Route::group('/blog', function () {Route::group('/v1', function () {Route::any('/create', function (Request $request) {return response('create');});
Route::any('/edit', function (Request $request) {return response('edit');});
Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
});
});
路由中间件
我们可以给某个或某一组路由设置中间件。
例如:
Route::any('/admin', [app\admin\controller\IndexController::class, 'index'])->middleware([
app\middleware\MiddlewareA::class,
app\middleware\MiddlewareB::class,
]);
Route::group('/blog', function () {Route::any('/create', function () {return response('create');});
Route::any('/edit', function () {return response('edit');});
Route::any('/view/{id}', function ($request, $id) {return response("view $id");});
})->middleware([
app\middleware\MiddlewareA::class,
app\middleware\MiddlewareB::class,
]);
# 错误使用例子 (webman-framework >= 1.5.7 时此用法有效)
Route::group('/blog', function () {Route::group('/v1', function () {Route::any('/create', function (Request $request) {return response('create');});
Route::any('/edit', function (Request $request) {return response('edit');});
Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
});
})->middleware([
app\middleware\MiddlewareA::class,
app\middleware\MiddlewareB::class,
]);
# 正确使用例子
Route::group('/blog', function () {Route::group('/v1', function () {Route::any('/create', function (Request $request) {return response('create');});
Route::any('/edit', function (Request $request) {return response('edit');});
Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
})->middleware([
app\middleware\MiddlewareA::class,
app\middleware\MiddlewareB::class,
]);
});
资源型路由
Route::resource('/test', app\controller\IndexController::class);
// 指定资源路由
Route::resource('/test', app\controller\IndexController::class, ['index','create']);
// 非定义性资源路由
// 如 notify 访问地址则为 any 型路由 /test/notify 或 /test/notify/{id} 都可 routeName 为 test.notify
Route::resource('/test', app\controller\IndexController::class, ['index','create','notify']);
| Verb | URI | Action | Route Name |
|---|---|---|---|
| GET | /test | index | test.index |
| GET | /test/create | create | test.create |
| POST | /test | store | test.store |
| GET | /test/{id} | show | test.show |
| GET | /test/{id}/edit | edit | test.edit |
| PUT | /test/{id} | update | test.update |
| DELETE | /test/{id} | destroy | test.destroy |
| PUT | /test/{id}/recovery | recovery | test.recovery |
url 生成
注意
暂时不支持 group 嵌套的路由生成 url
例如路由:
Route::any('/blog/{id}', [app\controller\BlogController::class, 'view'])->name('blog.view');
我们可以使用如下方法生成这个路由的 url。
route('blog.view', ['id' => 100]); // 结果为 /blog/100
视图里使用路由的 url 时可以使用此方法,这样不管路由规则如何变化,url 都会自动生成,避免因路由地址调整导致大量更改视图文件的情况。
获取路由信息
通过 $request->route 对象我们可以获取当前请求路由信息,例如
$route = $request->route; // 等价于 $route = request()->route;
if ($route) {var_export($route->getPath());
var_export($route->getMethods());
var_export($route->getName());
var_export($route->getMiddleware());
var_export($route->getCallback());
var_export($route->param());
}
注意
如果当前请求没有匹配到 config/route.php 中配置的任何路由,则$request->route为 null,也就是说走默认路由时$request->route为 null
处理 404
当路由找不到时默认返回 404 状态码并输出 404 相关内容。
如果开发者想介入路由未找到时的业务流程,可以使用 webman 提供的回退路由 Route::fallback($callback) 方法。比如下面的代码逻辑是当路由未找到时重定向到首页。
Route::fallback(function(){return redirect('/');
});
再比如当路由不存在时返回一个 json 数据,这在 webman 作为 api 接口时非常实用。
Route::fallback(function(){return json(['code' => 404, 'msg' => '404 not found']);
});
给 404 添加中间件
默认 404 请求不会走任何中间件,如果需要给 404 请求添加中间件,请参考以下代码。
Route::fallback(function(){return json(['code' => 404, 'msg' => '404 not found']);
})->middleware([
app\middleware\MiddlewareA::class,
app\middleware\MiddlewareB::class,
]);
相关链接 自定义 404 500 页面
禁用默认路由
// 禁用主项目默认路由,不影响应用插件
Route::disableDefaultRoute();
// 禁用主项目的 admin 应用的路由,不影响应用插件
Route::disableDefaultRoute('','admin');
// 禁用 foo 插件的默认路由,不影响主项目
Route::disableDefaultRoute('foo');
// 禁用 foo 插件的 admin 应用的默认路由,不影响主项目
Route::disableDefaultRoute('foo', 'admin');
// 禁用控制器 [\app\controller\IndexController::class, 'index'] 的默认路由
Route::disableDefaultRoute([\app\controller\IndexController::class, 'index']);
注解禁用默认路由
我们可以通过注解禁用某个控制器的默认路由,例如:
namespace app\controller;
use support\annotation\DisableDefaultRoute;
#[DisableDefaultRoute]
class IndexController
{public function index()
{return 'index';}
}
同样的,我们也可以通过注解禁用某个控制器方法的默认路由,例如:
namespace app\controller;
use support\annotation\DisableDefaultRoute;
class IndexController
{#[DisableDefaultRoute]
public function index()
{return 'index';}
}
路由接口
// 设置 $uri 的任意方法请求的路由
Route::any($uri, $callback);
// 设置 $uri 的 get 请求的路由
Route::get($uri, $callback);
// 设置 $uri 的 post 请求的路由
Route::post($uri, $callback);
// 设置 $uri 的 put 请求的路由
Route::put($uri, $callback);
// 设置 $uri 的 patch 请求的路由
Route::patch($uri, $callback);
// 设置 $uri 的 delete 请求的路由
Route::delete($uri, $callback);
// 设置 $uri 的 head 请求的路由
Route::head($uri, $callback);
// 设置 $uri 的 options 请求的路由
Route::options($uri, $callback);
// 同时设置多种请求类型的路由
Route::add(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], $uri, $callback);
// 分组路由
Route::group($path, $callback);
// 资源路由
Route::resource($path, $callback, [$options]);
// 禁用路由
Route::disableDefaultRoute($plugin = '');
// 回退路由,设置默认的路由兜底
Route::fallback($callback, $plugin = '');
// 获取所有路由信息
Route::getRoutes();
如果 uri 没有对应的路由(包括默认路由),且回退路由也未设置,则会返回 404。
多个路由配置文件
如果你想使用多个路由配置文件对路由进行管理,例如 多应用 时每个应用下有自己的路由配置,这时可以通过 require 外部文件的方式加载外部路由配置文件。
例如 config/route.php 中
<?php
// 加载 admin 应用下的路由配置
require_once app_path('admin/config/route.php');
// 加载 api 应用下的路由配置
require_once app_path('api/config/route.php');