FOSRestBundle CRUD

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

Je souhaite créer une application en utilisant les principes de RESTful, j'ai donc installer FOSRestBundle dans mon application et j'ai réussi en suivant différents tuto à implémenter les méthodes GET et DELETE mais je galère sur les méthodes POST et PUT.

J'ai installé plusieurs applications (au cas où qu'une d'entre elle ne fonctionnerai pas comme elle devrai) me permettant de tester des requêtes sur mon API.

Tout d'abord, de manière un peu anecdotique, lorsque je fais une requête POST http://projet:8000/produits j'obtiens le retour suivant:

1
2
3
4
<br />
<b>Deprecated</b>:  Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version. To avoid this warning set 'always_populate_raw_post_data' to '-1' in php.ini and use the php://input stream instead. in <b>Unknown</b> on line <b>0</b><br />
<br />
<b>Warning</b>:  Cannot modify header information - headers already sent in <b>Unknown</b> on line <b>0</b><br />

Cela n'empêche pas la méthode gérant le POST d'être exécutée ensuite… du coup j'ignore comment corriger ce problème. Par ailleurs curieusement avec PUT je n'ai pas cette erreur.

Voici le code des méthode POST et PUT de mon controller:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
public function postAction(Request $request) {
    $em = $this->getDoctrine()->getManager();
    $produit = new Produit();
    $form = $this->createForm(ProduitType::class, $produit);
    $form->handleRequest($request);

    if ( $form->isValid() ) {
        $em->persist($produit);
        $em->flush();
    }
}

public function putAction(Request $request, $id) {
    $em = $this->getDoctrine()->getManager();
    $produit = $em->getRepository('AppBundle:Produit')->find($id);
    $form = $this->createForm(ProduitType::class, $produit);
    $form->handleRequest($request);

    //return $produit->getLibelle();

    if ( $form->isValid() ) {
        $em->persist($produit);
        $em->flush();
    }
}

Si j'envoie un produit en PUT et que je dé-commente la ligne 20, je remarque que le libellé est en fait celui provenant de la base de données et non celui que j'ai envoyé avec ma requête HTTP alors que dans le profiler je constate que j'ai bien envoyé les informations:

Paramètre POST

C'est donc comme si mon entité n'était pas mise à jour par ma requête… J'imagine que pour mon POST le problème est le même.

Comment dois-je régler le problème ?

Merci d'avance pour votre aide.
Cordialement, La source.

+0 -0

Hello !

Tout d'abord je tiens à préciser que quoi que tu fasses c'est très loin de l'API RESTful, et c'est même pas REST tout court.

Pour ce qui est de FOSRest, tu ne l'utilises pas mal dans ton code. Par contre ta notice de deprecated ne devrait pas se retrouver là (ce qui provoque le problème suivant). Tu utilises quelle configuration pour faire fonctionner ton Symfony, ça vient probablement de là ? (je recommande de le lancer avec php app/console server:run en mode dev ! Comme ça tu n'auras aucun problème)

Et pour finir, il semblerait que ton problème vienne plutôt de la requête que tu lances sur ton serveur, tu peux la détailler ? :)

+0 -0
Auteur du sujet

Je pensais que faire un CRUD via http en gérant une ressources (ici Produit) était du REST… Je vais lire avec attention le lien que tu as donné ;)

J'utilise Symfony 3.0.4-DEV avec php 5.6.19 et apache 2.4.18, j'utilise effectivement la commande php bin/console server:run pour faire fonctionner le tout.
Concernant ma requête… j'ignore exactement ce que tu souhaite avoir, donc je vais détailler un maximum en espérant que l'informations dont tu as besoin s'y trouvera.

J'ai installé le programme Insomnia, voici une capture du programme une fois les différents champs complété:

Interface Insomnia

Je n'ai renseigner aucun autre paramètre (donc les onglets PARAMS, AUTH et HEADERS sont vide)

Au niveau de Symfony, voici ce que le profiler m'indique avoir reçu:

Informations Symfony suite à la requête d'Insomnia

Suite à ta réflexion sur la requête qui serai mauvaise j'ai vite fait écrit un code javascript pour voir ce que sa donnerai, cela ne fonctionne toujours pas, j'ai regardé le profiler et je remarque tout de même une légère différence.

Pour la requête avec Insomnia la Request Content est sous la forme json {"libelle": "Fanta2","prix": 1.5} et pour la requête JQuery la requête est sous la forme brute libelle=Fanta2&prix=1.5.

A par sa je ne vois pas quoi donner comme informations… J'espère qu'avec sa vous pourrez me dire où je me plante :p

+0 -0

Cette réponse a aidé l'auteur du sujet

Rapport à ce qu'est une API REST, par la même personne il y a cette vidéo qui est très bien: https://www.youtube.com/watch?v=nm1obAL1xoo

Pour ce qui est du deprecated je vais m'avancer sans trop de certitudes car je n'ai pas été voir le code de Symfony et n'ai jamais eu cette erreur, en outre c'est étrange que cela pop en 3.x alors que ça ne devrait être qu'en 2.8 max… De plus normalment Symfony handle totalement les deprecated et les affiche "joliment" dans le profiler

