onmoon/openapi-server-bundle

一个用于从OpenAPI v3规范创建功能齐全的API服务器的Symfony扩展包


README

Mutation testing badge Test Coverage Type Coverage Latest Stable Version License

关于

此扩展包可以生成实现API时编写的大多数常见样板代码。代码是从OpenAPI规范生成的。

以下问题将由扩展包自动处理

  • 路由生成和路由
  • 根据规范验证传入的请求
  • 严格类型请求和响应对象以及API调用处理程序接口
  • 将请求对象传递给包含API调用处理逻辑的您的代码
  • 序列化返回的响应对象

您只需实现API调用处理程序接口并返回提供的响应对象即可。

安装

安装此扩展的首选方式是通过composer

运行

composer require cebe/php-openapi:dev-php-81-compat onmoon/openapi-server-bundle 

然后,将扩展包类添加到您的config/bundles.php

<?php

return [
    OnMoon\OpenApiServerBundle\OpenApiServerBundle::class => ['all' => true],
];

用法

您可以通过向您的/config/packages/open_api_server.yaml中添加以下参数来配置扩展包

open_api_server:
  #root_name_space: App\Generated # Namespace for DTOs and Api Interfaces
  ## The bundle will try to derive the paths for the generated files from the namespace. If you do not want them to be 
  ## stored in \App namespace or if you \App namespace is not in %kernel.project_dir%/src/, then you
  ## can specify this path manually:
  #root_path: %kernel.project_dir%/src/Generated 
  #language_level: 8.0.0 # minimum PHP version the generated code should be compatible with
  #generated_dir_permissions: 0755 # permissions for the generated directories
  #full_doc_blocks: false # whether to generate DocBlocks for typed variables and params  
  #send_nulls: false # return null values in responses if property is nullable and not required
  #skip_http_codes: [] # List of response codes ignored while parsing specification. 
  ## Can be any open api response code ( like 500, "5XX", "default"), or
  ## "5**", which will include both numeric (500) and XX ("5XX") codes.
  ## Might be useful if you want to generate error responses in event listener.
  specs:
    petstore:
      path: '../spec/petstore.yaml' # path to OpenApi specification
      type: yaml  # Specification format, either yaml or json. If omitted, the specification file extension will be used.
      name_space: PetStore # Namespace for generated DTOs and Interfaces
      media_type: 'application/json' # media type from the specification files to use for generating request and response DTOs
      #date_time_class: '\Carbon\CarbonImmutable' # FQCN which implements \DateTimeInterface.
      ## If set up, then generated DTOs will return instances of this class in DateTime parameters

使用标准的resource关键字和open_api类型将您的OpenApi规范添加到应用程序路由配置文件中

petstore-api:
  resource: 'petstore' # This should be same as in specs section of /config/packages/open_api_server.yaml
  type: open_api
  prefix: '/api' # Add this standard parameter to add base path to all paths in api
  name_prefix: 'petstore_' # This will add a prefix to route names 

OpenAPI架构的要求

为了使扩展包与您的规范正确工作,它们应该使用OpenAPI 3.0格式编写,并且每个操作都必须有一个唯一的operationId

目前也存在以下限制

  • number没有format时被视为浮点数
  • 路径和查询参数中仅允许使用标量类型
  • 在选择路由时,路径参数模式中的部分匹配模式被忽略,仅使用^...$模式
  • 如果路径参数中指定了模式,则忽略类型和格式生成的模式
  • 请求和响应体架构只能使用一个媒体类型。见:https://swagger.org.cn/docs/specification/media-types/

生成API服务器代码

有两个控制台命令与生成的API服务器代码一起使用

  • 生成服务器代码:php bin/console open-api:generate
  • 删除服务器代码:php bin/console open-api:delete

大多数时候您应该使用generate命令。它将清除扩展包缓存,如果存在,则删除旧的服务器代码,并生成新的代码。

小心使用generate和delete命令,它们将删除您在/config/packages/open_api_server.yaml文件中指定的root_path目录中的所有内容。该目录应不包含任何文件,除了此扩展包生成的代码,因为它将在每次生成API服务器代码时被删除。

对于规范中描述的每个操作,都会生成一个API调用处理程序接口,您应该实现它来处理API调用。

实现API调用处理程序接口

给定以下生成的API处理程序接口

<?php

declare (strict_types=1);

namespace App\Generated\Apis\PetStore\ShowPetById;

use OnMoon\OpenApiServerBundle\Interfaces\RequestHandler;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Request\ShowPetByIdRequestDto;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Response\ShowPetByIdResponse;

