如何为PHP项目实现API版本控制?

wen PHP项目 1

本文目录导读:

如何为PHP项目实现API版本控制?

  1. URL路径版本控制(最常用、最直观)
  2. 请求头版本控制(更优雅、更规范)
  3. 查询参数版本控制(简单但不够规范)
  4. 推荐架构设计
  5. 最佳实践建议
  6. 完整示例:路由注册
  7. 版本迁移策略
  8. 推荐方案

为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规范。

抱歉,评论功能暂时关闭!