You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:07:00