/**
 * This interface was automatically generated
 * You should not change it manually as it will be overwritten
 */
interface ShowPetById extends RequestHandler
{
    /** Info for a specific pet */
    public function showPetById(ShowPetByIdRequestDto $request) : ShowPetByIdResponse;
}

您的API调用处理程序可能看起来像这样

<?php

namespace App\Api;

use App\Repository\PetRepository;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Request\ShowPetByIdRequestDto;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Response\OK\ShowPetByIdResponseDto;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Response\ShowPetByIdResponse;
use App\Generated\Apis\PetStore\ShowPetById\ShowPetById;

class ShowPetByIdHandler implements ShowPetById
{
    private PetRepository $pets;

    public function __construct(PetRepository $pets)
    {
        $this->pets = $pets;
    }

    public function showPetById(ShowPetByIdRequestDto $request) : ShowPetByIdResponse
    {
        $petId = $request->getPathParameters()->getPetId();
        $pet   = $this->pets->getById($petId);

        return new ShowPetByIdResponseDto($pet->id(), $pet->name());
    }
}

此外,您的API调用处理程序可以实现以下接口

  • \OnMoon\OpenApiServerBundle\Interfaces\SetClientIp - 如果它需要客户端IP地址
  • \OnMoon\OpenApiServerBundle\Interfaces\SetRequest - 如果它需要Symfony请求对象
  • \OnMoon\OpenApiServerBundle\Interfaces\GetResponseCode - 如果它需要指定自定义HTTP响应代码

使用DTO映射器

如果您想将Doctrine实体或其他业务逻辑类作为API响应的源,您可以使用DTO映射器轻松地将内容复制到DTO中。

使用以下命令安装:

composer require onmoon/dto-mapper

使用方法如下:

public function showPetById(ShowPetByIdRequestDto $request) : ShowPetByIdResponseDto
{
    $petId = $request->getPathParameters()->getPetId();
    $pet   = $this->pets->getById($petId);

    /** @var OnMoon\DtoMapper\DtoMapper $this->mapper */
    return $this->mapper->map($pet, ShowPetByIdResponseDto::class);
}

更多信息

自定义API服务器行为

在请求处理生命周期中,API服务器会发出几个事件,这些事件可以用作替代内置的Symfony Kernel事件,因为前者提供了更多的上下文。这些事件允许您挂钩到API服务器功能并修改其行为。

以下事件可用:

  • OnMoon\OpenApiServerBundle\Event\Server\RequestEvent

    RequestEvent事件在请求在OpenAPI模式之前进行验证时发生。此事件允许您在执行验证和处理请求之前修改操作和请求对象。

  • OnMoon\OpenApiServerBundle\Event\Server\RequestDtoEvent

    RequestDtoEvent事件在请求内容在表示API请求的DTO对象中反序列化后发生,在将此对象传递给您的RequestHandler实现之前。此事件允许您在将其传递给您的RequestHandler实现之前通过反射修改操作和请求DTO(仅通过反射)。请注意,如果API端点不期望请求体、路径或查询参数,则不会创建ResponseDTO。

  • OnMoon\OpenApiServerBundle\Event\Server\ResponseDtoEvent

    ResponseDtoEvent事件在请求处理类执行并返回ResponseDto之后发生,在将此ResponseDto序列化为响应之前。此事件允许您在序列化之前修改ResponseDto内容。这可以用作在Symfony ResponseEvent中修改Response对象的替代方案,避免不必要的响应体json解码/编码。请注意,如果API端点没有响应体,则不会创建ResponseDTO。

  • OnMoon\OpenApiServerBundle\Event\Server\ResponseEvent

    ResponseEvent事件在API服务器将响应发送之前发生。此事件允许您在服务器将响应发送到客户端之前修改Response对象。

自定义API服务器代码生成

在API服务器代码生成过程中,代码生成器会发出几个事件,可以用来通过更改OpenAPI规范对象的某些部分或更改代表各种代码定义的对象(如类、属性、方法)来修改生成的代码。

以下事件可用:

  • OnMoon\OpenApiServerBundle\Event\CodeGenerator\ClassGraphReadyEvent

    ClassGraphReadyEvent事件在解析所有规范之后,构建要生成的类的图之后发生。

    此事件允许您修改:

    • 类名、命名空间和路径,
    • 属性属性、getter和setter,
    • 基础接口和类。
  • OnMoon\OpenApiServerBundle\Event\CodeGenerator\FilesReadyEvent

    FilesReadyEvent事件在所有类文件生成之后发生,在将它们写入文件之前。

    此事件允许您修改生成的文件内容,例如更改代码风格。