web-dev-qa-db-fra.com

Récupérer POST données de AJAX appel

J'ai le script JS suivant:

jQuery('#form-recherche').submit(ajaxSubmit);

        function ajaxSubmit(){

            var newFormRecherche = jQuery(this).serialize();

            jQuery.ajax({
                type:"post",
                data: { 
                    action: "mon_action",
                    newFormRecherche: newFormRecherche,
                },

                url: ajaxurl,
                success: function(response){
                    console.log(response);
                }
            });

        return false;
        }

Du côté PHP:

    add_action( 'wp_ajax_mon_action', 'mon_action' );
    add_action( 'wp_ajax_nopriv_mon_action', 'mon_action' );

    function mon_action() {

        if (isset($_POST["newFormRecherche"])) {
            $field1= $_POST["field1"];
            $field2= $_POST["field2"];
        }
    }

Comme vous l'avez deviné, je peux accéder à $_POST["newFormRecherche"] alors que je ne peux pas récupérer $_POST["field1"] ni $_POST["field2"].

jQuery serialize() fonctionne correctement: j’ai testé newFormRecherche var avec une alerte et il s’affiche sous le bon formulaire: $field1=whatever&$field2=anything.

Normalement, je ne devrais pas avoir à analyser les résultats pour accéder aux variables $ _POST [], selon ce que j'ai lu ici , mais évidemment, cela ne fonctionne pas. D'où vient ce problème? Devrais-je utiliser autre chose que data pour transmettre mes arguments?

EDIT: $_POST["newFormRecherche"] existe du côté PHP et contient la chaîne attendue $field1=whatever&$field2=anything.

EDIT # 2 : Voici une mise à jour, selon l’intéressante remarque de @czerspalace et le message très détaillé de @bosco. J'essaie ici de résumer ce qu'ils ont dit et de donner des solutions.

