I am creating an API with API platform. One of the features is to be able to upload and download files from a React client developped independently from my API
1 - First try
I followed the docs to setup VichUploaderBundle which led me to the exact same configuration as the docs (https://api-platform.com/docs/core/file-upload/)
From this, I can get my images by sending a GET request to the contentURL attribute set by my subscriber, which has the following format : "localhost/media/{fileName}" . However, I get a "CORS Missing allow origin" from my app when doing this.
2 - Second try
I fixed this by :
- removing the subscriber and the contentUrl attribute
- writing an itemOperation on the get method to serve my files directly through the "media_objects/{id}" route :
<?php
// api/src/Controller/GetMediaObjectAction.php
namespace App\Controller;
use App\Entity\MediaObject;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use App\Repository\MediaObjectRepository;
final class GetMediaObjectAction
{
    private $mediaObjectRepository;
    public function __construct(MediaObjectRepository $mediaObjectRepository)
    {
        $this->mediaObjectRepository = $mediaObjectRepository;
    }
    public function __invoke(Request $request): BinaryFileResponse
    {
        $id = $request->attributes->get('id');
        $filePath = $this->mediaObjectRepository->findOneById($id)->getFilePath();
        $file = "media/" . $filePath;
        return new BinaryFileResponse($file);
    }
}
EDIT : Here is my implementation of the MediaObject entity as requested
<?php
// api/src/Entity/MediaObject.php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
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;
/**
 * @ORM\Entity
 * @ApiResource(
 *     iri="http://schema.org/MediaObject",
 *     normalizationContext={
 *         "groups"={"media_object_read"}
 *     },
 *     collectionOperations={
 *         "post"={
 *             "controller"=CreateMediaObjectAction::class,
 *             "deserialize"=false,
 *             "validation_groups"={"Default", "media_object_create"},
 *             "openapi_context"={
 *                 "requestBody"={
 *                     "content"={
 *                         "multipart/form-data"={
 *                             "schema"={
 *                                 "type"="object",
 *                                 "properties"={
 *                                     "file"={
 *                                         "type"="string",
 *                                         "format"="binary"
 *                                     }
 *                                 }
 *                             }
 *                         }
 *                     }
 *                 }
 *             }
 *         },
 *         "get"
 *     },
 *     itemOperations={
 *         "get"
 *     }
 * )
 * @Vich\Uploadable
 */
class MediaObject
{
    /**
     * @var int|null
     *
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     * @ORM\Id
     */
    protected $id;
    /**
     * @var string|null
     *
     * @ApiProperty(iri="http://schema.org/contentUrl")
     * @Groups({"media_object_read"})
     */
    public $contentUrl;
    /**
     * @var File|null
     *
     * @Assert\NotNull(groups={"media_object_create"})
     * @Vich\UploadableField(mapping="media_object",fileNameProperty="filePath")
     */
    public $file;
    /**
     * @var string|null
     *
     * @ORM\Column(nullable=true)
     */
    public $filePath;
    public function getId(): ?int
    {
        return $this->id;
    }
}
END OF EDIT
Now I don't have this CORS problem anymore since API-platform is directly serving the file when responding to my "media_objects/{id}" route.
However, this brought some questions :
- Why did the CORS error pop in the first place ? I would guess it is because when performing a get request directly on the "public" folder, API-platform is not enforcing its CORS policy and not providing the required headers to the client
- Is it a correct practice to serve the files this way ? The fact that the documentation introduces a subscriber to create a contentUrl makes me wonder...
- Now that the server handles retrieving the file in the Action, does it make sense to have the files in the public folder ? Wouldn't it allow anyone to retrieve my files, and make enforcing security rules on them more difficult ?
Thank you in advance !
 
    