如何解决使用 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)。默认情况下,此值通常仅包含 localhost
和 example.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 举报,一经查实,本站将立刻删除。