jrm / request-bundle
使您轻松地将自己的请求作为控制器参数获取
0.3.2
2024-08-18 16:29 UTC
Requires
- php: ^8.2 || ^8.3
- ext-filter: *
- symfony/config: ^6.4 || ^7.0
- symfony/dependency-injection: ^6.4 || ^7.0
- symfony/http-foundation: ^6.4 || ^7.0
- symfony/http-kernel: ^6.4 || ^7.0
- symfony/property-access: ^6.4 || ^7.0
- symfony/property-info: ^6.4|^7.0
- symfony/serializer: ^6.4 || ^7.0
- symfony/translation-contracts: ^3.4
- symfony/type-info: ^6.4 || ^7.0
- symfony/validator: ^6.4 || ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.16
- maglnet/composer-require-checker: ^4.6
- phpstan/phpstan: ^1.10
- phpstan/phpstan-phpunit: ^1.3
- phpunit/phpunit: ^10.1
- rector/rector: ^0.15
- roave/security-advisories: dev-latest
- symfony/framework-bundle: ^6.4 || ^7.0
- symfony/translation: ^6.4 || ^7.0
This package is not auto-updated.
Last update: 2024-09-29 16:58:38 UTC
README
这是从 symfony 请求中提取数据的实现,用于特定控制器/操作的窄请求。
安装
- 在您的应用程序中需要此包
composer require jrm/request-bundle
- 在您的应用程序中启用此包
return [ # ... Jrm\RequestBundle\JrmRequestBundle::class => ['all' => true], # ... ];
用法
使用某些数据源创建请求
Body
(从请求体或表单中获取数据)Collection
(用于填充某些子对象的集合)Cookie
(从cookie中获取数据)EmbeddableRequest
(用于填充某些子对象)File
(从文件中获取数据)Header
(从头部中获取数据)PathAttribute
(从路径属性中获取数据)Query
(从查询字符串中获取数据)
请求
use Jrm\RequestBundle\Model\Source; use Jrm\RequestBundle\Attribute\Collection; use Jrm\RequestBundle\Attribute\Header; use Jrm\RequestBundle\Attribute\PathAttribute; final class MyRequest { public function __construct( #[PathAttribute()] public readonly int $id, #[Body('pos_id')] private readonly string $posId, #[Header('Content-Type')] public readonly string $contentType, #[Assert\Valid] #[Collection( type: ProductItem::class, source: Source::BODY, path: 'products', )] public readonly array $products, ) { } }
控制器
此示例使用可调用的控制器,但您也可以与常规控制器一起使用。
use Jrm\RequestBundle\MapRequest; #[Route( '/do-something/{id}', name: 'app.do.something', methods: [Request::METHOD_POST], )] final class MyAction { public function __invoke(#[MapRequest] MyRequest $request): JsonResponse { //do something return new JsonResponse(null); } }
嵌套字段
您的数据,例如请求体,可能存在某些嵌套。
{ "request": { "some_field": "some_value", "next_field": "next_value" } }
您可以传递此字段的路径。
use Jrm\RequestBundle\Attribute\Body; final class MyRequest { public function __construct( #[Body('request.some_field')] public readonly string $someField, #[Body('request.next_field')] public readonly string $nextField, ) { } }
验证
您可以使用 symfony 约束来验证您的请求,如果验证失败,Jrm\RequestBundle\Listener\RequestValidationFailedExceptionListener 将发送包含所有失败字段和错误信息的响应
use Jrm\RequestBundle\Attribute\Query; use Symfony\Component\Validator\Constraints as Assert; final class MyRequest { public function __construct( #[Assert\NotBlank] #[Query('some_field')] public readonly string $field, ) { } }
{ "message": "Validation failed.", "errors": [ { "code": "48b70abd-a021-4ce7-9662-616cd58eeaee", "message": "This value should not be blank.", "parameters": [], "property_path": "some_field" } ] }
集合
在某些情况下,您可能需要填充数据的集合,您可以使用 Collection 属性并“描述”此集合项作为单独的对象。
use Jrm\RequestBundle\Attribute\Internal\Item; use Symfony\Component\Validator\Constraints as Assert; final class MyCollectionItem { public function __construct( #[Assert\Uuid] #[Item()] public readonly string $id, ) { } }
use Jrm\RequestBundle\Model\Source; use Jrm\RequestBundle\Attribute\Collection; use Symfony\Component\Validator\Constraints as Assert; final class MyRequest { public function __construct( #[Assert\Valid] #[Collection( type: MyCollectionItem::class, source: Source::BODY, path: 'sub_ids', )] public readonly array $items, ) { } }
注意: 您应该使用 #[Assert\Valid] 对您的集合进行验证,如上例所示,因为如果没有此约束,symfony 验证器将忽略它
自定义解析器
您可以创建自定义解析器来定义获取请求数据的新方式
为此,您需要创建
参数
use Jrm\RequestBundle\Attribute\RequestAttribute; #[Attribute(Attribute::TARGET_PARAMETER, Attribute::TARGET_PROPERTY)] final class UserId implements RequestAttribute { /** * @return class-string<UserIdResolver> */ public function resolvedBy(): string { return UserIdResolver::class; } }
参数解析器
use App\Domain\User\Exception\UserNotAuthorizedException; use App\Domain\User\Model\User; use Jrm\RequestBundle\Exception\UnexpectedAttributeException; use Jrm\RequestBundle\Model\Metadata; use Jrm\RequestBundle\Attribute\RequestAttribute; use Jrm\RequestBundle\Attribute\ValueResolver; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; final class UserIdResolver implements ValueResolver { public function __construct( private readonly TokenStorageInterface $tokenStorage, ) { } public function resolve( Request $request, Metadata $metadata, RequestAttribute $attribute, ): int { if (!$attribute instanceof UserId) { throw new UnexpectedAttributeException(UserId::class, $attribute::class); } try { $user = $this->tokenStorage->getToken()?->getUser(); if ($user === null) { throw new UserNotAuthorizedException(); } return $user->id(); } catch (Throwable $throwable) { if ($parameter->isOptional()) { return $parameter->defaultValue(); } throw $throwable; } } }
计划
- 实现自动化转换到 Open Api 文档
- 将
Item
属性设置为可选 - 添加验证测试,确保所有请求都是具有支持属性和类型的有效类
- 修复当您的请求没有任何必需参数时的验证问题
- 将包添加到 symfony flex
- 添加更多单元和集成测试