rollun-com / rollun-openapi
rollun骨架与openapi生成代码之间的桥梁
Requires
- php: >8.0
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- articus/data-transfer: ^0.5.2
- articus/path-handler: 0.6.1
- doctrine/annotations: ^1.8
- guzzle/guzzle: dev-php-8.0 as v3.9.0
- laminas/laminas-cache: ^3.1.2
- laminas/laminas-cache-storage-adapter-blackhole: ^2.0.0
- laminas/laminas-cache-storage-adapter-filesystem: ^2.0.1
- laminas/laminas-code: *
- laminas/laminas-component-installer: *
- laminas/laminas-config-aggregator: ^1.7
- laminas/laminas-diactoros: ^2.8.0
- laminas/laminas-serializer: ^2.13.0
- laminas/laminas-servicemanager: ^3.10
- mezzio/mezzio: ^3.9
- mezzio/mezzio-fastroute: ^3.4
- mezzio/mezzio-helpers: ^5.8
- nette/php-generator: ^3.4
- nikic/fast-route: ^1.3
- rollun-com/rollun-callback: ^7.0.0
- rollun-com/rollun-dic: ^4.0.0
- symfony/dotenv: ^6.0.3
Requires (Dev)
- ext-yaml: *
- filp/whoops: ^2.15
- phpunit/phpunit: ^9.5.10
- rollun-com/rollun-datastore: ^9.0
- dev-master
- 11.0.0
- 10.3.0
- 10.2.1
- 10.2.0
- 10.1.8
- 10.1.7
- 10.1.6
- 10.1.5
- 10.1.4
- 10.1.3
- 10.1.2
- 10.1.1
- 10.1.0
- 10.0.2
- 10.0.1
- 10.0.0
- v9.x-dev
- 9.2.0
- 9.1.4
- 9.1.3
- 9.1.2
- 9.1.1
- 9.1.0
- 9.0.9
- 9.0.8
- 9.0.7
- 9.0.5
- 9.0.4
- 9.0.3
- 9.0.2
- 9.0.1
- 9.0.0
- v8.x-dev
- 8.4.1
- 8.4.0
- 8.3.2
- 8.3.1
- 8.3.0
- 8.2.3
- 8.2.2
- 8.2.1
- 8.2.0
- 8.1.1
- 8.1.0
- 8.0.4
- 8.0.3
- 8.0.2
- 8.0.1
- 8.0.0
- 7.0.0
- 6.x-dev
- 6.3.1
- 6.3.0
- 6.2.0
- 6.1.0
- 6.0.0
- 4.0.1
- 4.0.0
- 3.x-dev
- 3.2.0
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.1
- 3.0.0
- 2.x-dev
- 2.1.3
- 2.1.2
- 2.1.1
- 2.1.0
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.0.1
- 1.0.0
- dev-misha-rollun-patch-1
- dev-reset-bug
- dev-iliya
- dev-specification
- dev-connection_exceptions_handling
- dev-fix_http_status_code
- dev-laminas
- dev-long_tasks
- dev-docker-php7.2
- dev-docker-openapi-generator
- dev-feat/draft-openapi-docs
- dev-interfaces_example
- dev-array_in_query
- dev-message_writing
- dev-custom-client
- dev-union-models
This package is auto-updated.
Last update: 2024-09-14 12:39:57 UTC
README
该库包含一个PHP脚本,用于从openapi规范中生成客户端或服务器端的代码。该脚本通过openapi-generator工具运行。
Openapi规范是一个特定结构的文档,用于描述HTTP API:URL路径、请求/响应数据格式、认证等。更多详情可以在这里阅读这里。
基于此规范可以生成客户端或服务器端的代码。
对于客户端部分,生成的API客户端可以用来向该API发送请求。
生成的服务器端代码将包含需要实现的控制器模板。
客户端和服务器都将包含验证以及数据对象与HTTP请求/响应之间的序列化和反序列化。
因此,您需要在项目的composer.json文件中包含此库,因为它包含用于运行生成代码所需的类。
快速开始
什么是openapi
Openapi是一种文件格式,用于描述http API:请求/响应格式和端点。
一个简单的openapi文件示例,描述一个返回用户名数组的API端点/users
,如下所示
openapi: 3.0.0 info: title: Sample API description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. version: 0.1.9 servers: - url: http://api.example.com/v1 description: Optional server description, e.g. Main (production) server - url: http://staging-api.example.com description: Optional server description, e.g. Internal staging server for testing paths: /users: get: summary: Returns a list of users. description: Optional extended description in CommonMark or HTML. responses: '200': # status code description: A JSON array of user names content: application/json: schema: type: array items: type: string
更多关于openapi规范格式的信息可以在swagger.io文档中找到
Openapi生成器
此文件可以用各种工具使用,例如swagger ui生成具有交互式文档的界面。
存在另一个工具openapi-generator,它可以根据openapi文档(规范)生成代码。此代码可能包含请求/响应对象、它们的验证、不同媒体类型的序列化/反序列化、测试等。
代码生成器可以分为两类:服务器端和客户端。它们根据您的程序是作为客户端还是服务器,或者两者都是(代理服务器)来使用。
服务器端生成器生成控制器模板,程序员需要实现以获得可接收请求的有效服务器。
客户端生成器生成代码,允许您方便地向服务器发送请求并处理响应。
实际上,我们的生成器由两个生成器组成:客户端和服务器端,分别通过generate:client
和generate:server
命令启动。
编写规范
通常规范由实现API的人或需要API的人编写。
为了编写规范,我们使用部署在我们的服务器上的swagger-editor,可通过以下链接访问:swagger-editor.rollun.net。此编辑器与github上的rollun-com/openapi-manifests存储库集成,其中存储所有我们的规范,并允许在存储库中打开或保存规范。请确保将规范保存在此存储库中,因为它可能被我们的程序用于搜索规范。有关保存规范的说明可以在rollun-com/openapi-manifests存储库中找到。
为了简化规范的编写,我们提供了一个名为skeleton的规范模板。您可以通过在swagger-editor中点击“打开规范”按钮找到它。
然后,将打开一个选择规范的窗口,其中您可以找到skeleton规范。
关于创建清单的规则,可以阅读manifests.md。
启动生成器
将库安装到您的项目(微服务)中
composer require rollun-com/rollun-openapi
重要 在 composer 处理完毕后,请检查 /config/config.php
文件中是否存在配置提供者 \OpenAPI\ConfigProvider::class
,以及它是否在 \Zend\Expressive\Router\FastRouteRouter\ConfigProvider::class
或 \Mezzio\Router\FastRouteRouter\ConfigProvider::class
之后加载,否则将无法工作。
之后,您需要通过 php 运行该库中的脚本 ./bin/openapi-generator,使用命令 generate:server
生成服务器端代码,如果需要生成客户端代码,则使用 generate:client
。脚本将自动询问清单路径,但也可以通过参数 -m
直接指定。
如果通过 composer 将此库安装到您的项目中,则该脚本将位于
./vendor/bin/openapi-generator
路径,而不是./bin/openapi-generator
。
重要 为避免错误,此脚本必须在已安装 openapi-generator 的环境中运行。这可以通过以下两种方式实现
- 按照其网站上的说明,在本地系统中安装 openapi-generator。
- 使用 docker,并在容器内运行此脚本。
通过 docker 启动生成
docker run --rm \
-v $PWD:/var/www/app \
maxrollundev/php-openapi-generator:8.0 \
php vendor/bin/openapi-generator generate:server \
-m openapi.yaml
其中
-v $PWD:/var/www/app
- 创建从主机当前目录到容器/var/www/app
目录的卷(此路径很方便使用,因为对于该容器来说,它默认是工作目录)maxrollundev/php-openapi-generator:8.0
- 容器名称(8.0 - php 版本)php vendor/bin/openapi-generator generate:server
- 直接运行生成器脚本(对于客户端,将generate:server
替换为generate:client
)-m openapi.yaml
- 清单路径(可以是 URL)
如果您在项目中使用 docker-compose,则可以在 services 部分添加生成器服务
services: # ... php-openapi-generator: image: maxrollundev/php-openapi-generator:8.0 volumes: - ./:/var/www/app
然后可以通过以下方式启动生成器
docker-compose run --rm php-openapi-generator \ php vendor/bin/openapi-generator generate:server \ -m openapi.yaml
不使用 docker 启动生成
-
安装 openapi-generator 版本低于 5(不包括 5)。要进行检查,请执行命令
openapi-generator version
,如果已安装 openapi-generator,您将看到生成器的版本。生成器版本必须低于第五版。 这是因为在第五版中 移除了 我们使用的生成器,它已被重命名并改为使用 Laminas 代替 Zend。
-
要生成代码,请执行命令
php vendor/bin/openapi-generator generate:server
或php vendor/bin/openapi-generator generate:client
该命令支持参数。参数以 --name=value 的形式传递。目前已实现通过路径或 URL 指定清单(manifest)参数。例如
php vendor/bin/openapi-generator generate:client --manifest=openapy.yaml
生成后的设置
必须将生成的类添加到 composer 的自动加载器中。
"autoload": {
"psr-4": {
"SomeModule\\": "src/SomeModule/src/"
}
},
其中,SomeModule 是清单的标题
如果出现错误
-
请检查容器中是否存在
rollun\logger\LifeCycleToken
。在容器中,此名称下应存在一个包含当前应用程序生命周期标识符的字符串。
建议的方法是安装 rollun-com/rollun-logger 库。该库包含 LifeCycleToken。有关如何在容器中安装它的说明,请参阅库的文档。
使用生成的服务器
服务器生成器生成需要程序员实现的控制器模板。控制器模板位于路径 src/{ManifestTitle}/src/OpenaAPI/{ManifestVersion}/Server/Rest
。例如 User.php(manifest)openapi.yaml
<?php namespace HelloUser\OpenAPI\V1\Server\Rest; use Articus\DataTransfer\Service as DataTransferService; use OpenAPI\Server\Rest\Base7Abstract; use Psr\Log\LoggerInterface; use rollun\dic\InsideConstruct; /** * Class User */ class User extends Base7Abstract { public const CONTROLLER_OBJECT = 'User1Controller'; /** @var object */ protected $controllerObject; /** @var LoggerInterface */ protected $logger; /** @var DataTransferService */ protected $dataTransfer; /** * User constructor. * * @param mixed $controllerObject * @param LoggerInterface|null logger * @param DataTransferService|null dataTransfer * * @throws \ReflectionException */ public function __construct($controllerObject = null, $logger = null, $dataTransfer = null) { InsideConstruct::init([ 'controllerObject' => static::CONTROLLER_OBJECT, 'logger' => LoggerInterface::class, 'dataTransfer' => DataTransferService::class ]); } /** * @inheritDoc * * @param \HelloUser\OpenAPI\V1\DTO\User $bodyData */ public function post($bodyData = null) { if (method_exists($this->controllerObject, 'post')) { $bodyDataArray =$this->dataTransfer->extractFromTypedData($bodyData); return $this->controllerObject->post($bodyDataArray); } throw new \Exception('Not implemented method'); } /** * @inheritDoc */ public function getById($id) { if (method_exists($this->controllerObject, 'getById')) { return $this->controllerObject->getById($id); } throw new \Exception('Not implemented method'); } }
该类中的 post
和 getById
方法将在处理请求时被调用。如所示,该类将方法委托给某个 controllerObject
。这个 controllerObject
是程序员需要创建的类,在其中编写所有必要方法(在本例中是 post
和 getById
)的实现。例如 UserController。之后,将该类放置在依赖注入容器中,名称为常量 CONTROLLER_OBJECT,在本例中为 'User1Controller'。这最简单的方法是在配置中添加别名:示例
使用生成的客户端
客户端使用起来更简单,在生成后程序员不需要进行任何额外操作。类似于服务器,在目录 src/{ManifestTitle}/src/OpenaAPI/{ManifestVersion}/Client/Rest
中生成 API 客户端类,允许发送请求。
可以从容器中获取所需类,它已经准备好使用。
日期和时间格式
根据 OpenApi 规范,日期和时间应以格式 OpenApi 返回,格式为 RFC 3339, section 5.6。例如:"2017-07-21T17:32:28Z","2020-12-11T15:04:02.255Z"。需要注意的是,PHP 的 \DateTime::RFC3339 ('Y-m-d\TH:i:sP')
格式并不完全符合当前的 RFC 3339 格式,即 PHP 的 \DateTime::RFC3339
不允许字符串末尾有 Z,也没有支持可选的毫秒数。
到版本 6.1.0,不支持毫秒数,日期时间的验证格式为 'Y-m-d\TH:i:s\Z'
.
从版本 6.1.0 开始,验证器被添加以完全符合规范 RFC 3339, section 5.6。但是,必须重新生成代码,以更改生成的 DTO 注解中的日期格式。
是否将库放入 require-dev 部分?
不是的,这个库中的几乎所有类都需要在生产环境中使用:路由、DTO 序列化等。生成代码使用的是 ./bin 目录中的命令、template 模板以及 nette/php-generator
包。目前,这些依赖项仍然保留在包中,并在生产中拉取。
服务器部分实现文档
客户端部分实现文档
主机切换
从版本 3.1.0 开始,Rest 类实现了接口 OpenAPI\Client\Rest\ClientInterface
,该接口包含接口 OpenAPI\Client\Rest\HostSelectionInterface
,允许切换主机。
为了使用这个功能,将 OpenAPI\Server\Rest\RestInterface
替换为 OpenAPI\Client\Rest\ClientInterface
,它也包含 RestInterface,因此不会破坏任何东西。
<?php namespace OpenAPI; use HelloUser\OpenAPI\V1\Client\Rest\Hello; use OpenAPI\Client\Rest\ClientInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use rollun\dic\InsideConstruct;use Zend\Diactoros\Response; class TestHandler implements RequestHandlerInterface { /** * @var ClientInterface|null */ private $rest; public function __construct(ClientInterface $rest = null) { InsideConstruct::init(['rest' => Hello::class]); } public function handle(ServerRequestInterface $request): ResponseInterface { $this->rest->setHostIndex(1); $result = $this->rest->getById('10'); return new Response\JsonResponse($result); } }
composer install 停滞
可能是因为 "rollun-com/rollun-callback" 库的问题。尝试将其从 composer.json 中删除并重新运行安装。如果一切顺利,则通过 composer require 单独安装此库。
用户操作和端点
除了标准的 CRUD 操作外,现在还可以生成代码以发送客户端和服务器处理的用户方法,这些方法将按您的要求运行端点。
之前我们只能生成8个方法,因此也只能有8个路径,例如对于某个实体“订单”。
现在可以生成任何其他PHP方法和任何其他路径。例如,需要生成一个处理POST请求的路径为/order/{id}/user的方法。首先,需要将所需路径添加到清单中。接下来有两种方式可以将该路径绑定到您的PHP方法。
选项1. 您可以不指定任何其他内容(除了路径)。
paths: /order/{id}/user: post: tags: - Order parameters: - name: id in: path schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/User'
在这种情况下将生成OrderIdUserPost方法。也就是说,类中的方法(Handler、Rest、Api)将根据以下模式自动生成 - 路径 + http方法(全部使用camelCase)。在控制器中需要描述一个具有相同名称的方法。该方法将接受2个参数 - $id和$bodyData,因此控制器的大致样子如下
public function orderIdUserPost(string $id, User $bodyData) { // code }
选项2. 在清单中指定operationId。
如果您不想以这种方式生成方法,即如果方法需要设置一个逻辑上易于理解的名称,可以在清单中指定operationId。
paths: /order/{id}/user: post: tags: - Order operationId: setOrderUser parameters: - name: id in: path schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/User'
在这种情况下,所有方法都将命名为setOrderUser。相应地,在控制器中也需要描述一个具有相同名称的方法。
public function setOrderUser(string $id, User $bodyData) { // code }
关于路径长度的注意事项。建议路径不要超过两级。也就是说,允许路径/order/{id}/user,但不允许/order/{id}/user/roles等。
启动测试
使用docker
需要确保系统中已安装
- docker
- docker-compose
- make工具
只需首先运行make up
即可启动应用程序。之后,为了执行测试,请运行make test
。要停止应用程序,请运行make down
。
不使用docker
可以通过composer test
运行测试。在某些测试中会启动内置的php服务器并监听端口8081,因此非常重要确保该端口是可用的。
常见问题
- 清单结构支持不足,无法进行无准备的开发。该包包含.yml示例。
- 响应结构必须严格匹配清单结构。否则,服务器将返回
发生意外错误
。