sajad torkamani

Suppose you need an endpoint that accepts the multipart/form-data request type so it can process an uploaded file. By default, Symfony can’t process multipart/form-data requests so you’ll get an error like Deserialization for the format \"multipart\" is not supported.

To handle this request type, you need to create a custom decoder and denormalizer.

Enable the multipart format globally

Edit config/packages/api_platform.yaml to include support for the multipart/form-data request type:

Create a decoder for multipart/form-data requests

<?php
// api/src/Encoder/MultipartDecoder.php

namespace App\Encoder;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\Encoder\DecoderInterface;

final class MultipartDecoder implements DecoderInterface
{
    public const FORMAT = 'multipart';

    public function __construct(private readonly RequestStack $requestStack)
    {
    }

    public function decode(string $data, string $format, array $context = []): ?array
    {
        $request = $this->requestStack->getCurrentRequest();

        if (!$request) {
            return null;
        }

        return array_map(static function (string $element) {
            // Multipart form values will be encoded in JSON.
            return json_decode($element, true, flags: \JSON_THROW_ON_ERROR);
        }, $request->request->all()) + $request->files->all();
    }

    public function supportsDecoding(string $format): bool
    {
        return self::FORMAT === $format;
    }
}

Create a file denormalizer

You want to ensure Symfony doesn’t try to denormalize the field containing the uploaded file otherwise you’ll get an error like Cannot denormalize object of type UploadedFile.

So create a denormalizer that’ll leave File fields as is:

<?php
// api/src/Serializer/UploadedFileDenormalizer.php

namespace App\Serializer;

use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

final class UploadedFileDenormalizer implements DenormalizerInterface
{
    public function denormalize($data, string $type, string $format = null, array $context = []): File
    {
        return $data;
    }

    public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
    {
        return $data instanceof File;
    }

    public function getSupportedTypes(?string $format): array
    {
        return [
            File::class => true,
        ];
    }
}

Send JSON-encoded fields in multipart/form-data request

When sending a multipart/form-data request, ensure the fields are encoded in JSON via the JSON.stringify() method. So you might do something like this:

async function uploadBook(file) {
    const bookMetadata = {
        title: "API Platform Best Practices",
        genre: "Programming",
    }

    const formData = new FormData()
    for (const [name, value] of Object.entries(bookMetadata)) {
        formData.append(name, JSON.stringify(value))
    }
    formData.append("file", file)

    const response = await fetch("https://my-api.com/books", {
        method: "POST",
        body: formData,
    });

    const result = await response.json()

    return result
}

Links