search

Home  >  Q&A  >  body text

How to filter/sanitize/validate request parameters in Rest API operations for Symfony 5.4

I'm fairly new to Symfony 5.4 and recently created my first API using that version

For my specific API endpoint, one of the parameters is an array of IDs.

I need to validate the array via:

I implemented it in a simple way, checking the array before persisting the entity using typecasting and the existing Repository:

$parentPropertyIds = (array)$request->request->get('parent_property_ids');
if ($parentPropertyIds) {
   $parentCount = $doctrine->getRepository(Property::class)->countByIds($parentPropertyIds);

   if ($parentCount !== count($parentPropertyIds)) {
       return $this->json([
            'status'  => 'error',
            'message' => 'parent_property_id_invalid'
       ], 422);
   }

   foreach ($parentPropertyIds as $parentPropertyId) {
      $parentProperty = $doctrine->getRepository(Property::class)->find($parentPropertyId);
      $property->addParent($parentProperty);
   }
}

However, this makes my controller actions too "body positive" and feels like something that could be implemented in a more elegant way.

I can't find anything in the Symfony 5.4 documentation.

Currently I want to know if:

Full endpoint code:

/**
     * @Route("/property", name="property_new", methods={"POST"})
     */
    public function create(ManagerRegistry $doctrine, Request $request, ValidatorInterface $validator): Response
    {
        $entityManager = $doctrine->getManager();

        $property = new Property();
        $property->setName($request->request->get('name'));
        $property->setCanBeShared((bool)$request->request->get('can_be_shared'));

        $parentPropertyIds = (array)$request->request->get('parent_property_ids');
        if ($parentPropertyIds) {
            $parentCount = $doctrine
                ->getRepository(Property::class)
                ->countByIds($parentPropertyIds);

            if ($parentCount !== count($parentPropertyIds)) {
                return $this->json([
                    'status'  => 'error',
                    'message' => 'parent_property_id_invalid'
                ], 422);
            }

            foreach ($parentPropertyIds as $parentPropertyId) {
                $parentProperty = $doctrine->getRepository(Property::class)->find($parentPropertyId);
                $property->addParent($parentProperty);
            }
        }

        $errors = $validator->validate($property);

        if (count($errors) > 0) {
            $messages = [];
            foreach ($errors as $violation) {
                $messages[$violation->getPropertyPath()][] = $violation->getMessage();
            }
            return $this->json([
                'status'   => 'error',
                'messages' => $messages
            ], 422);
        }

        $entityManager->persist($property);
        $entityManager->flush();

        return $this->json([
            'status' => 'ok',
            'id'     => $property->getId()
        ]);
    }

P粉617237727P粉617237727331 days ago523

reply all(1)I'll reply

  • P粉635509719

    P粉6355097192023-12-19 16:22:56

    You can use Data Transfer Objects (DTOs) with Authentication services. There are many predefined constraints , or you can create a custom constraint. < /p>

    For example, how to use simple constraints as annotations:

    class PropertyDTO {
      /**
       * @Assert\NotBlank
       */
      public string $name = "";
      public bool $shared = false;
    }

    Then assign the data to the DTO:

    $propertyData = new PropertyDTO();
    $propertyData->name = $request->request->get('name');
    ...

    In some cases, it is better to define the constructor in the DTO and then request from and pass it to the DTO immediately:

    $data = $request->getContent(); // or $request->getArray(); depends on your content type
    $propertyData = new PropertyDTO($data);

    Then verify it:

    $errors = $validator->validate($propertyData);
    
    if (count($errors) > 0) {
        /*
         * Uses a __toString method on the $errors variable which is a
         * ConstraintViolationList object. This gives us a nice string
         * for debugging.
         */
        $errorsString = (string) $errors;
    
        return $this->json([
                    'status'  => 'error',
                    'message' => 'parent_property_id_invalid'
                ], 422);
    }
    
    //...

    reply
    0
  • Cancelreply