reindert-vetter / api-version-control
一个用于优雅地管理端点版本的 Laravel 扩展包
Requires
- php: >=7.1
- illuminate/routing: >=5.7.12
Requires (Dev)
- phpunit/phpunit: ^7.0
README
一个用于优雅地管理端点版本的 Laravel 扩展包。
有关新闻,请关注我的 Twitter。
两种管理端点版本的方法
选项 1: 版本声明
你可能使用 if 语句来确定是否应该从特定版本执行代码。但如果你想要为两个端点运行此代码,一个是从版本 2 运行的,另一个是从版本 3 运行的,你该怎么办?此扩展包为此提供了一种干净的解决方案:版本声明。
选项 2: 版本中间件
遗留代码可能会很快阻碍进度。因此,你因此创建了多个控制器来分离旧代码和新代码?如果有 10 个版本,你将如何做到这一点?那时,你是否也将为每个端点创建 10 个验证方案和响应类?此扩展包还提供了一种比 版本声明 更进一步的 SOLID 解决方案:版本中间件。
你可以在一个项目中同时使用 版本中间件 和 版本声明
优势
注意 版本中间件:如果你还没有使用自定义中间件,你可以从控制器中进行调试。有了 版本中间件,同事们现在必须理解,只有当端点处于旧版本时,中间件中的代码也会影响其他代码。
如何使用
版本
在 api_version_control.php 配置文件中,您将看到版本数组
'releases' => [ 'orders.index' => [ '<=1' => [ PrepareParameterException::class, ], ], 'orders.store|orders.update' => [ '<=2' => [ ThrowCustomException::class, ValidateZipCode::class, ], '<=1' => [ PrepareParameterException::class, ], ], 'default' => [ '<=1' => [ ThrowCustomException::class, ], ], 'all' => [ '<=1.0' => [ RequireUserAgent::class, ], ], ],
路由匹配
您将路由名称放入 releases 数组的键中。键必须与当前路由名称匹配。使用 |
匹配多个路由名称。包将遍历路由名称。如果找到匹配项,则停止搜索。匹配项包含 版本规则。如果找不到路由名称匹配项,则使用 默认。这样,您可以更新您的其他端点。要匹配所有端点的版本,您可以使用 all 键。
您必须在您的路由器中指定路由名称。 示例:
Route::get('orders', 'OrdersController@index')->name('orders.index');
。当使用资源控制器时,名称会自动确定。有关更多信息,请参阅 Laravel 文档。
版本规则
版本规则包含一个运算符和版本('<=2'
)的字符串。支持的运算符有:<
、<=
、>
、>=
、==
、!=
。所有匹配的 版本规则 中的类都将使用。位于 版本规则 中的类是 版本声明 和 版本中间件。
版本声明
一个 版本声明 文件看起来像这样
<?php namespace App\VersionControl\Orders; use ReindertVetter\ApiVersionControl\Concerns\VersionStatement; class ValidateZipCode { use VersionStatement; }
如果文件包含特质 \ReindertVetter\ApiVersionControl\Concerns\VersionStatement
,那么你可以在你的源代码中这样做
if (ValidateZipCode::permitted()) { (...) }
版本中间件
在中间件中处理所有请求和响应,这与最新版本不同。您可以使用多个中间件调整请求以匹配最新版本。您还可以在版本中间件中调整响应的格式。
一个版本中间件文件(用于更改请求)可能如下所示
<?php namespace App\Middleware\Version; use Closure; use Illuminate\Http\Request; class PrepareParameterException { /** * @param $request * @param \Closure $next * @return mixed */ public function handle(Request $request, Closure $next) { // Set the default parameter because it is required in a newer version. $request->query->set('sort', 'DESC'); return $next($request); } }
一个版本中间件文件(用于更改响应)可能如下所示
<?php namespace App\Middleware\Version; use Closure; use Illuminate\Http\Request; class ThrowHumanException { /** * @param $request * @param \Closure $next * @return mixed */ public function handle(Request $request, Closure $next) { /** @var \Illuminate\Http\Response $response */ $response = $next($request); // Catch the exception to return an exception in a different format. if ($response->exception) { $response->setContent( [ "errors" => [ [ "human" => $response->exception->getMessage(), ], ], ] ); } return $response; } }
请求和资源绑定
您可以将FormRequest或Resource绑定到处理其他版本。这样,您可以更容易地使用规则支持不同的参数,并且可以更容易地支持不同的资源。支持不同版本的控制器可能如下所示
public function index(OrderIndexRequest $request, OrderResource $resource): ResourceCollection { $orders = Order::query() ->productIds($request->productIds()) ->with($resource->withRelationships()) ->paginate($request->limit()); return $resource::collection($orders); }
请求变量$request
可以是OrderIndexRequestV1或OrderIndexRequestV2,资源变量$resource
可以是OrderResourceV1或OrderResourceV2。OrderIndexRequestV2必须扩展基类OrderIndexRequest。您可以为资源类执行相同的操作。当使用Bind
中间件时,配置将如下所示
<?php use ReindertVetter\ApiVersionControl\Middleware\Version\Bind; return [ 'releases' => [ 'orders.index' => [ '<=1' => [ new Bind(OrderIndexRequest::class, OrderIndexRequestV1::class), new Bind(OrderIndexResource::class, OrderIndexResourceV1::class), ], '>=2' => [ new Bind(OrderIndexRequest::class, OrderIndexRequestV2::class), new Bind(OrderIndexResource::class, OrderIndexResourceV2::class), ], ], ] ]
如果还不清楚,请在讨论中发布您的问题。
版本解析器
默认情况下,此包支持头部的版本和URI中的版本。但您也可以创建自己的版本解析器。在api_version_control.php配置文件中指定此内容。
安装
- 运行
composer require reindert-vetter/api-version-control
。 - 在您的
RouteServiceProvider
中添加->middleware(['api', ApiVersionControl::class])
。
如果您使用的是URL版本解析器(这是默认设置),请确保URL中存在版本变量。例如
Route::middleware(['api', ApiVersionControl::class]) ->prefix('api/{version}') ->where(['version' => 'v\d{1,3}']) ->group(base_path('routes/api.php'));
现在,只有带有版本的URL(例如/api/v2/products
)可以访问路由。您也想让端点在没有版本的情况下工作吗?然后首先定义没有版本变量的路由
Route::middleware(['api', ApiVersionControl::class]) ->prefix('api') ->as('no_version.') ->group(base_path('routes/api.php')); Route::middleware(['api', ApiVersionControl::class]) ->prefix('api/{version}') ->where(['version' => 'v\d{1,3}']) ->group(base_path('routes/api.php'));
您可以看到,我们使用no_version.
(用于没有版本的router)前缀路由名称。您必须这样做以避免在缓存路由时出现错误Another route is already using that name
。自行决定这是否适合您的应用程序。
- 在config/app.php中将
\ReindertVetter\ApiVersionControl\ApiVersionControlServiceProvider::class
添加到您的提供者中 - 运行
php artisan vendor:publish --provider='ReindertVetter\ApiVersionControl\ApiVersionControlServiceProvider'
以创建配置文件。 - 选择一个版本解析器或自己创建一个。
- 在需要时运行
php artisan route:clear
或php artisan route:cache
。
如果还不清楚,请在讨论中发布您的问题。