本文目录导读:

为PHP项目实现API版本控制,常见且推荐的方式有以下几种,按推荐程度排序:
URL路径版本控制(最常用、最直观)
实现方式
/api/v1/users
/api/v2/users
/api/v1/orders
/api/v2/orders
代码实现
方案A:使用路由分组(推荐)
// routes/api.php
Route::group(['prefix' => 'v1'], function () {
Route::get('/users', 'V1\UserController@index');
Route::post('/users', 'V1\UserController@store');
});
Route::group(['prefix' => 'v2'], function () {
Route::get('/users', 'V2\UserController@index');
Route::post('/users', 'V2\UserController@store');
});
方案B:路径解析中间件
class VersionMiddleware {
public function handle($request, $next) {
$version = $request->segment(1); // 获取 v1 或 v2
// 将版本号注入到控制器命名空间
$request->attributes->set('version', $version);
return $next($request);
}
}
请求头版本控制(更优雅、更规范)
实现方式
GET /api/users Accept: application/vnd.myapi.v1+json
代码实现
class HeaderVersionMiddleware {
public function handle($request, $next) {
$acceptHeader = $request->header('Accept');
// 解析版本号: application/vnd.myapi.v1+json
if (preg_match('/vnd\.myapi\.v(\d+)/', $acceptHeader, $matches)) {
$version = $matches[1];
$request->attributes->set('api_version', $version);
} else {
// 默认版本
$request->attributes->set('api_version', '1');
}
return $next($request);
}
}
查询参数版本控制(简单但不够规范)
实现方式
GET /api/users?version=1 GET /api/users?version=2
代码实现
class QueryVersionMiddleware {
public function handle($request, $next) {
$version = $request->query('version', '1');
$request->attributes->set('api_version', $version);
return $next($request);
}
}
推荐架构设计
结合使用URL路径 + 命名空间
// app/Http/Controllers/Api/V1/UserController.php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Http\Resources\V1\UserResource;
class UserController extends Controller {
public function index() {
return UserResource::collection(User::paginate());
}
}
// app/Http/Controllers/Api/V2/UserController.php
namespace App\Http\Controllers\Api\V2;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Http\Resources\V2\UserResource;
class UserController extends Controller {
public function index() {
// V2 版本可能有不同的逻辑
return UserResource::collection(User::with('profile')->paginate());
}
}
最佳实践建议
a) 版本管理策略
// config/api.php
return [
'default_version' => 'v1',
'supported_versions' => ['v1', 'v2', 'v3'],
'deprecated_versions' => [
'v1' => '2024-12-31',
],
];
b) 版本检查中间件
class VersionCheckMiddleware {
public function handle($request, $next) {
$version = $request->route('version');
// 检查版本是否支持
if (!in_array($version, config('api.supported_versions'))) {
return response()->json([
'error' => 'Unsupported API version',
'message' => "Version {$version} is not supported"
], 400);
}
// 检查版本是否已废弃
if (array_key_exists($version, config('api.deprecated_versions'))) {
$deprecatedDate = config('api.deprecated_versions')[$version];
return response()->json([
'warning' => "Version {$version} is deprecated",
'sunset' => $deprecatedDate
], 200, [
'Sunset' => $deprecatedDate,
'Deprecation' => true
]);
}
return $next($request);
}
}
c) 版本化资源/请求
// 版本化的资源
// app/Http/Resources/V1/UserResource.php
namespace App\Http\Resources\V1;
class UserResource extends JsonResource {
public function toArray($request) {
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
];
}
}
// app/Http/Resources/V2/UserResource.php
namespace App\Http\Resources\V2;
class UserResource extends JsonResource {
public function toArray($request) {
return [
'id' => $this->id,
'full_name' => $this->name,
'email' => $this->email,
'profile_picture' => $this->avatar_url,
];
}
}
完整示例:路由注册
// routes/api.php
use Illuminate\Support\Facades\Route;
// 自动注册所有版本
foreach (['v1', 'v2'] as $version) {
Route::group([
'prefix' => $version,
'namespace' => "App\Http\Controllers\Api\\" . strtoupper($version),
'middleware' => ['api', "version:{$version}"]
], function () use ($version) {
// 用户相关
Route::get('/users', 'UserController@index');
Route::post('/users', 'UserController@store');
// 订单相关
Route::get('/orders', 'OrderController@index');
});
}
版本迁移策略
// 当V2与V1差异不大时,使用适配器模式
class UserControllerV2 extends UserControllerV1 {
public function index() {
$data = parent::index(); // 调用V1的实现
// 对数据进行V2格式转换
return $this->transformToV2($data);
}
}
推荐方案
对于大多数中小型项目,推荐使用URL路径版本控制(方案1),原因:
- 最直观:开发者、客户端都能清晰看到版本
- 易于调试:可以直接在浏览器、Postman中测试
- 缓存友好:不同版本可以独立缓存
- 转换成本低:迁移时只需调整路由和控制器
对于大型项目、微服务架构,可以考虑请求头版本控制,更符合RESTful规范。