Skip to content

Webapi: nullable nested objects does not support null #40374

@stollr

Description

@stollr

Preconditions and environment

  • Magento version 2.4.6-p12

Steps to reproduce

I have a webapi service that handles an object as input, which has a nullable property which is another object. When null is provided as value for the nested object, I get an error.

Here are the files needed to reproduce the issue:

namespace Stollr\CrmApi\Api\Data;

interface TestTypeInterface
{
    /**
     * @return string
     */
    public function getName(): string;

    /**
     * @param string $name
     * @return void
     */
    public function setName(string $name): void;

    /**
     * @return \Stollr\CrmApi\Api\Data\TestTypeNestedInterface|null
     */
    public function getNestedType(): ?TestTypeNestedInterface;

    /**
     * @param \Stollr\CrmApi\Api\Data\TestTypeNestedInterface|null $nestedType
     * @return void
     */
    public function setNestedType(?TestTypeNestedInterface $nestedType): void;
}
namespace Stollr\CrmApi\Api\Data;

interface TestTypeNestedInterface
{
    /**
     * @return string
     */
    public function getName(): string;

    /**
     * @param string $name
     * @return void
     */
    public function setName(string $name): void;
}
namespace Stollr\CrmApi\Api\Webapi;

use Stollr\CrmApi\Api\Data\TestTypeInterface;

interface TestNestedNullableWebapiInterface
{
    /**
     * @param \Stollr\CrmApi\Api\Data\TestTypeInterface $type
     * @return \Stollr\CrmApi\Api\Data\TestTypeInterface
     */
    public function save(TestTypeInterface $type): TestTypeInterface;
}
namespace Stollr\CrmApi\Model\Data;

use Stollr\CrmApi\Api\Data\TestTypeInterface;
use Stollr\CrmApi\Api\Data\TestTypeNestedInterface;

class TestType implements TestTypeInterface
{
    /**
     * @param string $name
     * @param \Stollr\CrmApi\Api\Data\TestTypeNestedInterface|null $nestedType
     */
    public function __construct(
        private string $name,
        private ?TestTypeNestedInterface $nestedType = null,
    ) {
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }

    public function getNestedType(): ?TestTypeNestedInterface
    {
        return $this->nestedType;
    }

    public function setNestedType(?TestTypeNestedInterface $nestedType): void
    {
        $this->nestedType = $nestedType;
    }
}
namespace Stollr\CrmApi\Model\Data;

use Stollr\CrmApi\Api\Data\TestTypeNestedInterface;

class TestTypeNested implements TestTypeNestedInterface
{
    /**
     * @param string $name
     */
    public function __construct(private string $name) {}

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}
namespace Stollr\CrmApi\Webapi;

use Stollr\CrmApi\Api\Data\TestTypeInterface;
use Stollr\CrmApi\Api\Webapi\TestNestedNullableWebapiInterface;

class TestNestedNullableWebapi implements TestNestedNullableWebapiInterface
{
    public function save(TestTypeInterface $type): TestTypeInterface
    {
        return $type;
    }
}
<!-- etc/di.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Stollr\CrmApi\Api\Data\TestTypeInterface" type="Stollr\CrmApi\Model\Data\TestType"/>
    <preference for="Stollr\CrmApi\Api\Data\TestTypeNestedInterface" type="Stollr\CrmApi\Model\Data\TestTypeNested"/>
    <preference for="Stollr\CrmApi\Api\Webapi\TestNestedNullableWebapiInterface" type="Stollr\CrmApi\Webapi\TestNestedNullableWebapi"/>
</config>
<!-- etc/webapi.xml -->
<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
    <route url="/V1/test-nested-nullable" method="POST">
        <service class="Stollr\CrmApi\Api\Webapi\TestNestedNullableWebapiInterface" method="save"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

The important thing is that in TestType the nestedType property is nullable.

Now, I call the API service with POST https://localhost/rest/V1/test-nested-nullable and the following request body:

{
  "type": {
    "name": "test",
    "nested_type": null
  }
}

Expected result

I get a response with the status code 200 and the json object.

Actual result

In fact I get a 400 error with the following content:

{
"message": "Type Error occurred when creating object: %type, %msg",
"parameters": {
"type": "Stollr\CrmApi\Model\Data\TestTypeNested",
"msg": "Stollr\CrmApi\Model\Data\TestTypeNested::__construct(): Argument #1 ($name) must be of type string, null given, called in /var/www/html/magento/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php on line 121"
},
"trace": "#0 /var/www/html/magento/vendor/magento/framework/ObjectManager/Factory/Compiled.php(108): Magento\Framework\ObjectManager\Factory\AbstractFactory->createObject('Stollr\\CrmApi\\...', Array)\n#1 /var/www/html/magento/vendor/magento/framework/ObjectManager/ObjectManager.php(56): Magento\Framework\ObjectManager\Factory\Compiled->create('Stollr\\CrmApi\\...', Array)\n#2 /var/www/html/magento/vendor/magento/framework/Webapi/ServiceInputProcessor.php(298): Magento\Framework\ObjectManager\ObjectManager->create('\\Stollr\\CrmApi...', Array)\n#3 /var/www/html/magento/vendor/magento/framework/Webapi/ServiceInputProcessor.php(537): Magento\Framework\Webapi\ServiceInputProcessor->_createFromArray('\\Stollr\\CrmApi...', Array)\n#4 /var/www/html/magento/vendor/magento/framework/Webapi/ServiceInputProcessor.php(329): Magento\Framework\Webapi\ServiceInputProcessor->convertValue(NULL, '\\Stollr\\CrmApi...')\n#5 /var/www/html/magento/vendor/magento/framework/Webapi/ServiceInputProcessor.php(537): Magento\Framework\Webapi\ServiceInputProcessor->_createFromArray('Stollr\\CrmApi\\...', Array)\n#6 /var/www/html/magento/vendor/magento/framework/Webapi/ServiceInputProcessor.php(200): Magento\Framework\Webapi\ServiceInputProcessor->convertValue(Array, 'Stollr\\CrmApi\\...')
[...]
}

Additional information

I think the relevant code for this issue is in Magento\Framework\Webapi\ServiceInputProcessor.
In this line a check could be added, and if $value === null then $setterValue should be set to null.

Trying to convert null to anything else does not make sense.

I hope this helps.

Release note

No response

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue: ready for confirmationReported on 2.4.6-p12Indicates original Magento version for the Issue report.Triage: Dev.ExperienceIssue related to Developer Experience and needs help with Triage to Confirm or Reject it

    Type

    No type

    Projects

    Status

    Ready for Confirmation

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions