Zapobiegaj przesyłaniu wielu formularzy (po stronie serwera)

9

Występuje problem polegający na tym, że użytkownik może wielokrotnie przesłać dowolny formularz zbudowany przez interfejs API formularza (szybkie kliknięcie powoduje powstanie wielu żądań).

Włączyłem podstawowe rozwiązanie polegające na wyłączeniu przycisku po stronie klienta (javascript), ale jestem ciekawy, jakie jest najlepsze podejście do zapobiegania tej sytuacji po stronie serwera.

Czy istnieje zalecany sposób użycia systemu tokenów formularzy Drupala do obsługi tego? Zwłaszcza globalne rozwiązanie formularza (tj. Dodanie niestandardowego walidatora do każdego formularza za pomocą hook_form_alter ()).

Moje podejście do tej pory było takie:

function mymodule_form_alter(&$form, &$form_state, $form_id) {
  $form['#validate'][] = 'mymodule_form_validate';
}

function mymodule_form_validate(&$form, &$form_state){
  //initialize form array
  if (!isset($_SESSION['submitted_forms'])){
    $_SESSION['submitted_forms'] = array();
  }

  $form_token = $form_state['values']['form_token'];
  if ( isset($_SESSION['submitted_forms'][$form_token]) && $_SESSION['submitted_forms'][$form_token] = TRUE ){
    form_set_error('name]', 'This form has already been submitted');
  }
  else{
    $_SESSION['submitted_forms'][$form_token] = TRUE;
  }
}

Mam problem z tym, że form_token nie jest unikalny dla formularza - wydaje się, że pozostaje taki sam bez względu na to, co się stanie. Prawdopodobnie nie rozumiem, czym jest token w wielkim schemacie interfejsu API.

Doceniamy każdy wgląd!

PrairieHippo
źródło
Jako kontynuację zacząłem używać $ form_state ['form_build_id'] zamiast tokena. Jeśli prześlę dwukrotnie ten sam identyfikator kompilacji formularza, gdzieś po drodze formularz zostanie zrekonstruowany i przetworzony.
PrairieHippo

Odpowiedzi:

8

Miałem dokładnie ten sam problem i udało mi się go naprawić za pomocą mechanizmów blokujących Drupala

W funkcji sprawdzania poprawności użyłem:

function mymodule_custom_form_validate($form, &$form_state){
  if (lock_acquire('your_custom_lock_name')) {
    // long operations here
  } else {
    form_set_error("", t("You submitted this form already."));
  }
}

W funkcji przesyłania zwolniłem blokadę:

function mymodule_custom_form_submit($form, &$form_state){
  // submit code
  lock_release('your_custom_lock_name');
}
Marius Ilie
źródło
1

Tutaj powinieneś wziąć pod uwagę wagę modułu:

  1. Jeden moduł (niech first_module), który powinien mieć ujemną wagę modułu wartość maksymalna (może wynosić -2000), powinien zaimplementować hook_form_alter () z następującym kodem. Teraz należy sprawdzić, czy formularz jest już przesłany przez kod.
   function first_module_form_alter(&$form, &$form_state, $form_id)
    {
      $form['#validate'][] = 'mymodule_form_validate';
    }
function mymodule_form_validate(&$form, &$form_state){
  //a($form_state);
  //initialize form array
  if (!isset($_SESSION['submitted_forms'])){
    $_SESSION['submitted_forms'] = array();
  }

  $form_token = $form_state['values']['form_id'];
  if ( isset($_SESSION['submitted_forms'][$form_token]) && $_SESSION['submitted_forms'][$form_token] = TRUE ){
    form_set_error('name]', 'This form has already been submitted');
  }
  else{
    $_SESSION['submitted_forms'][$form_token] = TRUE;
  }
}
  1. Second_module, które mają wagę dodatniej wyższej wartości. Tutaj należy rozbroić sesję poprzez dodanie wywołania zwrotnego do modułu

function second_module_form_alter (& $ form, & $ form_state, $ form_id) {$ form ['# Submit'] [] = 'mymodule_form_submit'; }

function mymodule_form_submit(&$form, &$form_state){

  $form_token = $form_state['values']['form_id'];
  unset($_SESSION['submitted_forms'][$form_token]);

}
sathishkumar
źródło
1

Jeśli chcesz tę funkcjonalność we wszystkich formularzach i większą kontrolę bez kodowania, zajrzyj do modułu Ukryj przycisk wysyłania.

Funkcje:

  1. Ukryj lub wyłącz przycisk wysyłania po jego kliknięciu
  2. Wyświetl wiadomość i / lub obraz podczas oczekiwania
Gokul NK
źródło
5
Moduł Hide Submt Button nie jest rozwiązaniem po stronie serwera. Z opisu modułu: „W przypadku przeglądarek z wyłączonym Javascriptem ten moduł nie będzie miał żadnego efektu”. drupal.org/project/hide_submit
Blake Frederick
0
$form['submit'] = array(
  '#type' => 'submit',
  '#value' => t('Save'),
  '#attributes' => array(
    'onclick' => 'javascript:var s=this;setTimeout(function(){s.value="Saving...";s.disabled=true;},1);',
  ),
);

mam nadzieję, że to pomoże ..

lub możesz polecić Zapobieganie wielokrotnym kliknięciom przycisku wysyłania, a drupal ma jeden moduł Ukryj przycisk wysyłania

Niektórzy użytkownicy przypadkowo klikają przycisk przesyłania więcej niż raz podczas oczekiwania na zapisanie swojego postu. W niektórych przypadkach może to skutkować powieleniem księgowań lub zduplikowaniem zamówień w handlu elektronicznym.

madhurjya
źródło
-1

To był mój problem także wcześniej. Moim rozwiązaniem jest wyłączenie przycisku za pomocą JS.

.moduł:

/**
 * Implementation of hook_init().
 */
function myModule_init(){
if (arg(0) == 'node' && (arg(2) == 'edit' || arg(1) == 'add')) {
    //hide btn when clicked on article nodes
    drupal_add_js(drupal_get_path('module', myModule') . '/js/disable-submit.js');
}

JS:

Drupal.behaviors.module_disable_submit = function (context) {

/* 
 * Disable keypress on form fields.
 * Prevent browser to reload when pressing enter in input fields 
 */


$('.buttons input:submit').click(function() {
  $('.buttons input:submit').hide();
  $('#node-form .buttons').prepend('<input type="submit" style="margin:1px 0; box-shadow:0 1px 1px #DDDDDD; border-radius:3px 3px 3px 3px; background:url(/sites/all/themes/rubik/images/bleeds.png) repeat-x scroll 0 -41px #F4F4F4; border-color:#DDDDDD #DDDDDD #CCCCCC; border-style:solid; border-width:1px; color:#B8A98F; cursor:default; font-weight:normal; padding:2px 10px; text-align:center;" value="Saving..." name="op" onclick="return false;" />');
  if ('.buttons input:submit') {
    $('.buttons input:submit').keypress(function() {
      $('.buttons input:submit').parents("form").submit();
      $('.buttons input:submit').hide();
    });
  }
});
}
ninjascorner
źródło