title: Cannot see validation errors when form is invalid (submitted as json from react)
body: <p>How do I see validation errors when posting json data and self submitting to a symfony form? isValid() is false but I can't access error messages to return. But the Symfony Profiler DOES show the error messages in the Ajax request history. E.g. when duplicate username the profiler shows:</p>
  
  <p>Validator calls in ValidationListener.php
  data.username There is already an account with this username</p>
  
  <p>Forms "registration_form" "App\Form\RegistrationFormType"
  There is already an account with this username
  Caused by: Symfony\Component\Validator\ConstraintViolation</p>
  
  <p>When all the fields are valid the new User is created in the database successfully as expected.</p>
  
  <p>Here is my controller:</p>
  
  <pre><code>namespace App\Controller;
  
  use App\Entity\User;
  use App\Form\RegistrationFormType;
  use App\Security\LoginFormAuthenticator;
  use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  use Symfony\Component\HttpFoundation\Request;
  use Symfony\Component\HttpFoundation\Response;
  use Symfony\Component\Routing\Annotation\Route;
  use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
  
  class RegistrationController extends AbstractController
  {
      /**
       * @Route("/api/register", name="app_register")
       */
      public function register(
          Request $request,
          UserPasswordEncoderInterface $passwordEncoder,
          GuardAuthenticatorHandler $guardHandler,
          LoginFormAuthenticator $authenticator
      ): Response {
          if ($request-&gt;isMethod('POST')) {
              $user = new User();
              $form = $this-&gt;createForm(RegistrationFormType::class, $user);
  
              $data = json_decode($request-&gt;getContent(), true);
  
              $form-&gt;submit($data);
  
              if ($form-&gt;isSubmitted()) {
                  if ($form-&gt;isValid()) {
                      $user-&gt;setPassword(
                          $passwordEncoder-&gt;encodePassword(
                              $user,
                              $form-&gt;get('plainPassword')-&gt;getData()
                          )
                      );
  
                      $em = $this-&gt;getDoctrine()-&gt;getManager();
                      $em-&gt;persist($user);
                      $em-&gt;flush();
  
                      // login the newly registered user
                      $login = $guardHandler-&gt;authenticateUserAndHandleSuccess(
                          $user,
                          $request,
                          $authenticator,
                          'main' // firewall name in security.yaml
                      );
  
                      if ($login !== null) {
                          return $login;
                      }
  
                      return $this-&gt;json([
                          'username' =&gt; $user-&gt;getUsername(),
                          'roles' =&gt; $user-&gt;getRoles(),
                      ]);
                  } else {
                      $formErrors = $form-&gt;getErrors(true); // returns {}
                      return $this-&gt;json($formErrors, Response::HTTP_BAD_REQUEST);
                  }
              }
          }
      }
  </code></pre>
  
  <p>Here is my RegistrationFormType:</p>
  
  <pre><code>namespace App\Form;
  
  use App\Entity\User;
  use Symfony\Component\Form\AbstractType;
  use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  use Symfony\Component\Form\Extension\Core\Type\EmailType;
  use Symfony\Component\Form\Extension\Core\Type\PasswordType;
  use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  use Symfony\Component\Form\Extension\Core\Type\TextType;
  use Symfony\Component\Form\FormBuilderInterface;
  use Symfony\Component\OptionsResolver\OptionsResolver;
  use Symfony\Component\Validator\Constraints\IsTrue;
  
  class RegistrationFormType extends AbstractType
  {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder
              -&gt;add('firstName', TextType::class, [
                  'label' =&gt; 'First Name',
                  'required' =&gt; false
              ])
              -&gt;add('lastName', TextType::class, [
                  'label' =&gt; 'Last Name',
                  'required' =&gt; false
              ])
              -&gt;add('username')
              -&gt;add('emailAddress', EmailType::class, [
                  'label' =&gt; 'Email Address'
              ])
              -&gt;add('plainPassword', PasswordType::class, [
                  'mapped' =&gt; false
              ])
              -&gt;add('agreeTerms', CheckboxType::class, [
                  'mapped' =&gt; false,
                  'constraints' =&gt; [
                      new IsTrue([
                          'message' =&gt; 'You must comply.',
                      ]),
                  ],
              ])
              -&gt;add('Register', SubmitType::class)
          ;
      }
  
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults([
              'data_class' =&gt; User::class,
              'csrf_protection' =&gt; false
          ]);
      }
  
      public function getName()
      {
          return 'registration_form';
      }
  }
  </code></pre>
  
  <p>Here is my entity:</p>
  
  <pre><code>namespace App\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
  use Symfony\Component\Security\Core\User\UserInterface;
  
  /**
   * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
   * @UniqueEntity(fields={"username"}, message="There is already an account with this username")
   * @UniqueEntity(fields={"emailAddress"}, message="There is already an account with this email address")
   */
  class User implements UserInterface
  {
      /**
       * @ORM\Id()
       * @ORM\GeneratedValue()
       * @ORM\Column(type="integer")
       */
      private $id;
  
      /**
       * @ORM\Column(type="string", length=180, unique=true)
       */
      private $username;
  
      /**
       * @ORM\Column(type="json")
       */
      private $roles = [];
  
      /**
       * @var string The hashed password
       * @ORM\Column(type="string")
       */
      private $password;
  
      /**
       * @ORM\Column(type="string", length=180, unique=true)
       */
      private $emailAddress;
  
      /**
       * @ORM\Column(type="string", length=80, nullable=true)
       */
      private $firstName;
  
      /**
       * @ORM\Column(type="string", length=80, nullable=true)
       */
      private $lastName;
  
      public function getId(): ?int
      {
          return $this-&gt;id;
      }
  
      /**
       * A visual identifier that represents this user.
       *
       * @see UserInterface
       */
      public function getUsername(): string
      {
          return (string) $this-&gt;username;
      }
  
      public function setUsername(string $username): self
      {
          $this-&gt;username = $username;
  
          return $this;
      }
  
      /**
       * @see UserInterface
       */
      public function getRoles(): array
      {
          $roles = $this-&gt;roles;
          // guarantee every user at least has ROLE_USER
          $roles[] = 'ROLE_USER';
  
          return array_unique($roles);
      }
  
      public function setRoles(array $roles): self
      {
          $this-&gt;roles = $roles;
  
          return $this;
      }
  
      /**
       * @see UserInterface
       */
      public function getPassword(): string
      {
          return (string) $this-&gt;password;
      }
  
      public function setPassword(string $password): self
      {
          $this-&gt;password = $password;
  
          return $this;
      }
  
      /**
       * @see UserInterface
       */
      public function getSalt()
      {
          // not needed when using the "bcrypt" algorithm in security.yaml
      }
  
      /**
       * @see UserInterface
       */
      public function eraseCredentials()
      {
          // If you store any temporary, sensitive data on the user, clear it here
          // $this-&gt;plainPassword = null;
      }
  
      public function getEmailAddress(): ?string
      {
          return $this-&gt;emailAddress;
      }
  
      public function setEmailAddress(string $emailAddress): self
      {
          $this-&gt;emailAddress = $emailAddress;
  
          return $this;
      }
  
      public function getFirstName(): ?string
      {
          return $this-&gt;firstName;
      }
  
      public function setFirstName(?string $firstName): self
      {
          $this-&gt;firstName = $firstName;
  
          return $this;
      }
  
      public function getLastName(): ?string
      {
          return $this-&gt;lastName;
      }
  
      public function setLastName(?string $lastName): self
      {
          $this-&gt;lastName = $lastName;
  
          return $this;
      }
  }
  </code></pre>