Api Platform 2.7使用PUT方法为何会保存无效数据?
Symfony 6.2 + Api Platform 2.7:PUT请求验证报错但数据仍被保存的问题
问题描述
使用Symfony 6.2搭配Api Platform 2.7开发时遇到异常:
- 创建了带
@Assert验证规则的Test实体,字段text要求非空且长度在10-255字符之间。 - POST请求:提交
{"text": "a"}时,验证正常触发,返回错误提示,数据库不会创建新记录。 - PUT请求:提交相同数据时,虽返回和POST一致的错误提示,但无效数据
{"text": "a"}却被更新保存到了数据库中。
实体代码
<?php namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use App\Repository\TestRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: TestRepository::class)] #[ApiResource] class Test { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(length: 255)] #[Assert\NotBlank()] #[Assert\Length(min: 10, max: 255)] private ?string $text = null; public function getId(): ?int { return $this->id; } public function getText(): ?string { return $this->text; } public function setText(string $text): self { $this->text = $text; return $this; } }
POST请求正常响应示例
{ "@context": "/api/contexts/ConstraintViolationList", "@type": "ConstraintViolationList", "hydra:title": "An error occurred", "hydra:description": "text: This value is too short. It should have 10 characters or more.", "violations": [ { "propertyPath": "text", "message": "This value is too short. It should have 10 characters or more.", "code": "9ff3fdc4-b214-49db-8718-39c315e33d45" } ] }
问题原因
POST与PUT操作的核心差异在于:PUT操作会先将请求数据映射到已存在的实体对象(Doctrine会立即跟踪该实体的变更),即便后续验证失败,若Doctrine的Unit of Work未被正确重置,变更仍会在flush时被保存。而POST操作是创建新实体,验证失败时不会触发持久化流程。
解决方法
1. 显式配置PUT操作的验证上下文
在实体的#[ApiResource]注解中,为PUT操作明确指定验证组,确保验证规则在反序列化后立即生效:
use ApiPlatform\Metadata\Put; use ApiPlatform\Metadata\Post; #[ORM\Entity(repositoryClass: TestRepository::class)] #[ApiResource( operations: [ new Post(), new Put( validationContext: ['groups' => ['Default']], deserializeContext: ['groups' => ['Default']] ) ] )] class Test { // ... 实体原有代码 }
2. 开启Doctrine自动验证
确保config/packages/doctrine.yaml中开启ORM验证,让Doctrine在执行flush()前自动验证实体:
doctrine: orm: auto_mapping: true mappings: App: is_bundle: false dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App validation: enabled: true
3. 排查自定义组件
如果你自定义了DataPersister或Doctrine事件监听器/订阅者,检查代码逻辑是否在验证失败时仍执行了flush()操作,需确保验证失败时终止持久化流程。
4. 验证失败时重置Doctrine Unit of Work
添加事件订阅者,在验证失败时清除Doctrine跟踪的实体变更,避免无效数据被保存:
namespace App\EventSubscriber; use ApiPlatform\Core\EventListener\EventPriorities; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ViewEvent; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Doctrine\ORM\EntityManagerInterface; class ValidationErrorSubscriber implements EventSubscriberInterface { private $entityManager; public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } public static function getSubscribedEvents() { return [ ViewEvent::class => ['onView', EventPriorities::POST_VALIDATE], ]; } public function onView(ViewEvent $event) { $exception = $event->getThrowable(); if ($exception instanceof BadRequestHttpException) { $this->entityManager->clear(); } } }
内容的提问来源于stack exchange,提问作者Laszlooo
相关产品推荐
相关产品推荐

