Impedindo o reenvio do formulário

107

A página um contém um formulário HTML. Página dois - o código que lida com os dados enviados.

O formulário na página um é enviado. O navegador é redirecionado para a página dois. A página dois trata dos dados enviados.

Nesse ponto, se a página dois for atualizada, um alerta "Confirmar reenvio de formulário" aparecerá.

Isso pode ser evitado?

Emanuil Rusev
fonte
Use uma caixa de diálogo de confirmação: stackoverflow.com/questions/6457750/form-confirm-before-submit/…
3
use post redirect get conforme descrito aqui - >> en.wikipedia.org/wiki/Post/Redirect/Get
Meer,
Você pode evitar isso mesmo sem redirecionamento. Olhe aqui
Eugen Konkov

Respostas:

145

Existem 2 abordagens que as pessoas costumam adotar aqui:

Método 1: Use AJAX + Redirect

Desta forma, você publica seu formulário em segundo plano usando JQuery ou algo semelhante a Page2, enquanto o usuário ainda vê a page1 exibida. Após a postagem bem-sucedida, você redireciona o navegador para a Página2.

Método 2: Post + Redirecionar para si mesmo

Esta é uma técnica comum em fóruns. O formulário na Página1 envia os dados para a Página2, a Página2 processa os dados e faz o que precisa ser feito e, em seguida, faz um redirecionamento HTTP para si mesma. Desta forma, a última "ação" que o navegador lembra é um simples GET na página 2, de modo que o formulário não está sendo reenviado em F5.

CodeTwice
fonte
2
Uma variante do método 2 é um redirecionamento para outra página. Por exemplo, você faz POST em example.com/save.html e quando o salvamento é feito, você redireciona para example.com/list.html .
Guillaume,
1
Com o Método 1, eu usaria o plugin jQuery chamado BlockUI para permitir que o cliente soubesse que algo está acontecendo.
Shikiryu,
Estou usando o método 2, mas ele está me pedindo para reenviar novamente se eu atualizá-lo. Alguém está tendo um problema semelhante a este?
interessado em
Tem certeza de que o redirecionamento HTTP está funcionando corretamente? Abra o inspetor de rede / firebug / o que quer que seu navegador tenha para inspecionar solicitações HTTP de saída e verifique as chamadas HTTP. Você deve ver o primeiro (postar os dados do formulário) acontecendo com o método POST, retornando o código HTTP 301 com o cabeçalho Location apontando para ele mesmo, e então imediatamente você deve ver outra consulta HTTP para a mesma página com o método GET.
Código Duas vezes
@CodeTwice: No meu caso, page1 posta para page2, page2 processa os dados e mostra uma página (com valores sendo passados ​​para o template) .. Onde o redirecionamento deve acontecer? .. A página não tem requisições GET.
user1050619
24

Você precisa usar o padrão PRG - Post / Redirect / Get e acabou de implementar o P do PRG. Você precisa redirecionar . (Agora dias você não precisa de redirecionamento. Veja isto )

PRG é um padrão de design de desenvolvimento da web que evita alguns envios de formulários duplicados, o que significa: Enviar formulário (Pós-solicitação 1) -> Redirecionar -> Obter (Solicitação 2)

Under the hood

Código de status de redirecionamento - HTTP 1.0 com HTTP 302 ou HTTP 1.1 com HTTP 303

Uma resposta HTTP com código de status de redirecionamento fornecerá adicionalmente um URL no campo do cabeçalho do local. O agente do usuário (por exemplo, um navegador da web) é convidado por uma resposta com este código a fazer uma segunda solicitação, de outra forma idêntica, para o novo URL especificado no campo de localização.

O código de status de redirecionamento é para garantir que, nessa situação, o navegador do usuário da web possa atualizar com segurança a resposta do servidor sem fazer com que a solicitação HTTP POST inicial seja reenviada.

Double Submit Problem

Problema de envio duplo

Post/Redirect/Get Solution

Publicar / redirecionar / obter solução

Fonte

