微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 API 平台提供静态文件

如何解决使用 API 平台提供静态文件

我正在使用 API 平台创建 API。其中一项功能是能够从独立于我的 API 开发的 React 客户端上传和下载文件

1 - 第一次尝试

我按照文档设置了 VichUploaderBundle,这使我获得了与文档 (https://api-platform.com/docs/core/file-upload/) 完全相同的配置

由此,我可以通过向订阅者设置的 contentURL 属性发送 GET 请求来获取我的图像,该属性具有以下格式: "localhost/media/{fileName}" 。 但是,在执行此操作时,我从我的应用中收到了“CORS Missing allow origin”。

2 - 第二次尝试

我通过以下方式解决了这个问题:

<?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);
    }
}

编辑: 这是我按要求实现的 MediaObject 实体

<?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;
    }
}

编辑结束

现在我不再有这个 CORS 问题,因为 API 平台在响应我的“media_objects/{id}”路由时直接提供文件

然而,这带来了一些问题:

  • 为什么首先会弹出 CORS 错误?我猜这是因为当直接在“公共”文件夹上执行获取请求时,API 平台没有执行其 CORS 策略,也没有向客户端提供所需的标头
  • 以这种方式提供文件是否正确?文档介绍了一个订阅者来创建 contentUrl 的事实让我想知道......
  • 既然服务器处理在 Action 中检索文件,那么将文件放在公共文件夹中是否有意义?它不会允许任何人检索我的文件,并使对它们执行安全规则变得更加困难吗?

先谢谢你!

解决方法

为什么首先会弹出 CORS 错误?

因为 API 平台使用 Access-Control-Allow-Origin 文件中定义的 CORS_ALLOW_ORIGIN 值将 .env 标头添加到 HTTP 响应(使用 Nelmio Cors Bundle)。默认情况下,此值通常仅包含 localhostexample.com。您的 React 客户端发送的请求可能不是来自这两个主机中的任何一个,从而导致您的浏览器介入并引发错误。更多信息here

Nelmio Cors Bundle configuration documentation 解释了如何处理此错误。最简单的方法是在您的 CORS_ALLOW_ORIGIN=* 中设置 .env,并使您的 nelmio_cors.yaml 配置文件包括:

nelmio_cors:
    defaults:
        origin_regex: true
        allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']

您的自定义控制器返回的通用 BinaryFileResponse 实例不包含此标头(绕过所有 CORS 内容),从而使您的浏览器满意。

以这种方式提供文件是否正确?

我建议您遵循任何供应商文档提供的指南和最佳做法。包括这个。

将文件放在公共文件夹中有意义吗?它不会允许任何人检索我的文件,并使对它们执行安全规则变得更加困难吗?

后端公开公共媒体资产而不是数据库 blob 没有错。如有必要,Web 服务器非常有能力限制对这些资源的访问,PHP 也是如此。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。