Le problème ici était une double sérialisation, faite "à la main", faite par jQuery en effectuant l'appel AJAX. Le fait que les variables $_POST[] ne puissent pas être récupérées correctement côté serveur provient de data, qui doit correspondre au formalisme Wordpress, c’est-à-dire: une action (qui est une fonction PHP) et les données envoyé (généralement, à partir d'un formulaire).

  • Solution by @Bosco - Utilisez la méthode jQuery serializeArray(). Dans ce cas, les données envoyées sont composées de 2 objets. Afin de récupérer correctement les champs côté serveur, je dois gérer un tableau associatif comme celui-ci: $_POST['newFormRecherche'][0]['name'] et $_POST['newFormRecherche'][0]['value']. Même chose pour les autres champs (remplacement de [0] par d'autres nombres). Pour résoudre ce problème, @Bosco propose ci-dessous la fonction formFieldsToObject qui est appelée sur les données lors de l'appel AJAX.

  • Solution by @czerspalace - Utilisez la méthode serialize() jQuery et effectuez une désérialisation manuelle côté serveur à l'aide de parse_str( $_POST[ 'newFormRecherche' ], $newFormRecherche ); afin de pouvoir récupérer les champs souhaités: $ newFormRecherche ['field1'], ..., etc., etc.

Dans les deux cas, les données côté serveur doivent être correctement nettoyées, tout comme les données envoyées par un utilisateur via des formulaires. Cela implique: vérifier les types de champs, vérifier (et même tronquer) la longueur des champs ..., autrement dit: ne jamais faire confiance à l'utilisateur.

EDIT # 3 : Si vous utilisez FormData, assurez-vous d’ajouter cette ligne à votre appel AJAX: processData: false,. Cependant, je n'ai pas implémenté de solution complète avec cette technique.

5
Fafanellu

Problème

La "sérialisation" consiste à transformer un objet de données en une représentation sous forme de chaîne. jQuery sérialise automatiquement la propriété data avant d'envoyer une demande AJAX. Le serveur désérialise ensuite la chaîne de chaînes GET à partir de l'URL et le corps de la requête pour les requêtes POST avant de renseigner les variables de requête PHP.

Votre code a fonctionné comme prévu lorsque data consistait uniquement en vos données de formulaire sérialisées (c.-à-d. data: newFormRecherche,), car les données étaient déjà au format chaîne et ne pouvaient donc pas être sérialisées davantage. La passe de désérialisation du serveur a ensuite correctement analysé les données de votre formulaire en variables de demande.

Cependant, lorsque l'argument data est un objet, jQuery doit le sérialiser pour le transmettre à un serveur. En tant que propriété de cet objet, vos données de formulaire pré-sérialisées sont traitées comme s'il s'agissait de toute autre chaîne. En particulier, elles sont échappées de manière à éviter toute "erreur" par rapport à un objet sérialisé. Ainsi, lorsque le serveur désérialise ensuite data, newFormRecherche est désérialisé dans la valeur à laquelle jQuery l'a initialement reçue - c'est-à-dire une chaîne - et nécessite donc la deuxième passe de désérialisation à laquelle @czerspalace a fait allusion dans les commentaires afin de générer un tableau associatif. des données de formulaire.


Solutions

- jQuery

Pour éviter la double sérialisation de vos données de formulaire, acquérez-les sous la forme d'un tableau de paires clé/valeur plutôt que d'une chaîne sérialisée en appelant la méthode .serializeArray() de jQuery sur l'élément de formulaire au lieu de .serialize().

Bien que jQuery soit capable de sérialiser correctement ce format clé/valeur de tableau data, il semble qu'il s'étouffe lorsqu'un tel tableau est imbriqué en tant que propriété d'un objet data (au lieu d'envoyer la chaîne '[object Object]' à la place de chaque paire clé/valeur), ainsi que lors de la tentative d'imbrication d'un tel tableau clé/valeur dans un autre. En tant que tel, je pense que le meilleur moyen d’envoyer des données de formulaire dans le cadre de données multidimensionnelles est de convertir le tableau clé/valeur en objet.

Tout cela peut être fait comme suit:

function ajaxSubmit() {
  var newFormRecherche = jQuery( this ).serializeArray();

  jQuery.ajax({
    type:"POST",
    data: { 
      action: "mon_action",
      newFormRecherche: formFieldsToObject( newFormRecherche )
    },
    url: ajaxurl,
    success: function( response ){
      console.log( response );
    }
  });

  return false;
}

function formFieldsToObject( fields ) {
  var product = {};

  for( var i = 0; i < fields.length; i++ ) {
    var field = fields[ i ];

    if( ! product.hasOwnProperty( field.name ) ) {
      product[ field.name ] = field.value;
    }
    else {
      if( ! product[ field.name ] instanceof Array )
        product[ field.name ] = [ product[ field.name ] ];

      product[ field.name ].Push( field.value );
    }
  }

  return product;
}

- HTML5 FormData

Alternativement, si vous n'avez besoin que de supporter les navigateurs modernes ou si vous chargez un polyfill pour prendre en charge les anciens, utilisez simplement l'objet FormData pour transmettre vos données de formulaire en tant qu'objet de données approprié:

function ajaxSubmit() {
  var newFormRecherche = new FormData( this );

  jQuery.ajax({
    type:"POST",
    data: { 
      action: "mon_action",
      newFormRecherche: newFormRecherche
    },
    url: ajaxurl,
    success: function( response ){
      console.log( response );
    }
  });

  return false;
}

- Double désérialisation côté serveur

Comme @czerspalace le suggère dans les commentaires, la comptabilisation de la double sérialisation par une simple désérialisation manuelle des données de formulaire sur le serveur est également une solution tout à fait valable:

add_action( 'wp_ajax_mon_action', 'mon_action' );
add_action( 'wp_ajax_nopriv_mon_action', 'mon_action' );

function mon_action() {
  if( isset( $_POST[ 'newFormRecherche' ] ) ) {
    parse_str( $_POST[ 'newFormRecherche' ], $newFormRecherche );
    die( json_encode( $newFormRecherche ) );
  }
}

Je suis enclin à penser que les autres approches sont plus "professionnelles" en ce sens que toutes les données envoyées au serveur sont regroupées dans un format cohérent et attendu - aucun "décodage" supplémentaire n'est nécessaire de la part du serveur, ce qui améliore la modularité du code.

Mais la modularité des codes et le "professionnalisme" subjectif ne sont pas toujours les facteurs les plus importants. Parfois, la solution la plus simple est la plus appropriée.


N'oubliez pas de valider et nettoyer les données de la demande sur le serveur avant de les utiliser afin d'atténuer les vulnérabilités de sécurité.

7
bosco