From 34c083ad0c02922950d9cf47a557f7ab2461ba54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Sat, 11 Feb 2023 13:05:18 +0800 Subject: [PATCH] Support validation for swagger. (#5395) --- CHANGELOG-3.0.md | 1 + src/swagger/composer.json | 5 +- .../src/Annotation/AnnotationTrait.php | 22 +--- src/swagger/src/Annotation/Get.php | 2 +- src/swagger/src/Annotation/Head.php | 2 +- src/swagger/src/Annotation/JsonContent.php | 19 +++ .../Annotation/MultipleAnnotationTrait.php | 48 ++++++++ src/swagger/src/Annotation/Patch.php | 2 +- src/swagger/src/Annotation/PathParameter.php | 2 +- src/swagger/src/Annotation/Post.php | 2 +- src/swagger/src/Annotation/Property.php | 109 ++++++++++++++++++ src/swagger/src/Annotation/Put.php | 2 +- src/swagger/src/Annotation/QueryParameter.php | 2 +- src/swagger/src/Annotation/RequestBody.php | 40 +++++++ .../src/Exception/RuntimeException.php | 16 +++ .../src/Listener/BootSwaggerListener.php | 27 +---- src/swagger/src/Request/RuleCollector.php | 57 +++++++++ src/swagger/src/Request/SwaggerRequest.php | 48 ++++++++ src/swagger/src/Util.php | 35 ++++++ 19 files changed, 390 insertions(+), 51 deletions(-) create mode 100644 src/swagger/src/Annotation/JsonContent.php create mode 100644 src/swagger/src/Annotation/MultipleAnnotationTrait.php create mode 100644 src/swagger/src/Annotation/Property.php create mode 100644 src/swagger/src/Annotation/RequestBody.php create mode 100644 src/swagger/src/Exception/RuntimeException.php create mode 100644 src/swagger/src/Request/RuleCollector.php create mode 100644 src/swagger/src/Request/SwaggerRequest.php create mode 100644 src/swagger/src/Util.php diff --git a/CHANGELOG-3.0.md b/CHANGELOG-3.0.md index cf720cef5..19ecfac93 100644 --- a/CHANGELOG-3.0.md +++ b/CHANGELOG-3.0.md @@ -11,6 +11,7 @@ - [#5376](https://github.com/hyperf/hyperf/pull/5376) Support coroutine server stats for `hyperf/metric`. - [#5379](https://github.com/hyperf/hyperf/pull/5379) Added log records when nacos heartbeat failed. - [#5389](https://github.com/hyperf/hyperf/pull/5389) Added swagger support. +- [#5395](https://github.com/hyperf/hyperf/pull/5395) Support validation for swagger. # v3.0.5 - 2023-02-05 diff --git a/src/swagger/composer.json b/src/swagger/composer.json index 5642cd1d4..7141fa48e 100644 --- a/src/swagger/composer.json +++ b/src/swagger/composer.json @@ -25,9 +25,8 @@ "Hyperf\\Swagger\\": "src/" } }, - "autoload-dev": { - "psr-4": { - } + "suggest": { + "hyperf/validation": "Required to use SwaggerRequest." }, "config": { "sort-packages": true diff --git a/src/swagger/src/Annotation/AnnotationTrait.php b/src/swagger/src/Annotation/AnnotationTrait.php index 0564e3d00..3c99a0954 100644 --- a/src/swagger/src/Annotation/AnnotationTrait.php +++ b/src/swagger/src/Annotation/AnnotationTrait.php @@ -12,37 +12,21 @@ declare(strict_types=1); namespace Hyperf\Swagger\Annotation; use Hyperf\Di\Annotation\AnnotationCollector; -use Hyperf\Di\Annotation\MultipleAnnotation; trait AnnotationTrait { public function collectClass(string $className): void { - $annotation = AnnotationCollector::getClassAnnotation($className, static::class); - - AnnotationCollector::collectClass($className, static::class, $this->formatAnnotation($annotation)); + AnnotationCollector::collectClass($className, static::class, $this); } public function collectMethod(string $className, ?string $target): void { - $annotation = AnnotationCollector::getClassMethodAnnotation($className, $target)[static::class] ?? null; - - AnnotationCollector::collectMethod($className, $target, static::class, $this->formatAnnotation($annotation)); + AnnotationCollector::collectMethod($className, $target, static::class, $this); } public function collectProperty(string $className, ?string $target): void { - $annotation = AnnotationCollector::getClassPropertyAnnotation($className, $target)[static::class] ?? null; - - AnnotationCollector::collectProperty($className, $target, static::class, $this->formatAnnotation($annotation)); - } - - protected function formatAnnotation(?MultipleAnnotation $annotation): MultipleAnnotation - { - if ($annotation instanceof MultipleAnnotation) { - return $annotation->insert($this); - } - - return new MultipleAnnotation($this); + AnnotationCollector::collectProperty($className, $target, static::class, $this); } } diff --git a/src/swagger/src/Annotation/Get.php b/src/swagger/src/Annotation/Get.php index ff74628a0..74fe4bb76 100644 --- a/src/swagger/src/Annotation/Get.php +++ b/src/swagger/src/Annotation/Get.php @@ -17,5 +17,5 @@ use Hyperf\Di\Annotation\AnnotationInterface; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class Get extends \OpenApi\Attributes\Get implements AnnotationInterface { - use AnnotationTrait; + use MultipleAnnotationTrait; } diff --git a/src/swagger/src/Annotation/Head.php b/src/swagger/src/Annotation/Head.php index 614610baf..ac5f60591 100644 --- a/src/swagger/src/Annotation/Head.php +++ b/src/swagger/src/Annotation/Head.php @@ -17,5 +17,5 @@ use Hyperf\Di\Annotation\AnnotationInterface; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class Head extends \OpenApi\Attributes\Head implements AnnotationInterface { - use AnnotationTrait; + use MultipleAnnotationTrait; } diff --git a/src/swagger/src/Annotation/JsonContent.php b/src/swagger/src/Annotation/JsonContent.php new file mode 100644 index 000000000..7073a754d --- /dev/null +++ b/src/swagger/src/Annotation/JsonContent.php @@ -0,0 +1,19 @@ +formatAnnotation($annotation)); + } + + public function collectMethod(string $className, ?string $target): void + { + $annotation = AnnotationCollector::getClassMethodAnnotation($className, $target)[static::class] ?? null; + + AnnotationCollector::collectMethod($className, $target, static::class, $this->formatAnnotation($annotation)); + } + + public function collectProperty(string $className, ?string $target): void + { + $annotation = AnnotationCollector::getClassPropertyAnnotation($className, $target)[static::class] ?? null; + + AnnotationCollector::collectProperty($className, $target, static::class, $this->formatAnnotation($annotation)); + } + + protected function formatAnnotation(?MultipleAnnotation $annotation): MultipleAnnotation + { + if ($annotation instanceof MultipleAnnotation) { + return $annotation->insert($this); + } + + return new MultipleAnnotation($this); + } +} diff --git a/src/swagger/src/Annotation/Patch.php b/src/swagger/src/Annotation/Patch.php index 5aedbcc04..bca244651 100644 --- a/src/swagger/src/Annotation/Patch.php +++ b/src/swagger/src/Annotation/Patch.php @@ -17,5 +17,5 @@ use Hyperf\Di\Annotation\AnnotationInterface; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class Patch extends \OpenApi\Attributes\Patch implements AnnotationInterface { - use AnnotationTrait; + use MultipleAnnotationTrait; } diff --git a/src/swagger/src/Annotation/PathParameter.php b/src/swagger/src/Annotation/PathParameter.php index 40e3a11b7..aa83af662 100644 --- a/src/swagger/src/Annotation/PathParameter.php +++ b/src/swagger/src/Annotation/PathParameter.php @@ -17,5 +17,5 @@ use Hyperf\Di\Annotation\AnnotationInterface; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER | Attribute::IS_REPEATABLE)] class PathParameter extends \OpenApi\Attributes\PathParameter implements AnnotationInterface { - use AnnotationTrait; + use MultipleAnnotationTrait; } diff --git a/src/swagger/src/Annotation/Post.php b/src/swagger/src/Annotation/Post.php index 1d9b54e74..7b476a879 100644 --- a/src/swagger/src/Annotation/Post.php +++ b/src/swagger/src/Annotation/Post.php @@ -17,5 +17,5 @@ use Hyperf\Di\Annotation\AnnotationInterface; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class Post extends \OpenApi\Attributes\Post implements AnnotationInterface { - use AnnotationTrait; + use MultipleAnnotationTrait; } diff --git a/src/swagger/src/Annotation/Property.php b/src/swagger/src/Annotation/Property.php new file mode 100644 index 000000000..2465d5d76 --- /dev/null +++ b/src/swagger/src/Annotation/Property.php @@ -0,0 +1,109 @@ +_content = $content; + } +} diff --git a/src/swagger/src/Exception/RuntimeException.php b/src/swagger/src/Exception/RuntimeException.php new file mode 100644 index 000000000..c3d1deed4 --- /dev/null +++ b/src/swagger/src/Exception/RuntimeException.php @@ -0,0 +1,16 @@ +findAnnotations($methodAnnotations, SA\Server::class); + $serverAnnotations = Util::findAnnotations($methodAnnotations, SA\Server::class); if (! $serverAnnotations) { - $serverAnnotations = $this->findAnnotations($classAnnotations, SA\Server::class); + $serverAnnotations = Util::findAnnotations($classAnnotations, SA\Server::class); } - $middlewareAnnotations = $this->findAnnotations($methodAnnotations, Middleware::class); - $middlewareAnnotations = array_merge($middlewareAnnotations, $this->findAnnotations($classAnnotations, Middleware::class)); + $middlewareAnnotations = Util::findAnnotations($methodAnnotations, Middleware::class); + $middlewareAnnotations = array_merge($middlewareAnnotations, Util::findAnnotations($classAnnotations, Middleware::class)); /** @var Operation $opera */ foreach ($annotation->toAnnotations() as $opera) { @@ -132,22 +133,4 @@ class BootSwaggerListener implements ListenerInterface } } } - - public function findAnnotations(?array $classAnnotations, string $class): array - { - $result = []; - foreach ((array) $classAnnotations as $annotation) { - if ($annotation instanceof $class) { - $result[] = $annotation; - } - - if ($annotation instanceof MultipleAnnotation) { - if ($annotation->className() === $class) { - $result = array_merge($result, $annotation->toAnnotations()); - } - } - } - - return $result; - } } diff --git a/src/swagger/src/Request/RuleCollector.php b/src/swagger/src/Request/RuleCollector.php new file mode 100644 index 000000000..4990914c6 --- /dev/null +++ b/src/swagger/src/Request/RuleCollector.php @@ -0,0 +1,57 @@ +rules) { + $rules[$parameter->name] = $parameter->rules; + } + } + + /** @var RequestBody $body */ + $body = Util::findAnnotations($methodAnnotations, RequestBody::class)[0] ?? null; + if ($body) { + if ($body->_content instanceof JsonContent) { + foreach ($body->_content->properties as $property) { + if ($property instanceof Property) { + if ($property->rules) { + $rules[$property->property] = $property->rules; + } + } + } + } + } + + return static::$rules[$class][$method] = $rules; + } +} diff --git a/src/swagger/src/Request/SwaggerRequest.php b/src/swagger/src/Request/SwaggerRequest.php new file mode 100644 index 000000000..c381211a7 --- /dev/null +++ b/src/swagger/src/Request/SwaggerRequest.php @@ -0,0 +1,48 @@ +getAttribute(Dispatched::class); + if (! $dispatched) { + throw new RuntimeException('The request is invalid.'); + } + + $callback = $dispatched->handler?->callback; + if (! $callback || ! is_array($callback)) { + throw new RuntimeException('The SwaggerRequest is only used by swagger annotations.'); + } + + return RuleCollector::get($callback[0], $callback[1]); + } +} diff --git a/src/swagger/src/Util.php b/src/swagger/src/Util.php new file mode 100644 index 000000000..820d2eee4 --- /dev/null +++ b/src/swagger/src/Util.php @@ -0,0 +1,35 @@ +className() === $class) { + $result = array_merge($result, $annotation->toAnnotations()); + } + } + } + + return $result; + } +}