- Published on
Api platform 教程11---自定义操作
- Authors
-
-
- Name
- langziyang
-
Api platform 教程11---自定义操作
我们的代码越来越有趣了,一个完整而又简单的的CRUD一点问题都没有了,可是现实中我们肯定还有别的要求,比如上传文件、以及在 除了默认端点以外再写一个自己的端点等等。
在普通的程序中我们上传文件可能是先把文件放在input中,然后提交表单处理,可是在常见的API中,我们可能会自动上传图片, 然后再把返回的图片地址放在数据中提交。
如果我们去看api platform文档,它的方法和我接下来的方式可能会有差别。
在api platform3.3以前,官方建议写一个Controller来处理,但无论什么时候,自定义Controller都是不建议的,你应该首先想到 Processor和Provider来处理你的流程。
另一个问题又来了:上传文件我们是一个在依附于任何一个Entity的操作,所以我应该写在哪一个Entity上呢?Api platform也 可以公开一个非实体的文件,
#src/ApiResource/Common.php
<?php
namespace App\ApiResource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\State\Common\Processor\FileUploadProcessor;
#[ApiResource(
new Post('/common/open/upload', formats: ['multipart' => 'multipart/form-data'], inputFormats: ['multipart' => 'multipart/form-data'], processor: FileUploadProcessor::class,)
)]
class Common
{
}
以下是上传文件及返回信息
#src/State/Common/Processor/FileUploadProcessor.php
<?php
namespace App\State\Common\Processor;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
class FileUploadProcessor implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
{
$path = '';//上传文件的逻辑,这里不再详细写出来
return new JsonResponse([
'path' => $path
]);
}
}
为了查找所有 API 资源类,API Platform 仅扫描两个目录来查找此属性: src/Entity/ 和 src/ApiResource 。不过,这可以在 /config/packages/api_platform.yaml 中使用映射路径配置进行调整。
注意,在api platform3.3以前文件上传使用Controller需要:
api_platform:
use_symfony_listeners: true
从3.3开始,我们建议使用处理器,请注意,您需要全局启用多部分格式:
api_platform:
use_symfony_listeners: false
在POST中的第一个参数:uriTemplate就是API路由端点,顺便提一句:你可能会觉得把所有代码写在Entity文件里太凌乱,其实你也可以 把ApiResource写在类似于Common这样的文件里,我们先修改一下Common文件,仅仅做为演示:
#[ApiResource(
shortName: 'test',
stateOptions: new Options(entityClass: User::class)
)]
然后刷新API文档,测试一下每一个端点。是不是很有意思?但是在我的开发经验中,这种方式我还并不是很熟练以及我并没觉得它比我们前面 的方式更好。如果你仔细研究后觉得这是更好的方式,请告诉我们。
接下来我们再为user自定义一个Get操作
#[ApiResource(
operations: [
new GetCollection(provider: UserProvider::class),
new Get(provider: UserProvider::class),
new Post(processor: UserProcessor::class),
new Patch(processor: UserProcessor::class),
new Delete(),
new Get('/users/test',provider: UserTestProvider::class)
],
)]
<?php
namespace App\State\User\Provider;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
class UserTestProvider implements ProviderInterface
{
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
return new JsonResponse(['done']);
}
}
当我们访问/api/users/test时,你可能会出现两种错误:
- "assert($user instanceof User)",
- 404
404很好理解,就是没有找到路由,
第一个错误我们通过控制台看到是/src/State/User/Provider/UserProvider.php的错误,我们去修改一下:
if ($user instanceof User){
$user->setEmail('123@qq.com');
}
修改后还是会返回404, 其实这是因为有一个默认的Get接口,路由为/api/users/{id},和我们这个接口有冲突,这是symfony的 原因,与Api platform无关,解决办法如下:
#[ApiResource(
operations: [
new GetCollection(provider: UserProvider::class),
new Get(requirements: ['id' => '\d+'], provider: UserProvider::class),
new Post(processor: UserProcessor::class),
new Patch(processor: UserProcessor::class),
new Delete(),
new Get('/users/test', provider: UserTestProvider::class)
],
)]
注意:默认情况下,API Platform 使用定义的第一个 Get 操作来生成项目的 IRI,并使用第一个 GetCollection 操作来生成集合的 IRI。
所以,当你有多个Get或者GetCollection时,如果返回资源中的@id有问题,请先试一试修改顺序