• 50 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 27/02/2019

Validez des ressources

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous avons vu comment créer un point d'entrée pour créer une nouvelle ressource. Rappelez-vous, nous avons une action dans la classe  ArticleController : 

<?php

namespace AppBundle\Controller;

// …
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\Controller\Annotations as Rest;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;

class ArticleController extends Controller
{
    /**
     * @Rest\Post("/articles")
     * @Rest\View(StatusCode = 201)
     * @ParamConverter("article", converter="fos_rest.request_body")
     */
    public function createAction(Article $article)
    {
        $em = $this->getDoctrine()->getManager();

        $em->persist($article);
        $em->flush();

        return $article;
    }
}

 Grâce au  ParamConverter du FOSRestBundle, le contenu envoyé par le client dans la requête (JSON ou XML) est converti en objet. Nous sommes ensuite en mesure de manipuler l'objet converti.

Il manque néanmoins une étape extrêmement importante : la validation des informations envoyées par le client avant de persister notre objet. :waw:

Pour ce faire, il nous suffit d'ajouter la configuration de validation nécessaire pour chacun des champs de notre objet, comme nous avons l'habitude de le faire, puis de faire appel au service  validator  qui nous permet d'effectuer la validation.

Contraintes de validation sur la ressource article

Commençons donc par ajouter les contraintes de validation pour chacun des champs de la ressource :

<?php

namespace AppBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
 * @ORM\Table()
 *
 * @ExclusionPolicy("all")
 *
 */
class Article
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     *
     * @Expose
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     * @Expose
     * @Assert\NotBlank
     */
    private $title;

    /**
     * @ORM\Column(type="text")
     * @Expose
     * @Assert\NotBlank
     */
    private $content;
    
    // …
}

Mettons à jour l'actioncreateActionafin de faire appel au service  validator :

<?php

namespace AppBundle\Controller;

// …
use Symfony\Component\HttpFoundation\Response;

class ArticleController extends FOSRestController
{
    /**
     * @Rest\Post("/articles")
     * @Rest\View(StatusCode = 201)
     * @ParamConverter("article", converter="fos_rest.request_body")
     */
    public function createAction(Article $article)
    {
        $errors = $this->get('validator')->validate($article);

        if (count($errors)) {
            return $this->view($errors, Response::HTTP_BAD_REQUEST);
        }

        $em = $this->getDoctrine()->getManager();

        $em->persist($article);
        $em->flush();

        return $article;
    }
}

Nous faisons donc appel au servicevalidatorpour valider l'objet désérialisé que nous obtenons via le body converter. Il faut ensuite récupérer les erreurs suite à la validation. La méthode  validate renvoie un objet de type Symfony\Component\Validator\ConstraintViolationList. Il suffit de gérer le cas où cette liste contient des éléments ou non. Si c'est le cas, nous renvoyons une réponse HTTP ayant pour code status 400 (bad request), avec la liste des erreurs retournées par le validator. Sinon, la création de l'article peut suivre son cours.

Parfait ! Mais FOSRestBundle propose de se charger de tout cela avant que l'objet converti ne soit envoyé à l'action. Voyons comment refactorer notre code pour en bénéficier.

Utiliser le mécanisme de validation de ressource du FOSRestBundle

Configuration

Dans un premier temps il faut activer la validation avec FOSRestBundle :

# app/config/config.yml

fos_rest:
    # …
    body_converter:
        enabled: true
        validate: true
        validation_errors_argument: violations

Nous connaissons déjà la ligne 6 : en effet, elle nous sert à activer le body converter. Deux nouvelles lignes sont apparues :

  •  validate: true : permet de faire que la validation de l'objet converti soit déclenchée juste avant qu'il soit passé à l'action ;

  •  validation_errors_argument: violations : il s'agit du nom de l'argument contenant un objet de type  ConstraintViolationList, contenant toutes les erreurs résultantes de la validation de l'objet.

Validation

Il nous suffit de mettre à jour le code de notre action afin de bénéficier de la validation automatique des objets convertis :

<?php

namespace AppBundle\Controller;

// …
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\HttpFoundation\Response;

class ArticleController extends FOSRestController
{
    /**
     * @Rest\Post("/articles")
     * @Rest\View(StatusCode = 201)
     * @ParamConverter("article", converter="fos_rest.request_body")
     */
    public function createAction(Article $article, ConstraintViolationList $violations)
    {
        if (count($violations)) {
            return $this->view($violations, Response::HTTP_BAD_REQUEST);
        }

        $em = $this->getDoctrine()->getManager();

        $em->persist($article);
        $em->flush();

        return $article;
    }
}

Un nouvel argument a fait son apparition dans l'action : l'objet $violations de type  ConstraintViolationList.

Il suffit ensuite de voir si la liste contient au moins un élément. Si c'est le cas, nous avons choisi de renvoyer l'objet  ConstraintViolationList, en prenant soin de faire que la réponse soit bien une 400 (bad request). Nous avons besoin de changer le code status de la réponse, c'est pour cela nous ne renvoyons pas uniquement l'objet sérialisé : nous utilisons la méthode  view  disponible dans le controller FOSRestController que nous étendons dans la classe  ArticleController .

Résultat de la désérialisation avec un objet JSON qui ne respecte pas les contraintes de validation de l'objet Article
Résultat de la désérialisation avec un objet JSON qui ne respecte pas les contraintes de validation de l'objet Article

Groupe de validation

Les groupes de validation sont également supportés par le FOSRestBundle. Pour faire appel à un ou plusieurs groupe(s) de validation lors de la désérialisation via le body converter, il suffit d'ajouter une option  validator  au ParamConverter, comme suit :

<?php

namespace AppBundle\Controller;

// …
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\HttpFoundation\Response;

class ArticleController extends FOSRestController
{
    /**
     * @Rest\Post("/articles")
     * @Rest\View(StatusCode = 201)
     * @ParamConverter(
     *     "article",
     *     converter="fos_rest.request_body",
     *     options={
     *         "validator"={ "groups"="Create" }
     *     }
     * )
     */
    public function createAction(Article $article, ConstraintViolationList $violations)
    {
        // …
    }
}

 Il suffit ensuite de déclarer le groupe de validation dans la configuration de validation de notre ressource, et le tour est joué !

<?php

namespace AppBundle\Entity;

// …

/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
 * @ORM\Table()
 *
 * @ExclusionPolicy("all")
 *
 */
class Article
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     *
     * @Expose
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     * @Expose
     * @Assert\NotBlank(groups={"Create"})
     */
    private $title;

    /**
     * @ORM\Column(type="text")
     * @Expose
     * @Assert\NotBlank(groups={"Create"})
     */
    private $content;

    // …
}

 

Rendez-vous au prochain chapitre où nous aborderons le sujet de la gestion des erreurs/exceptions afin de s'assurer que les clients de notre API aient une réponse adéquate en fonction de la situation.

Exemple de certificat de réussite
Exemple de certificat de réussite