Symfony + API Platform: Is there a way to throw an error when POST data has unexpected (eg. mistyped) properties?

Viewed 649

Say I have an API resource named "Foo" with property "bar" and I make the POST operation available to create a new instance:

POST /api/foo
{
   "bar": "whatever"
}

If the user of my API mis-types the property like so:

POST /api/foo
{
   "bat": "whatever"
}

There is no error.

I could add a NotNull constraint on the property "bar" so that if "bar" was not included, then an error would be thrown, but what to do if "bar" is nullable? Right now the result will be that "bar" becomes NULL and that is not what the end user expected.

Alternately, if any additional properties are added, there is no error or warning thrown, for example if "publish" is not a property but the following is submitted:

POST /api/foo
{
   "bar": "whatever",
   "publish": true
}

There is no error or warning about the unexpected "publish" property. The end user might expect this property to be available, possibly because it exists on other resources, but there is no error in the response. Obviously the end user should be reading documentation, but I would prefer giving feedback directly in the response to ensure 100% compliance.

In summary: If users of my API add erroneous properties, or accidentally mistype property names, how can I detect this and throw an error?

2 Answers

You can use @Groups annotation with denormalization_context. So you declare the properties who's granted to be updated. And using strong type-hint

<?php

declare(strict_types=1);

namespace AppBundle\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * Class Foo
 * @package AppBundle\Entity
 * @ORM\Entity()
 * @ORM\Table()
 * @ApiResource(
 *  attributes={
 *      "normalization_context"={"groups"={"get"}},
 *      "denormalization_context"={"groups"={"put, "post"}}
 *  },
 *  itemOperations={},
 *  collectionOperations={"post"}
 * ) 
 */
class Foo
{
    /**
     * @var string 
     * @ORM\Column(name="bar", type="string", nullable=false) 
     * @Groups({"get", "post"}) 
     */
    private string $bar;

    /**
     * @var string
     * @ORM\Column(name="bar", type="string", nullable=false)
     * @Groups({"get"}) 
     */
    private string $bat;

    /**
     * @var bool 
     * @ORM\Column(name="public", type="boolean", nullable=false)
     * @Groups({"get", "post"})
     */
    private bool $public;

    /**
     * @return string
     */
    public function getBar(): string
    {
        return $this->bar;
    }

    /**
     * @param string $bar
     *
     * @return Foo
     */
    public function setBar(string $bar): Foo
    {
        $this->bar = $bar;
        return $this;
    }

    /**
     * @return string
     */
    public function getBat(): string
    {
        return $this->bat;
    }

    /**
     * @param string $bat
     *
     * @return Foo
     */
    public function setBat(string $bat): Foo
    {
        $this->bat = $bat;
        return $this;
    }

    /**
     * @return bool
     */
    public function isPublic(): bool
    {
        return $this->public;
    }

    /**
     * @param bool $public
     *
     * @return Foo
     */
    public function setPublic(bool $public): Foo
    {
        $this->public = $public;
        return $this;
    }
}

You can see that property $bat don't have group annotation "post": so this field is not authorized in your POST request. You can add too some Constraints: for example :

use Symfony\Component\Validator\Constraint as Assert;

//...
    /**
     * @var string
     * @ORM\Column(name="bar", type="string", nullable=false)
     * @Groups({"get", "post"})
     * @Assert\NotBlank(message="property bar is mandatory")
     */
    private string $bar;

I hope it will be helpfull.

Related