Angelin Nadar
fonte
2
Boa explicação. Estou imaginando o que acontecerá se o usuário clicar no botão Voltar do navegador após o redirecionamento. O pop-up "confirmar reenvio do formulário" será exibido novamente. Eu acho que mesmo que o popup de confirmação não apareça novamente, uma solicitação de postagem na página 1 com os mesmos dados será feita novamente e mais uma vez o usuário será redirecionado para a página 2. Eu tenho uma série de formulários, form1, form2, form3. como pode voltar do formulário "n" para o formulário "n-1" perfeitamente. Pergunta aleatória: onde você estudou o padrão de design PRG
Rpant,
@Rpant no chrome, o arquivo php que envia o cabeçalho da localização nem mesmo aparece no histórico do navegador, então o botão Voltar simplesmente o leva de volta ao formulário.
FluorescentGreen5
@Angelin Após o redirecionamento, como obterá os dados? Por exemplo, quando eu posto alguns dados em / some_url e redireciono para / some_url que faz uma solicitação GET. Como posso saber qual deve ser a resposta, visto que / some_url é genérico e não contém um id como / some_url / <id>?
Amit Tripathi
@AmitTripathi você tem que criar você mesmo. Ex: a solicitação POST pode ser de inserção de dados do cliente enquanto GET seria a exibição dos dados inseridos com sucesso. Você será capaz de mostrar os dados como nada mais eram do que os dados do formulário ou reutilizar a visualização da página do cliente, obtendo do banco de dados para esse cliente o ID do cliente que você recebeu após a inserção bem-sucedida no banco de dados.
Angelin Nadar
Mas concordo com a solução @Eugen Konkov
Angelin Nadar
10

Diretamente, você não pode, e isso é uma coisa boa. O alerta do navegador existe por um motivo. Este tópico deve responder à sua pergunta:

Impedir que o botão Voltar exiba um alerta de confirmação do POST

Duas soluções alternativas principais sugeridas foram o padrão PRG e um envio AJAX seguido por uma realocação de script.

Observe que, se o seu método permitir um método de envio GET e não POST, isso resolveria o problema e se ajustaria melhor à convenção. Essas soluções são fornecidas no pressuposto de que você deseja / precisa POSTAR os dados.

Davin
fonte
8

A única maneira de ter 100% de certeza de que o mesmo formulário nunca será enviado duas vezes é incorporar um identificador exclusivo em cada um deles e rastrear quais foram enviados ao servidor. A armadilha é que, se o usuário voltar para a página onde estava o formulário e inserir novos dados, o mesmo formulário não funcionará.

Blrfl
fonte
6

A resposta tem duas partes:

  1. Certifique-se de que as postagens duplicadas não mexam com seus dados no servidor. Para fazer isso, incorpore um identificador exclusivo na postagem para que você possa rejeitar solicitações subsequentes do lado do servidor. Este padrão é chamado de Receptor Idempotente em termos de mensagens.

  2. Certifique-se de que o usuário não se incomode com a possibilidade de envios duplicados por ambos

    • redirecionando para um GET após o POST (padrão de redirecionamento POST GET)
    • desabilitando o botão usando javascript

Nada que você faça abaixo de 2. impedirá totalmente envios duplicados. As pessoas podem clicar muito rápido e os hackers podem postar de qualquer maneira. Você sempre precisa de 1. se quiser ter certeza absoluta de que não há duplicatas.

Iwein
fonte
2

Se você atualizar uma página com dados POST, o navegador confirmará seu reenvio. Se você usar dados GET, a mensagem não será exibida. Você também pode fazer com que a segunda página, depois de salvar o envio, redirecione para uma terceira página sem dados.

Joel
fonte
2

Bem, eu descobri que ninguém mencionou esse truque.

Sem o redirecionamento, você ainda pode impedir a confirmação do formulário ao atualizar.

Por padrão, o código do formulário é assim:

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

agora, mude para <form method="post" action="test.php?nonsense=1">

Você verá a magia.

Eu acho que é porque os navegadores não irão acionar o pop-up de alerta de confirmação se ele obtiver um método GET (string de consulta) na url.

Realçar
fonte
2

Você pode fazer isso usando jquery

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

Esta é a maneira mais elegante de evitar dados novamente após o envio devido à postagem de volta.

Espero que isto ajude.

noobprogrammer
fonte
0

O padrão PRG só pode impedir o reenvio causado pela atualização da página. Esta não é uma medida 100% segura.

Normalmente, realizarei as ações abaixo para evitar o reenvio:

  1. Cliente - Use javascript para evitar cliques duplicados em um botão que irá acionar o envio do formulário. Você pode simplesmente desativar o botão após o primeiro clique.

  2. Lado do servidor - Vou calcular um hash nos parâmetros enviados e salvá-lo na sessão ou banco de dados, para que quando o envio duplicado for recebido, possamos detectar a duplicação e, em seguida, responder corretamente ao cliente. No entanto, você pode conseguir gerar um hash no lado do cliente.

Na maioria das vezes, essas medidas podem ajudar a prevenir a reapresentação.

ehe888
fonte
0

Eu realmente gosto da resposta de @Angelin. Mas se você estiver lidando com algum código legado em que isso não seja prático, essa técnica pode funcionar para você.

No topo do arquivo

// 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'];
     }
}

Então, no final do formulário, insira da last_pos_subseguinte maneira:

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

Experimente 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;
    }
}

Como usar / exemplo:

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
    }
}

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

Rômulo ZC Cunha
fonte
0

use js para evitar adicionar dados:

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