Evitar el reenvío del formulario

107

La página uno contiene un formulario HTML. Página dos: el código que maneja los datos enviados.

Se envía el formulario de la página uno. El navegador se redirige a la página dos. La página dos maneja los datos enviados.

En este punto, si se actualiza la página dos, aparece una alerta de "Confirmar reenvío de formulario".

¿Se puede prevenir esto?

Emanuil Rusev
fuente
Use un cuadro de diálogo de confirmación: stackoverflow.com/questions/6457750/form-confirm-before-submit/…
3
use post redirect get como se describe aquí - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer
Puede evitarlo incluso sin redirección. Mira aquí
Eugen Konkov

Respuestas:

145

Hay 2 enfoques que la gente solía tomar aquí:

Método 1: usar AJAX + Redirect

De esta manera, publica su formulario en segundo plano usando JQuery o algo similar a Page2, mientras el usuario todavía ve la página1 mostrada. Luego de una publicación exitosa, redirige el navegador a la página 2.

Método 2: Publicar + Redirigir a uno mismo

Esta es una técnica común en los foros. El formulario en la página 1 publica los datos en la página 2, la página 2 procesa los datos y hace lo que debe hacerse, y luego realiza una redirección HTTP sobre sí misma. De esta manera, la última "acción" que recuerda el navegador es un simple GET en la página2, por lo que el formulario no se vuelve a enviar en F5.

CodeTwice
fuente
2
Una variante del método 2 es una redirección a otra página. Por ejemplo, PUBLICA en example.com/save.html y una vez que se realiza el guardado, se redirige a example.com/list.html .
Guillaume
1
Con el Método 1, usaría el complemento jQuery llamado BlockUI para que el cliente sepa que algo está sucediendo.
Shikiryu
Estoy usando el método 2 pero me pide que vuelva a enviarlo si lo actualizo. ¿Alguien tiene un problema similar a este?
entusiasta
¿Estás seguro de que la redirección HTTP funciona correctamente? Abra el inspector de red / firebug / lo que tenga su navegador para inspeccionar las solicitudes HTTP salientes y verifique las llamadas HTTP. Debería ver el primero (publicando los datos del formulario) sucediendo con el método POST, devolviendo el código HTTP 301 con el encabezado Location apuntando a sí mismo, y luego inmediatamente debería ver otra consulta HTTP en la misma página con el método GET.
CodeTwice
@CodeTwice: En mi caso, page1 publica en page2, page2 procesa los datos y muestra una página (con valores que se pasan a la plantilla). ¿Dónde debería ocurrir la redirección? .. La página no tiene solicitudes GET.
user1050619
24

Necesita usar PRG - Patrón Post / Redirect / Get y acaba de implementar la P de PRG. Necesitas redireccionar . (Hoy en día no necesita redirección en absoluto. Vea esto )

PRG es un patrón de diseño de desarrollo web que evita algunos envíos de formularios duplicados, lo que significa, Enviar formulario (Publicar solicitud 1) -> Redirigir -> Obtener (Solicitar 2)

Under the hood

Código de estado de redireccionamiento : HTTP 1.0 con HTTP 302 o HTTP 1.1 con HTTP 303

Una respuesta HTTP con código de estado de redireccionamiento proporcionará además una URL en el campo de encabezado de ubicación. El agente de usuario (por ejemplo, un navegador web) es invitado por una respuesta con este código a realizar una segunda solicitud, por lo demás idéntica, a la nueva URL especificada en el campo de ubicación.

El código de estado de redireccionamiento es para garantizar que, en esta situación, el navegador del usuario web pueda actualizar de forma segura la respuesta del servidor sin que se vuelva a enviar la solicitud HTTP POST inicial.

Double Submit Problem

Problema de envío doble

Post/Redirect/Get Solution

Publicar / Redirigir / Obtener solución

Fuente

Angelin Nadar
fuente
2
Buena explicación. Me pregunto qué sucederá si el usuario hace clic en el botón Atrás en el navegador después de que se realiza la redirección. ¿Volverá a aparecer la ventana emergente "Confirmar reenvío de formulario"? Supongo que incluso si la ventana emergente de confirmación no se vuelve a mostrar, se realizará una solicitud de publicación en la página 1 con los mismos datos y, una vez más, el usuario será redirigido a la página 2. Tengo una serie de formularios, form1, form2, form3. cómo se puede volver de la forma "n" a la forma "n-1" sin problemas. Pregunta aleatoria: ¿Dónde estudió el patrón de diseño de PRG
Rpant
@Rpant en Chrome, el archivo php que envía el encabezado de ubicación ni siquiera aparece en el historial del navegador, por lo que el botón Atrás simplemente lo lleva de regreso al formulario.
FluorescentGreen
@Angelin Después de la redirección, ¿cómo obtendrán los datos? Por ejemplo, cuando publico algunos datos en / some_url y redirijo a / some_url, lo que hace una solicitud GET. ¿Cómo sé cuál debería ser la respuesta si / some_url es genérico y no contiene una identificación como / some_url / <id>?
Amit Tripathi
@AmitTripathi tienes que crear tú mismo. Por ejemplo: la solicitud POST podría consistir en insertar datos del cliente, mientras que GET sería la visualización de los datos insertados correctamente. Podrá mostrar los datos como si no fueran más que los datos del formulario o reutilizar la vista de la página del cliente obteniendo de la base de datos para ese cliente la identificación de cliente que recibió después de una inserción exitosa en la base de datos.
Angelin Nadar
Pero estoy de acuerdo con la solución de @Eugen Konkov
Angelin Nadar
10

