lezhnev74/openapi-psr7-validator

此包已被废弃且不再维护。作者建议使用league/openapi-psr7-validator包。

验证PSR-7消息是否符合OpenAPI(3.0.2)规范,规范以YAML或JSON格式表达

0.16 2019-08-23 08:58 UTC

README

Latest Stable Version Build Status License contributions welcome

注意 - 此包已贡献给PHP League

前往https://github.com/thephpleague/openapi-psr7-validator

此包仅面向现有用户。

OpenAPI PSR-7消息(HTTP请求/响应)验证器

此包可以验证PSR-7消息是否符合以YAML或JSON格式表达的OpenAPI(3.0.x)规范。

安装

composer require lezhnev74/openapi-psr7-validator

OpenAPI(OAS)术语

在包中使用了某些特定术语。这些术语来自OpenAPI

  • specification - 描述API的OpenAPI文档,以JSON或YAML文件表达
  • data - 实际验证规范的东西,包括正文和元数据
  • schema - 规范中描述请求/响应正文的 部分
  • keyword - 用于描述实例的属性称为关键字,或称为模式关键字
  • path - 到单个端点的相对路径
  • operation - 应用在路径上的方法(如 get /password
  • response - 描述的响应(包括状态码、内容类型等)

如何验证

ServerRequest消息

您可以通过这种方式验证\Psr\Http\Message\ServerRequestInterface实例

$yamlFile = "api.yaml";
$jsonFile = "api.json";

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getServerRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYaml(file_get_contents($yamlFile))->getServerRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJson(file_get_contents($jsonFile))->getServerRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJsonFile($jsonFile)->getServerRequestValidator();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getServerRequestValidator();

$match = $validator->validate($request);

结果您将获得一个OperationAddress $match,它匹配了给定的请求。如果您已经知道应该匹配您请求的操作(即您的项目中已有路由),则可以使用RouterRequestValidator

$address = new \OpenAPIValidation\PSR7\OperationAddress('/some/operation', 'post');

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getRoutedRequestValidator();

$validator->validate($address, $request);

这将大大简化验证并提高性能。

请求消息

您可以按这种方式验证\Psr\Http\Message\RequestInterface实例

$yamlFile = "api.yaml";
$jsonFile = "api.json";

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYaml(file_get_contents($yamlFile))->getRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJson(file_get_contents($jsonFile))->getRequestValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJsonFile($jsonFile)->getRequestValidator();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getRequestValidator();

$match = $validator->validate($request);

响应消息

\Psr\Http\Message\ResponseInterface的验证稍微复杂一些。因为您不仅需要YAML文件和响应本身,还需要知道这个响应属于哪个操作(在OpenAPI术语中)。

示例

$yamlFile = "api.yaml";
$jsonFile = "api.json";

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getResponseValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYaml(file_get_contents($yamlFile))->getResponseValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJson(file_get_contents($jsonFile))->getResponseValidator();
#or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromJsonFile($jsonFile)->getResponseValidator();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromSchema($schema)->getResponseValidator();

$operation = new \OpenAPIValidation\PSR7\OperationAddress('/password/gen', 'get') ;

$validator->validate($operation, $request);

验证后重用模式

\OpenAPIValidation\PSR7\ValidatorBuilder将模式读取和编译到内存中,作为\cebe\openapi\spec\OpenApi的实例。验证器使用此实例执行验证逻辑。您可以在验证后像这样重用此实例

$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getServerRequestValidator();
# or
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)->fromYamlFile($yamlFile)->getResponseValidator();

/** @var \cebe\openapi\spec\OpenApi */
$openApi = $validator->getSchema();

请求消息

\Psr\Http\Message\RequestInterface 验证尚未实现。

PSR-15 中间件

PSR-15 中间件可以使用如下方式:

$yamlFile = 'api.yaml';
$jsonFile = 'api.json';

