php - 使用 PHP/Slim 和 Apache 实现 RESTful API 的版本控制
问题描述
根据一些评论,我在CodeReview上创建了一个问题。
我想用 Apache/PHP 和 Slim Framework (3.x) 创建一个 RESTful API。API 应支持基于 URI 的版本控制,例如<host>/rest/api/v1/<resource>
和<host>/rest/api/latest/<resource>
.
总的来说,我找到了一个可行的解决方案,但我对我的解决方案并不满意,我想知道我可以做得更好。我是 Slim 的新手/新手。我正在寻找新的想法以及如何提高我对 Slim 的了解。
- 一般改进或评论?
- 你有更好/更简单的解决方案吗?
- 您在我的解决方案中看到问题了吗?
- .htaccess / 重写规则的改进
- 链接到带有 Slim 的 RESTful API 的实际实现
- ……
我期待着你的回答,我很想看看有什么新东西。
每个新版本都应该是一个新项目,有自己独立的代码库。版本调度应该由 Apache 服务器完成,而不是在 PHP/Slim 代码中。我发现了一些使用 group-method 在项目中实现不同版本的 API 的示例。但我对这个解决方案不是很满意。我觉得有独立版本的独立项目会更好。
我在 htdocs 文件夹中创建一个文件夹/文件结构,如下所示:
rest
+--api
+-v1
+-.htaccess
+-api.php
+-v2
+-.htaccess
+-api.php
+-.htaccess
API 的实现在文件中api.php
。/rest/api/v1/books
像我的实现一样映射调用我在每个版本文件夹中创建一个.htaccess
文件,其中包含重写 Apache 模块的规则:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^$ / [QSA,L]
RewriteRule ^.*$ api.php [QSA,L]
第一个重写规则需要匹配调用/rest/api/v1
,第二个规则重写路径,如 /rest/api/v1/books
实现。
在api.php
我创建一些路线
$app->get('/books', function ...
$app->get('/books/{id}', function ...
如果我在 URI ( <host>/rest/api/v2/books
) 中使用显式版本,一切正常。为方便起见,如果我创建一个别名latest ( /rest/api/latest/books
),它将别名调用重定向到 API 版本的最新版本。
因此,我创建了rest/api/.htaccess
将 URI 重写为实现的文件:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^latest.*$ ./v2/api.php [QSA,L]
重写规则工作正常,并调用了 v2 实现。但是路线不再匹配。我发现原来的路径现在是路径的一部分。
/rest/api/v2/books -> /books
/rest/api/latest/books -> /rest/api/latest/books
如果我像这样修改规则,它会起作用——但我不喜欢每条规则都执行两次。
$app->get('/rest/api/latest/books', function ...
$app->get('/rest/api/latest/books/{id}', function ...
因此,我编写了一个中间件函数,该函数/rest/api/latest
在匹配路由之前从路径中修剪。
$app->add(function (Request $request, Response $response, callable $next) {
$uri = $request->getUri();
$path = $uri->getPath();
if (substr($path,0,16) == "/rest/api/latest") {
$uri = $uri->withPath(substr($path,16));
return $next($request->withUri($uri), $response);
}
return $next($request, $response);
});
现在我可以对两种情况使用相同的规则,显式版本调用和隐式版本调用。
解决方案
推荐阅读
- ios - 如何从 Firebase Firestore 获取时间戳日期?
- javascript - 在 Javascript 中创建字符串搜索功能 - React Native
- django - gunicorn 和 Django 项目
- nginx - 运行 Nginx ./configure 时,Ansible playbook 挂起
- swift - 从 github 克隆后 Xcode 项目工作区丢失文件
- wordpress - 自定义古腾堡块自动包装
- c# - 如何在 C# 中使用 3rd 方 DLL
- .net - 运行 Jenkins 的 Docker 容器中的 Dotnet 构建权限被拒绝
- azure - VSTS/Azure DevOps 在“npm install”上失败
- excel - VBA:将 Excel 工作表复制到另一个工作簿中