Directamente, no puede, y eso es bueno. La alerta del navegador está ahí por una razón. Este hilo debería responder a tu pregunta:

Evitar que el botón Atrás muestre una alerta de confirmación POST

Se sugirieron dos soluciones alternativas clave: el patrón PRG y un envío AJAX seguido de una reubicación de secuencias de comandos.

Tenga en cuenta que si su método permite un método de envío GET y no POST, entonces eso resolvería el problema y encajaría mejor con la convención. Esas soluciones se proporcionan bajo el supuesto de que desea / necesita POST datos.

davin
fuente
8

La única forma de estar 100% seguro de que el mismo formulario nunca se envía dos veces es incrustar un identificador único en cada uno que emita y rastrear cuáles se han enviado al servidor. La trampa es que si el usuario retrocede a la página donde estaba el formulario e ingresa nuevos datos, el mismo formulario no funcionará.

Blrfl
fuente
6

La respuesta consta de dos partes:

  1. Asegúrese de que las publicaciones duplicadas no alteren sus datos en el lado del servidor. Para hacer esto, inserte un identificador único en la publicación para que pueda rechazar las solicitudes posteriores del lado del servidor. Este patrón se denomina Receptor idempotente en términos de mensajería.

  2. Asegúrese de que al usuario no le moleste la posibilidad de envíos duplicados de ambos

    • redirigir a un GET después del POST (POST redirecciona el patrón GET)
    • deshabilitar el botón usando javascript

Nada de lo que haga en 2. evitará totalmente envíos duplicados. La gente puede hacer clic muy rápido y los piratas informáticos pueden publicar de todos modos. Siempre necesita 1. si quiere estar absolutamente seguro de que no hay duplicados.

iwein
fuente
2

Si actualiza una página con datos POST, el navegador confirmará su reenvío. Si usa GET data, el mensaje no se mostrará. También puede hacer que la segunda página, después de guardar el envío, redirija a una tercera página sin datos.

Joel
fuente
2

Bueno, encontré que nadie mencionó este truco.

Sin la redirección, aún puede evitar la confirmación del formulario cuando se actualiza.

Por defecto, el código del formulario es así:

<form method="post" action="test.php">

ahora, cámbialo a <form method="post" action="test.php?nonsense=1">

Verás la magia.

Supongo que es porque los navegadores no activarán la ventana emergente de alerta de confirmación si obtiene un método GET (cadena de consulta) en la URL.

Realce
fuente
2

Puedes hacer esto usando jquery

<script>
   $(document).ready(function(){
   window.history.replaceState('','',window.location.href)
   });
</script>

Esta es la forma más elegante de evitar que los datos vuelvan a enviarse debido a la devolución.

Espero que esto ayude.

noobprogrammer
fuente
0

El patrón PRG solo puede evitar el reenvío causado por la actualización de la página. Esta no es una medida 100% segura.

Por lo general, tomaré las siguientes medidas para evitar que se vuelvan a enviar:

  1. Lado del cliente: use javascript para evitar clics duplicados en un botón que activará el envío de formularios. Puede deshabilitar el botón después del primer clic.

  2. Lado del servidor: calcularé un hash en los parámetros enviados y guardaré ese hash en la sesión o la base de datos, de modo que cuando se reciba el envío duplicado podamos detectar la duplicación y luego la respuesta adecuada al cliente. Sin embargo, puede lograr generar un hash en el lado del cliente.

En la mayoría de las ocasiones, estas medidas pueden ayudar a prevenir el reenvío.

ehe888
fuente
0

Realmente me gusta la respuesta de @ Angelin. Pero si está tratando con algún código heredado donde esto no es práctico, esta técnica podría funcionar para usted.

En la parte superior del archivo

// Protect against resubmits
if (empty($_POST))  {
   $_POST['last_pos_sub'] = time();
} else {
     if (isset($_POST['last_pos_sub'])){
        if ($_POST['last_pos_sub'] == $_SESSION['curr_pos_sub']) {
           redirect back to the file so POST data is not preserved
        }
        $_SESSION['curr_pos_sub'] = $_POST['last_pos_sub'];
     }
}

Luego, al final del formulario, adhiérase de la last_pos_subsiguiente manera:

<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>
Scott C Wilson
fuente
0

Prueba tris:

function prevent_multi_submit($excl = "validator") {
    $string = "";
    foreach ($_POST as $key => $val) {
    // this test is to exclude a single variable, f.e. a captcha value
    if ($key != $excl) {
        $string .= $key . $val;
    }
    }
    if (isset($_SESSION['last'])) {
    if ($_SESSION['last'] === md5($string)) {
        return false;
    } else {
        $_SESSION['last'] = md5($string);
        return true;
    }
    } else {
    $_SESSION['last'] = md5($string);
    return true;
    }
}

Cómo usar / ejemplo:

if (isset($_POST)) {
    if ($_POST['field'] != "") { // place here the form validation and other controls
    if (prevent_multi_submit()) { // use the function before you call the database or etc
        mysql_query("INSERT INTO table..."); // or send a mail like...
        mail($mailto, $sub, $body); // etc
    } else {
        echo "The form is already processed";
    }
    } else {
    // your error about invalid fields
    }
}

Fuente: https://www.tutdepot.com/prevent-multiple-form-submission/

Rômulo ZC Cunha
fuente
0

use js para evitar agregar datos:

if ( window.history.replaceState ) {
    window.history.replaceState( null, null, window.location.href );
}
Tran Anh Hien
fuente