Je pense que le problème vient du fait que tu utilises des formulaires… En envoyant du json dans la request ! Cet usage inconscient était permis par Symfony 2.x mais la magie n'étant jamais source de bonheur, ils sont probablement revenu en arrière.

Concrètement le problème c'est que même en étant conscient de ça, les APIs codées avec Symfony étaient souvent utilisables avec deux type de request: les form-data et les "json body content" (appellation © Nek).

Il y a probablement un moyen de configurer de sorte à ce que ce binding soit toujours valable, mais c'est un peu bizarre de faire les deux à la fois.

Sinon tu es sous windows, j'imagine que tu utilises WAMP… Il n'est pas impossible qu'il y ai une mauvaise configuration de PHP/Apache par rapport au deprecated. Tu devrais pouvoir trouver des réponses avec une recherche rapide.

Édité par Nek

+1 -0
Auteur du sujet

Usage inconscient ??? Et comment suis-je censé envoyer les informations normalement ?

Pour ma part peu importe la méthode, je veux bien me limiter qu'a une seule d'entre elle pour peu que cela fonctionne, cependant comme je l'ai dit, que cela soie via Insomnia ou via JQuery aucune des deux méthodes bind mon entité :(

+0 -0

Cette réponse a aidé l'auteur du sujet

Je te l'ai dit, en "form-data". Ici ils montrent comment faire avec jquery: http://chez-syl.fr/2012/01/envoyer-un-formulaire-en-ajax-avec-jquery-et-json/

Pour une requête http (POST) ça serait quelque chose comme ça:

1
2
3
4
5
6
POST /path/to/script HTTP/1.0
User-Agent: Firefox, whatever
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

libelle=Fanta2&prix=1.5

Et pour l'envoie de fichiers on utilisera les requêtes multipart.

Alors que dans ton cas tu utilises du json, et si tu devais envoyer un fichier j'imagine que tu l'envoies encodé en base64 dans une chaîne de caractère. Ce que tu envoies ressemble à ça:

1
2
3
4
5
6
POST /path/to/script HTTP/1.0
User-Agent: Firefox, whatever
Content-Type: application/json
Content-Length: 32

{"libelle": "Fanta2","prix": 1.5}

En soit c'est FOS Rest qui fait la conversion « automatique ».

Édité par Nek

+1 -0
Auteur du sujet

Pour le Deprecated, j'ai fais des recherches sur Google comme tu l'avais conseillé, et en fait ce n'est pas Symfony qui rouspétait mais php, toutes les informations sont sur le site officiel de php. Une fois réglé la valeur à -1 dans les php.ini je n'ai plus le problème. :)

Pour la requête http j'ai compris ce que tu voulais dire, le seul problème c'est que cela dépens du client finalement la manière dont ces informations sont envoyée.

Ceci étant je restais persuadé que le problème ne venait pas de là puisque j'ai tenté d'envoyer les informations dans les deux formats et dans les deux cas cela ne fonctionnait pas.
J'ai donc continuer de fouillé sur le web et j'ai remarqué que dans ma classe ProduitType je déclarait une méthode getName retournant la valeur 'produit'.
J'ai pu lire dans un tuto que pour accepter une entrée de données brute (donc tel que présenté dans notre conversation jusqu'ici) il fallait retourner la valeur ''.
J'ai essayé et cela ne fonctionnait toujours pas, mais j'ai cherché sur google getName et Symfony 3 et j'ai pu constaté que la méthode a été renommée en getBlockPrefix (que je trouve un peu moins explicite mais soit). En renommant la méthode mon entité a finalement été bindée. :)

Enfin bref, mon problème est résolu :) il aura fallut beaucoup chercher mais je sais quoi faire pour mes autres entités à présent ^^

Un tout grand merci à toi du temps que tu m'as consacré.

+0 -0

Haha j'allais y venir. Personellement je fait comme ça:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

class Controller extends BaseController
{
    /**
     * @param string                                        $name
     * @param string|\Symfony\Component\Form\AbstractType   $type
     * @param mixed                                         $data
     * @param array                                         $options
     * @return \Symfony\Component\Form\FormInterface
     */
    protected function createNamedForm($name, $type, $data = null, array $options = [])
    {
        return $this->get('form.factory')->createNamed($name, $type, $data, $options);
    }

    public function createUserAction(Request $request)
    {
        if ($this->getUser() !== null) {
            return $this->errorResponse('Cannot create a new user.', 400);
        }
        $user = new User();
        $form = $this->createNamedForm('', UserType::class, $user);
        $form->handleRequest($request);
        if ($form->isValid()) {
            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($user);
            $entityManager->flush();
            return $this->successResponse('Your account was correctly created.');
        }

        return new JsonResponse([
            'errors' => $this->getFormErrors($form),
        ], 400);
    }

Édité par Nek

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte