【问题标题】:Api-Platform: using PUT for creating resourcesApi-Platform:使用 PUT 创建资源
【发布时间】:2019-04-12 20:02:57
【问题描述】:

我想使用 PUT 方法来创建资源。它们由 UUID 标识,并且由于可以在客户端创建 UUID,因此我想启用以下行为:

  • 在 PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 上,如果此资源存在,请更新它
  • 在 PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 上,如果此资源不存在,则创建它

可以通过实现ItemDataProviderInterface / RestrictedDataProviderInterface 来实现。

但是,我的资源实际上是一个子资源,所以假设我想创建一个新的 Book 引用现有的 Author

我的构造函数如下所示:

/**
 * Book constructor
 */
public function __construct(Author $author, string $uuid) {
    $this->author = $author;
    $this->id = $uuid;
}

但我不知道如何从我的BookItemProvider 访问Author 实体(在请求正文中提供)。

有什么想法吗?

【问题讨论】:

  • 我可能离这里很远,我既不知道 api-platform.com 也不知道 ItemDataProviderInterface。但如果我理解正确,AuthorBook 有一对多的关系?在那种情况下,也许在AuthorType 中应该有书籍作为CollectionType 并包括'allow_add'' => true
  • 在快速查看了看起来很棒的 api-platform.com 之后,我相信我上面的答案完全是垃圾。请忽略它。
  • 我认为您需要查看 Api-Platform event system 并使用它自定义 PUT 请求。

标签: symfony api-platform.com


【解决方案1】:

在 API 平台中,创建项目时应该发生的许多事情都是基于请求的类型。改变会很复杂。

这里有 2 种可能性来制作您想要的东西。

首先,您可以考虑做一个自定义路由并使用您自己的逻辑。如果您这样做,您可能会很高兴知道在您的自定义路由上使用选项 _api_resource_class 将启用 APIPlaform 的一些侦听器并避免您做一些工作。

例如,如果您需要全局行为,则第二种解决方案是覆盖 API 平台。您的主要问题是 ApiPlatform 的 ReadListener 如果找不到您的资源,它将引发异常。此代码可能不起作用,但这里是如何覆盖此行为的想法:

class CustomReadListener
{
    private $decoratedListener;

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

    public function onKernelRequest(GetResponseEvent $event)
    {
        try {
            $this->decoratedListener->onKernelRequest($event);
        } catch (NotFoundHttpException $e) {
            // Don't forget to throw the exception if the http method isn't PUT 
            // else you're gonna break the 404 errors
            $request = $event->getRequest();

            if (Request::METHOD_PUT !== $request->getMethod()) {
                throw $e;
            }

            // 2 solutions here:

            // 1st is doing nothing except add the id inside request data
            // so the deserializer listener will be able to build your object


            // 2nd is to build the object, here is a possible implementation

            // The resource class is stored in this property
            $resourceClass = $request->attributes->get('_api_resource_class');

            // You may want to use a factory? Do your magic.
            $request->attributes->set('data', new $resourceClass());
        }
    }
}

您需要指定一个配置来将您的类声明为服务装饰器:

services:
    CustomReadListener:
        decorate: api_platform.listener.request.read
        arguments:
            - "@CustomReadListener.inner"

希望对您有所帮助。 :)

更多信息:

【讨论】:

  • 嘿,Nek,您的解决方案听起来很棒!我只是在为服务装饰而苦苦挣扎(以前从未使用过)。我有一个“服务“AppBundle\EventListener\ReadListener”依赖于不存在的服务 api_platform.listener.request.read.inner 异常(Symfony 4.0.6 w/flex + api-pack)。参数必须有一个@("@api_platform.listener.request.read.inner"),否则它是一个传递给构造函数的字符串。
  • 没关系,看起来decoration_inner_name 参数是强制性的。文档中不清楚。我继续。
  • 抱歉,我的服务定义有误。我编辑了它现在应该没问题。不要犹豫,参考我在帖子中链接的 Symfony 文档。
  • 嗨,Nek,感谢您的帮助,我最终设法实现了我想要的。用子资源很难做到这一点(不能从请求中自动水合作者实体),但我终于绕过了这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-13
  • 2017-09-06
  • 2019-06-20
  • 2019-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多