$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYamlFile($yamlFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYaml(file_get_contents($yamlFile))->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJsonFile($jsonFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJson(file_get_contents($jsonFile))->getValidationMiddleware();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidationMiddlewareBuilder)->fromSchema($schema)->getValidationMiddleware();

SlimFramework 中间件

Slim 框架使用略有不同的中间件接口,因此这里有一个适配器,您可以像这样使用:

$yamlFile = 'api.yaml';
$jsonFile = 'api.json';

$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYamlFile($yamlFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromYaml(file_get_contents($yamlFile))->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJsonFile($jsonFile)->getValidationMiddleware();
#or
$psr15Middleware = (new \OpenAPIValidation\PSR15\ValidationMiddlewareBuilder)->fromJson(file_get_contents($jsonFile))->getValidationMiddleware();
#or
$schema = new \cebe\openapi\spec\OpenApi(); // generate schema object by hand
$validator = (new \OpenAPIValidation\PSR7\ValidationMiddlewareBuilder)->fromSchema($schema)->getValidationMiddleware();

$slimMiddleware = new \OpenAPIValidation\PSR15\SlimAdapter($psr15Middleware);

/** @var \Slim\App $app */
$app->add($slimMiddleware);

缓存层 / PSR-6 支持

PSR-7 验证器内置了一个缓存层(基于 PSR-6 接口),它可以在解析 OpenAPI 规范时节省时间。这是可选的。您可以通过将配置好的缓存池对象传递给静态构造函数来启用缓存,如下所示:

// Configure a PSR-6 Cache Pool
$cachePool = new ArrayCachePool();

// Pass it as a 2nd argument
$validator = (new \OpenAPIValidation\PSR7\ValidatorBuilder)
    ->fromYamlFile($yamlFile)
    ->setCache($cachePool)
    ->getResponseValidator();
# or
\OpenAPIValidation\PSR15\ValidationMiddleware::fromYamlFile($yamlFile, $cachePool);

您可以使用 ->setCache($pool, $ttl) 调用为 PSR-7 和 PSR-15 构建器设置适当的过期时间(以秒为单位或显式 null)。

如果您想控制模式项的缓存键,或者您的缓存不支持自动生成缓存键,您可以通过 ->overrideCacheKey('my_custom_key') 确保缓存使用您想要的键。

独立 OpenAPI 验证器

该软件包包含一个独立验证器,可以像这样验证任何数据与 OpenAPI 模式:

$spec = <<<SPEC
schema:
  type: string
  enum:
  - a
  - b
SPEC;
$data = "c";

$spec   = cebe\openapi\Reader::readFromYaml($spec);
# (optional) reference resolving
$spec->resolveReferences(new ReferenceContext($spec, "/"));
$schema = new cebe\openapi\spec\Schema($spec->schema);

try {
    (new \OpenAPIValidation\Schema\SchemaValidator())->validate($data, $schema);
} catch(\OpenAPIValidation\Schema\Exception\KeywordMismatch $e) {
    // you can evaluate failure details
    // $e->keyword() == "enum"
    // $e->data() == "c"
    // $e->dataBreadCrumb()->buildChain() -- only for nested data
}

自定义类型格式

如您所知,OpenAPI 允许您向类型添加格式

schema:
  type: string
  format: binary

此软件包包含一系列内置格式验证器

  • string 类型
    • 字节
    • 日期
    • 日期时间
    • 电子邮件
    • 主机名
    • IPv4
    • IPv6
    • URI
    • uuid (uuid4)
  • number 类型
    • 浮点数
    • 双精度浮点数

您还可以添加自己的格式。例如:

# A format validator must be a callable
# It must return bool value (true if format matched the data, false otherwise)

# A callable class:
$customFormat = new class()
{
    function __invoke($value): bool
    {
        return $value === "good value";
    }
};

# Or just a closure:
$customFormat = function ($value): bool {
    return $value === "good value";
};

# Register your callable like this before validating your data
\OpenAPIValidation\Schema\TypeFormats\FormatsContainer::registerFormat('string', 'custom', $customFormat);

异常

该软件包会抛出一系列异常,您可以捕获并处理。以下是一些异常:

  • 与模式相关的异常
    • \OpenAPIValidation\Schema\Exception\KeywordMismatch - 表示数据未与模式的关键字匹配
      • \OpenAPIValidation\Schema\Exception\TypeMismatch - 对 type 关键字的验证失败。例如,type:string 和值是 12
      • \OpenAPIValidation\Schema\Exception\FormatMismatch - 数据与给定的类型格式不匹配。例如,type: string, format: email 不会匹配 not-email
  • 与 PSR7 消息相关的异常
    • \OpenAPIValidation\PSR7\Exception\NoContentType - HTTP 消息(请求/响应)不包含 Content-Type 标头。一般 HTTP 错误。
    • \OpenAPIValidation\PSR7\Exception\NoPath - 路径未在规范中找到
    • \OpenAPIValidation\PSR7\Exception\NoOperation - 路径中未找到操作
    • \OpenAPIValidation\PSR7\Exception\NoResponseCode - 规范中未找到操作下的响应代码
    • 验证异常(检查父异常以获取可能的根本原因)
      • \OpenAPIValidation\PSR7\Exception\ValidationFailed - 失败的 PSR-7 消息的通用异常
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidBody - 主体与模式不匹配
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidCookies - 饼干与模式不匹配或缺少必需的饼干
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidHeaders - 标头与模式不匹配或缺少必需的标头
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidPath - 路径不匹配模式或模式值不匹配模式
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidQueryArgs - 查询参数与模式不匹配或缺少必需参数
      • \OpenAPIValidation\PSR7\Exception\Validation\InvalidSecurity - 请求与安全模式不匹配或安全头无效
    • 请求相关
      • \OpenAPIValidation\PSR7\Exception\MultipleOperationsMismatchForRequest - 请求与规范中的多个操作匹配,但所有验证均失败。

测试

您可以使用以下命令运行测试

vendor/bin/phpunit

贡献指南

欢迎提交问题或添加Pull request。此包遵循以下代码风格: doctrine/coding-standard

为了符合此风格,请使用此包附带的git钩子,位于.githooks/pre-commit

如何使用它

  1. 在本地克隆包并导航到该文件夹
  2. 创建一个指向钩子的符号链接,例如:ln -s -f ../../.githooks/pre-commit .git/hooks/pre-commit
  3. 添加执行权限:chmod +x .git/hooks/pre-commit
  4. 现在提交任何新更改,代码将被相应地检查和格式化。
  5. 如果代码有任何问题,请在此处检查日志:.phpcs-report.txt

鸣谢

人员

资源

  • 图标由Freepik制作,许可协议为CC 3.0 BY
  • cebe/php-openapi 包用于读取OpenAPI文件
  • slim3-psr15 包用于Slim中间件适配器

许可证

MIT许可证(MIT)。有关更多信息,请参阅License.md文件。

待办事项

  • 支持Discriminator Object(注意:显然,这并不简单,因为discriminator可以指向任何外部模式)