Bonjour,
Je suis actuellement en train de faire un site avec une api en Symfony 4 et le fornt en Vue.js.

Pour communiquer avec mon api, j'utilise vue-resource pour pouvoir réaliser des requêtes Ajax.

Mon problème est que j'obtiens une erreur 400 lorsque je soumet mon formulaire de login.

<template>
  <div class="login">
    <h1>Formulaire de connexion</h1>
    <form method="post" v-on:submit.prevent>
      <input v-model="email">
      <input type="password" v-model="password">
      <button @click="login(email, password)">Se connecter</button>
    </form>
  </div>
</template>

<script>
  export default {
    name: 'Login',
    data () {
      return {
        email: '',
        password: '',
      }
    },
    mounted () {
      this.$http.get('https://a2asymfonyapi.herokuapp.com/concurrents').then((response) => {
        console.log(response)
      })
    },
    methods: {
      login (email, password) {
        this.$http.post('https://a2asymfonyapi.herokuapp.com/login', {
          _username: email,
          _password: password
        }).then((response) => {
          console.log('toto')
        }, (response) => {
            console.log('error', response)
        })
      }
    }
  }
</script>

Mon action Symfony:

    /**
     * @param AuthenticationUtils $authenticationUtils
     * @param Request $request
     * @param User $user
     * @return \Symfony\Component\HttpFoundation\Response
     * @Route("/login", name="login", methods="POST")
     */
    public function login(AuthenticationUtils $authenticationUtils, Request $request, User $user)
    {
        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();
        if($error) {
            return new JsonResponse([
                'status' => false,
                'message' => $error
            ], 404);
        }

        $user->setToken($request->cookies->get('token'));

        return new JsonResponse([
            'status' => true,
            'user' => $this->getUser(),
            'token' => $user->getToken()
        ], 200);
    }

Lorsque je regarde dans les logs d'Heroku, je vois que _username est à NULL:
[error] Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\BadRequestHttpException: "The key "_username" must be a string, "NULL" given." at /app/vendor/symfony/security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php line 89

Je ne comprends pas pourquoi puisque j'arrive à récupérer le username et le mot de passe dans la finction login (vue.js) grâce au console.log.

Quelqu'un a t-il une idée?

PS: J'arrive à communiquer avec mon api puisque que la requête http réalisée dans le mounted() fonctionne.

Je vous remercie d'avance pour votre aide! :)

13 réponses


Salut,
Le formulaire ?
Sinon pour te simplifier la vie : https://github.com/lexik/LexikJWTAuthenticationBundle
Avec Api-Platform suivant tes besoins c'est le top.

eclairia
Auteur

Le formulaire est dans la partie templatedu premier code fourni.

<template>
  <div class="login">
    <h1>Formulaire de connexion</h1>
    <form method="post" v-on:submit.prevent>
      <input v-model="email">
      <input type="password" v-model="password">
      <button @click="login(email, password)">Se connecter</button>
    </form>
  </div>
</template>

Hello, envoies-tu le bon content-type ?

eclairia
Auteur

C'est à dire?
Je dois préciser dans ma requête ajax le content-type?

Je ne sais pas comment fonctionne Symfony mais j'ai déjà eu ce genre de problème sur Laravel et c'était un souci de content-type. L'un envoyait du JSON, l'autre attendait 'application/x-www-form-urlencoded' .

eclairia
Auteur

J'ai tenté ça mais cela ne fonctionne toujours pas:

      toto() {
        const requestBody = {
          name: 'toto'
        }

        const config = {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          }
        }

        let name = this.name

        this.$http.post('https://a2asymfonyapi.herokuapp.com/toto', {
          name: name
        }, config).then((response) => {
          console.log('toto')
        }, (response) => {
            console.log('error', response)
          }
        )
      }

Je remarque dans certains tutoriels que l'application Vue.js est dans l'app Symfony comme le montre ce tuto: https://blog.eleven-labs.com/fr/ssr-symfony-vue/

Suis-je obligé de faire comme dans le tuto ou je peux avoir 2 applis complètement différentes?

Il y a pas mal de choses que je ne comprends pas dans ton dernier code. Pourquoi l'url est maintenant /toto au lieu de /login?
Pourquoi as-tu changé les clés dans ton body?

Le plus simple serait d'essayer avec un logiciel d'API (Postman). Une fois que tu as trouvé la bonne configuration, tu peux réintégrer cela dans ton code.

eclairia
Auteur

J'arrive à récupérer la bonne valeur quand je passe par postman par contre lorsque je passe par l'app VUe.js mon serveur (heroku) me retourne une erreur 400.
Pour éviter de me répéter, j'explique plus en détails mon problème içi:
[https://openclassrooms.com/forum/sujet/sf4-request-est-a-null?page=1#message-92660945]()

Il y a quelque chose d'incorrect dans ton code. https://a2asymfonyapi.herokuapp.com/toto?name=toto n'est pas identique à

this.$http.post('https://a2asymfonyapi.herokuapp.com/toto', {
          name: name
        }, config)

Si tu fais un POST, ta donnée doit être dans le body de la requête et non dans l'URL. Si on appelle ton url, tu récupères bien toto, mais si on l'envoie dans le body, cela affiche NULL.

eclairia
Auteur

J'ai modifié mon code et ma requête dans postman en mettant le name dans le body. Cela fonctionne toujours sur postman mais j'ai toujours la même erreur avec mon code vue.js.

https://codepen.io/prbaron/pen/xaBjYX

J'ai essayé un petit code et il semblerait que tu aies un souci de CORS. Il faut que tu acceptes les connexions depuis d'autres sites.

eclairia
Auteur

J'ai réglé ce soucis en rajoutant ces headers dans la requête:

         const config = {
          headers: {
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'application/x-www-form-urlencoded'
          }
        }  

Côté Symfony, j'ai utilisé le bundle nelmio/cors-bundle pour autoriser toutes les applications.

Configuration de nelmio pour l'api

nelmio_cors:
    defaults:
        origin_regex: true
        allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
        allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
        allow_headers: ['Content-Type', 'Authorization', 'application/x-www-form-urlencoded']
        max_age: 3600
    paths:
        '^https://a2asymfonyapi.herokuapp.com/':
            allow_origin: ['*']
            allow_headers: ['*']
            allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
            max_age: 3600
eclairia
Auteur

J'ai réussi à régler l'erreur sur le CORS (comme tu peux le voir avec ton code codepen) mais je n'arrive toujours pas à récupérer la valeur de ma requête dans mon controller Symfony.
Une idée?