thinkphp 8.0需手动组合路由分组与中间件实现双模式api版本控制:uri路径(如/api/v1/user)通过prefix分组路由,header(api-version)依赖中间件注入version参数并统一分发,uri优先级高于header。

要在ThinkPHP 8.0中让同一个API接口支持v1、v2等不同版本,并允许客户端通过URL路径(如/api/v1/user)或HTTP请求头(如Api-Version: v2)两种方式指定版本,必须手动组合路由分组与中间件逻辑——框架本身不提供开箱即用的双模式版本识别。
配置URI路径版本(原生稳定方式)
这一步直接利用TP8路由系统最底层的能力,无需额外解析,兼容性最好、性能最高。
打开route/app.php,在HTTP路由区块内,为每个版本单独定义一个带前缀的路由分组:
Route::group('api', function () { Route::get('user/:id', 'User/read')->name('api.v1.user.read'); })->prefix('api/v1/');
Route::group('api', function () { Route::get('user/:id', 'UserV2/read')->name('api.v2.user.read'); Route::post('user', 'UserV2/create'); })->prefix('api/v2/');
【prefix必须写在分组链式调用末尾,且不能和version()混用——后者在TP8中不参与路径匹配,仅作标记】
这样访问/api/v1/user/123会进入User@read,而/api/v2/user/123则命中UserV2@read,互不干扰。
启用Header版本支持(需中间件注入参数)
当客户端不想改URL,只想在请求头里加Api-Version: v2就切换逻辑时,必须靠中间件把头信息转成路由可识别的参数。
第一步:创建中间件文件app/middleware/ApiVersionMiddleware.php,内容如下:
namespace app\middleware;
use think\Request;
use think\Response;
class ApiVersionMiddleware
{
public function handle(Request $request, \Closure $next): Response
{
$version = $request->header('Api-Version');
if ($version && in_array($version, ['v1', 'v2'])) {
$request = $request->withParam('version', $version);
}
return $next($request);
}
}
第二步:将该中间件注册到全局中间件列表,在app/middleware.php中加入:[\app\middleware\ApiVersionMiddleware::class],并确保它排在think\middleware\LoadLangPack::class之后、think\middleware\RouteCheck::class之前——顺序错了,$request->param('version')在路由匹配时就拿不到。
第三步:定义一个通用路由接收version参数,放在route/app.php中(注意要放在所有prefix分组之后):
Route::group('api', function () {
Route::get('user/:id', function ($id) {
$version = request()->param('version', 'v1');
return $version === 'v2'
? \app\controller\UserV2::read($id)
: \app\controller\User::read($id);
});
})->prefix('api/');
此时访问/api/user/123并带上Api-Version: v2头,就会走v2逻辑;没带头则默认v1。URI优先级高于Header,所以/api/v2/user/123永远走v2,不管Header里写啥。
统一版本分发逻辑(避免重复判断)
方法一:在基础控制器中封装版本路由分发
新建app/controller/ApiBase.php,继承think\Controller,写一个dispatchByVersion方法:
protected function dispatchByVersion(string $action, array $params = []): mixed
{
$version = $this->request->param('version', 'v1');
$class = match($version) {
'v1' => \app\controller\User::class,
'v2' => \app\controller\UserV2::class,
default => \app\controller\User::class,
};
return call_user_func_array([$class, $action], $params);
}
然后在具体控制器里调用:return $this->dispatchByVersion('read', [$id]);
方法二:用服务容器绑定动态类
在app/provider/AppServiceProvider.php的register方法中加入:
$this->app->bind('api.user.service', function ($app) {
$version = $app->request->param('version', 'v1');
return match($version) {
'v1' => new \app\service\UserV1Service(),
'v2' => new \app\service\UserV2Service(),
};
});
控制器中直接$this->app->make('api.user.service')->read($id)即可。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











