Symfony 7中VichUploaderBundle返回体不显示ContentUrl问题
问题:ApiPlatform + VichUploaderBundle 无法返回 contentUrl 字段
严格按照ApiPlatform官方文档配置VichUploaderBundle、MediaObject实体类及MediaObjectNormalizer序列化器后,在Swagger中执行文件上传(Post)和查询(Get)操作时,返回体始终无法显示ContentUrl字段。
相关配置与代码
1. VichUploader配置文件(config/packages/vich_uploader.yaml)
vich_uploader: db_driver: orm metadata: type: attribute mappings: media_object: uri_prefix: /media upload_destination: '%kernel.project_dir%/public/media' namer: Vich\UploaderBundle\Naming\OrignameNamer
2. MediaObject实体类(src/Entity/MediaObject.php)
<?php declare(strict_types=1); namespace App\Entity; use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Post; use ApiPlatform\OpenApi\Model; use App\Controller\CreateMediaObjectAction; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; use Vich\UploaderBundle\Mapping\Annotation as Vich; #[Vich\Uploadable] #[ORM\Entity] #[ApiResource( types: ['https://schema.org/MediaObject'], operations: [ new Get(), new GetCollection(), new Post( controller: CreateMediaObjectAction::class, openapi: new Model\Operation( requestBody: new Model\RequestBody( content: new \ArrayObject([ 'multipart/form-data' => [ 'schema' => [ 'type' => 'object', 'properties' => [ 'file' => [ 'type' => 'string', 'format' => 'binary' ] ] ] ] ]) ) ), validationContext: ['groups' => ['Default', 'media_object_create']], deserialize: false ) ], normalizationContext: ['groups' => ['media_object:read']] )] class MediaObject { #[ORM\Id, ORM\Column, ORM\GeneratedValue] private ?int $id = null; #[ApiProperty(types: ['https://schema.org/contentUrl'])] #[Groups(['media_object:read'])] public ?string $contentUrl = null; #[Vich\UploadableField(mapping: "media_object", fileNameProperty: "filePath")] #[Assert\NotNull(groups: ['media_object_create'])] public ?File $file = null; #[ORM\Column(nullable: true)] public ?string $filePath = null; public function getId(): ?int { return $this->id; } }
3. MediaObjectNormalizer序列化器(src/Serializer/MediaObjectNormalizer.php)
<?php declare(strict_types=1); namespace App\Serializer; use App\Entity\MediaObject; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Vich\UploaderBundle\Storage\StorageInterface; final class MediaObjectNormalizer implements NormalizerAwareInterface { use NormalizerAwareTrait; private const ALREADY_CALLED = 'MEDIA_OBJECT_NORMALIZER_ALREADY_CALLED'; public function __construct(private StorageInterface $storage) { } public function normalize($object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { $context[self::ALREADY_CALLED] = true; $object->contentUrl = $this->storage->resolveUri($object, 'file'); return $this->normalizer->normalize($object, $format, $context); } public function supportsNormalization($data, ?string $format = null, array $context = []): bool { if (isset($context[self::ALREADY_CALLED])) { return false; } return $data instanceof MediaObject; } }
排查解决步骤
1. 确认序列化器服务已注册
检查config/services.yaml,确保src/Serializer目录未被排除在自动注册规则之外:
services: App\: resource: '../src/' exclude: - '../src/DependencyInjection/' - '../src/Entity/' - '../src/Kernel.php' # 不要添加Serializer目录到排除列表
2. 验证resolveUri返回值
在MediaObjectNormalizer的normalize方法中添加日志,确认是否生成了正确的URL:
// 在$object->contentUrl赋值前添加 $uri = $this->storage->resolveUri($object, 'file'); error_log('Resolved media URI: ' . $uri); // 查看服务器日志确认值是否存在 $object->contentUrl = $uri;
3. 检查CreateMediaObjectAction控制器返回值
确保上传控制器返回的是MediaObject实例,且未覆盖序列化上下文:
public function __invoke(Request $request): MediaObject { $mediaObject = new MediaObject(); $form = $this->createForm(MediaObjectType::class, $mediaObject); $form->handleRequest($request); if ($form->isValid()) { $this->entityManager->persist($mediaObject); $this->entityManager->flush(); return $mediaObject; // 必须返回实体实例,ApiPlatform才会自动序列化 } throw new ValidationException($form); }
4. 提高序列化器优先级
若存在其他自定义序列化器,可能导致当前normalizer未被调用,在services.yaml中设置优先级:
App\Serializer\MediaObjectNormalizer: tags: - { name: serializer.normalizer, priority: 100 }
5. 清除缓存并验证静态文件访问
执行命令清除缓存,确保配置生效:
php bin/console cache:clear
同时确认/media目录可访问,上传的文件能通过/media/[文件名]正常打开。
内容的提问来源于stack exchange,提问作者Shamshir Isroilov
相关产品推荐
相关产品推荐

