【问题标题】:Operation without entity无实体操作
【发布时间】:2020-11-08 23:52:15
【问题描述】:

我一直在寻找解决方案,但我找到的解决方案都没有真正让我做我想做的事。我只想创建不一定需要使用实体或 id 的路由。你能帮我吗?文档不清楚这样做。

先谢谢你。

【问题讨论】:

    标签: api-platform.com


    【解决方案1】:

    正如您在General Design Considerations 中看到的那样,只需创建一个普通的 PHP 类 (POPO)。像这样给它一个 ApiResource 注释:

     * @ApiResource(
     *     collectionOperations={
     *         "post"
     *     },
     *     itemOperations={}
     * )
    

    确保你的类所在的文件夹在 api/config/packages/api_platform.yaml 的路径列表中。通常有如下配置:

    api_platform:
        mapping:
            paths: ['%kernel.project_dir%/src/Entity']
    

    如果你的类不在实体文件夹中,你应该添加你的路径。

    Api Platform 将期望 json 被发布并尝试将其反序列化为您的类的实例。制作一个自定义DataPersister 来处理实例,例如如果你的类是 App\ApiCommand\Doit:

    namespace App\DataPersister;
    
    use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
    use App\ApiCommand\Doit;
    use App\ApiResult\DoitResult;
    
    final class DoitDataPersister implements ContextAwareDataPersisterInterface
    {
        public function supports($data, array $context = []): bool
        {
            return $data instanceof Doit;
        }
    
        public function persist($data, array $context = [])
        {
            // code to process $data
    
            $result = new DoitResult();
            $result->description = 'Hello world';
            return $result;
        }
    
        public function remove($data, array $context = [])
        {
          // will not be called if you have no delete operation
        }
    }
    

    如果需要Doctrine,请添加:

        public function __construct(ManagerRegistry $managerRegistry)
        {
            $this->managerRegistry = $managerRegistry;
        }
    

    使用方法见Injecting Extensions

    注意 ::persist 返回的结果不是 Doit 的实例。如果您返回 Doit api,平台将尝试将其序列化为您的操作结果。但是我们已经将 Doit 标记为 ApiResource,因此(?)api 平台会查找可以检索它的项操作,从而导致错误“No item route associated with the type App\ApiCommand\Doit”。为了避免这种情况,您可以返回 Symfonys 序列化程序可以序列化的任何不是 ApiResource 的对象。在示例中是 DoitResult 的一个实例。或者你可以返回一个 Symfony\Component\HttpFoundation\Response 的实例,但是你必须自己处理序列化。

    post 操作应该已经可以工作了,但是 swagger 文档是由元数据制作的。要告诉 api 平台它应该期望返回一个 DoitResult,请更改 @ApiResource 注释:

     *     collectionOperations={
     *         "post"={
     *             "output"=DoitResult::class
     *         }
     *     },
    

    这将在 swagger 文档中添加 DoitResult 的新类型,但描述仍然错误。您可以使用SwaggerDecorator 更正它们。这是一个 201 帖子回复:

    namespace App\Swagger;
    
    use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
    
    final class SwaggerDecorator implements NormalizerInterface
    {
        private $decorated;
    
        public function __construct(NormalizerInterface $decorated)
        {
            $this->decorated = $decorated;
        }
    
        public function normalize($object, string $format = null, array $context = [])
        {
            $summary = 'short explanation about DoitResult';
            $docs = $this->decorated->normalize($object, $format, $context);
            $docs['paths']['/doit']['post']['responses']['201']['description'] = 'Additional explanation about DoitResult';
    
            $responseContent = $docs['paths']['/doit']['post']['responses']['201']['content'];
            $this->setByRef($docs, $responseContent['application/ld+json']['schema']['properties']['hydra:member']['items']['$ref'],
                'description', $summary);
            $this->setByRef($docs, $responseContent['application/json']['schema']['items']['$ref'],
                'description', $summary);
    
            return $docs;
        }
    
        public function supportsNormalization($data, string $format = null)
        {
            return $this->decorated->supportsNormalization($data, $format);
        }
    
        private function setByRef(&$docs, $ref, $key, $value)
        {
            $pieces = explode('/', substr($ref, 2));
            $sub =& $docs;
            foreach ($pieces as $piece) {
                $sub =& $sub[$piece];
            }
            $sub[$key] = $value;
        }
    }
    

    要配置服务,请将以下内容添加到 api/config/services.yaml:

    'App\Swagger\SwaggerDecorator':
        decorates: 'api_platform.swagger.normalizer.api_gateway'
        arguments: [ '@App\Swagger\SwaggerDecorator.inner' ]
        autoconfigure: false
    

    如果您的发布操作实际上并未创建某些内容,您可能不喜欢 201 响应。您可以通过在 @ApiResource 注释中指定响应代码来更改它,例如:

     *     collectionOperations={
     *         "post"={
     *             "output"=DoitResult::class,
     *             "status"=200
     *         }
     *     },
    

    您可能需要相应地调整 SwaggerDecorator。

    创建“get”集合操作类似,但您需要创建一个DataProvider 而不是DataPersister。我的tutorial 的 chapter9-api 分支包含一个用于收集响应的 SwaggerDecorator 示例。

    【讨论】:

    • 我对@9​​87654327@ 的回答解释了如何使用“/getit/{foo}/{bar}”等非标准路径变量
    【解决方案2】:

    感谢您的回答。我有一些信息,但不是全部。我周末试试。

    【讨论】:

      猜你喜欢
      • 2018-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-01
      相关资源
      最近更新 更多