Hyperf方案 API版本管理

张开发
2026/4/10 22:25:10 15 分钟阅读

分享文章

Hyperf方案 API版本管理
?php/** * 案例053API版本管理 * 说明路由前缀v1/v2实现版本隔离新旧版本并存平滑升级 * 需要安装的包hyperf/http-server */declare(strict_types1);namespaceApp\Controller;useHyperf\HttpServer\Annotation\Controller;useHyperf\HttpServer\Annotation\GetMapping;useHyperf\HttpServer\Annotation\Middleware;useHyperf\HttpServer\Contract\RequestInterface;/** * 版本管理策略 * 方案一推荐URL路径版本号 /api/v1/users、/api/v2/users * 方案二请求头版本号 Accept: application/vnd.apijson;version2 * 方案三查询参数 /api/users?version2 * * 这里用方案一最直观对缓存也友好 */// V1版本旧版本继续维护中 /** * V1用户接口 * 特点返回字段少格式简单 */#[Controller(prefix:/api/v1/users)]classUserV1Controller{publicfunction__construct(privateRequestInterface$request){}/** * GET /api/v1/users/{id} * V1返回格式字段少直接返回data */#[GetMapping(path:{id:\d})]publicfunctionshow(int$id):array{$user\Hyperf\DbConnection\Db::table(users)-find($id);if(!$user){return[code404,message用户不存在];}// V1只返回基础字段return[code0,data[id$user-id,name$user-name,// V1用name字段],];}#[GetMapping(path:)]publicfunctionindex():array{$users\Hyperf\DbConnection\Db::table(users)-select([id,name])// V1只返回id和name-paginate(20);return[code0,data$users];}}// V2版本新版本 /** * V2用户接口 * 特点字段更丰富格式有变化比如name改成了nickname */#[Controller(prefix:/api/v2/users)]classUserV2Controller{publicfunction__construct(privateRequestInterface$request){}/** * GET /api/v2/users/{id} * V2返回格式变化 * 1. name字段改名为nickname * 2. 新增avatar、created_at字段 * 3. 分页信息更丰富 */#[GetMapping(path:{id:\d})]publicfunctionshow(int$id):array{$user\Hyperf\DbConnection\Db::table(users)-find($id);if(!$user){return[code404,message用户不存在,datanull];}return[code0,messagesuccess,data[id$user-id,nickname$user-name,// V2改名了avatar$user-avatar??,// V2新增字段email$user-email,// V2新增字段created_at$user-created_at,],];}#[GetMapping(path:)]publicfunctionindex():array{$page(int)$this-request-query(page,1);$size(int)$this-request-query(size,20);$total\Hyperf\DbConnection\Db::table(users)-count();$users\Hyperf\DbConnection\Db::table(users)-select([id,name as nickname,avatar,email])// V2字段-forPage($page,$size)-get();return[code0,data$users,meta[total$total,page$page,size$size],// V2有meta];}}/** * 版本路由器根据请求头里的Accept版本号自动路由到对应版本 * 同时支持URL路径版本和请求头版本两种方式 */classApiVersionMiddlewareimplements\Psr\Http\Server\MiddlewareInterface{publicfunctionprocess(\Psr\Http\Message\ServerRequestInterface$request,\Psr\Http\Server\RequestHandlerInterface$handler):\Psr\Http\Message\ResponseInterface{// 从Accept头解析版本号如 Accept: application/json; version2$accept$request-getHeaderLine(Accept);$versionv1;// 默认版本if(preg_match(/version[\/](\d)/i,$accept,$m)){$versionv.$m[1];}// URL路径里有明确版本号就用URL的优先级更高$path$request-getUri()-getPath();if(preg_match(#/api/(v\d)/#,$path,$m)){$version$m[1];}// 存到协程上下文Controller里可以读到\Hyperf\Context\Context::set(api_version,$version);return$handler-handle($request);}}/** * 版本兼容性处理用装饰器模式V2继承V1复用代码只重写变更的方法 */classUserV3ControllerextendsUserV2Controller{/** * V3只改了详情接口列表接口复用V2的 */#[GetMapping(path:{id:\d})]publicfunctionshow(int$id):array{$v2Resultparent::show($id);if($v2Result[code]!0)return$v2Result;// V3在V2基础上增加更多字段$v2Result[data][extra_field]v3新字段;return$v2Result;}}

更多文章