Diferenciar refresh (f5) de post(submit)

Salve salve!

Este é um problema que assombra diversos desenvolvedores.
Temos um formulário, que será submetido para mesma página, em que ele se encontra:

	<form action="" method="post">
		<input type="text" name="ae" value="ae" />
		<input type="submit" name="enviar" value="enviar" />
	</form>

Até ai tudo bem. Se pudessemos redirecionar o usuário para outra página, não teríamos o ‘problema do F5’.
Mas se não pudermos fazer isso, o form está na index.php, essa mesma processa os dados enviados, e ela mesma vai mostrar o sucesso ou falha da requisição.

Logo, precisamos notar que não adianta apenas tentar apagar o post enviado:

unset( $_POST );

O unset(), apenas vai liberar memória do servidor, pois quando o usuário apertar F5, quem vai enviar o POST novamente, é o navegador.

Poderíamos ter uma SESSION simples, do tipo: $_SESSION[‘ja_enviado’];
Se ela tivesse com o valor ‘true’, poderíamos desconsiderar a requisição.

Mas, numa situação em que o usuário pode querer realmente enviar novamente o formulário(via submit), essa ‘ja_enviou’ não resolve a questão. Não existe nenhuma diferença nos cabeçalhos da requisição, entre um submit via form, e um refresh que reenvia um submit.

Então, precisamos criar essa diferença, ou seja, distinguir, se a requisição atual, é a mesma da vez passada ou não.

<?php
	session_start();
	
	if( $_SERVER['REQUEST_METHOD']=='POST' )
	{
		$request = md5( implode( $_POST ) );
		
		if( isset( $_SESSION['last_request'] ) && $_SESSION['last_request']== $request )
		{
			echo 'refresh';
		}
		else
		{
			$_SESSION['last_request']  = $request;
			echo 'post';
		}
	}
?>
	<form action="" method="post">
		<input type="text" name="ae" value="ae" />
		<input type="submit" name="enviar" value="enviar" />
	</form>

=) Feito.
Nossa string guardada na session, vai impedir também que requisições iguais sejam enviadas(mesmo que ambas via submit).
O que não chega a ser ruim pois, se o cara já enviou uma vez, e sei lá, gravamos no banco, não tem sentido reescrevermos o mesmo dado de novo no banco, sendo que ele já está lá.

É isso galera.
A referência que pesquisei qndo precisei resolver essa situação está aqui:
http://pt.w3support.net/index.php?db=so&id=456841

Afinal, muito dificilmente seremos os primeiros a nos deparar com uma ‘situação problema’. E já que é assim, provavelmente alguém já o tenha solucionado. Fica a dica: pesquisem! =)

21 Comments

  1. Eu tinha uma situação parecida com essa. Porem eu tinha de ‘renovar’ quando o usuario enviava de novo. Fiz o mesmo raciocínio desse tuto, porem eu não renovava quando era refresh e renovava quando era GET(testar e ver o que envio é tudo!).

    Otimo tutorial.

  2. Muito legal, esse F5 ja me deu muita dor de cabeça. rsrsrs

  3. Jonathan [JCM]

    junho 21, 2011 at 00:10

    Interessante esta tua forma Bruno, porém na minha opinião, o melhor é seguir a pattern Post/Redirect/Get.

    Cumprimentos.

    • Isso que vc citou não é um pattern ^^ mas enfim… esse post é uma solução em ocasiões onde não é feito(por questões não discutiveis) esse comportamento de Redirect.

  4. Evandro Oliveira

    junho 21, 2011 at 03:54

    Não resolve para formulários baseados enviados via GET 😀
    Creio que a melhor solução seja criar tickets/tokens com “prazo de validade” aguardando um submit. Isso resolve outro problema por tabela: O form foi enviado e recebeu um F5. O browser vai re-enviar o token vencido 😉

    • Exatamente, por isso a session.

      Resolve para GET sim, pois o hash se nenhum dado for alterado permanece o mesmo lá na session.

  5. Formulário com ajax é sempre bem + útil.
    Pois evita este tipo de situação.

  6. Muito Obrigado por suas informações, apesar de dar erro naquilo que estava tentando fazer, descobri depois que o comando session_start(); deve aparecer antes de qualquer coisa, inclusive do HTML. Após corrigir isso, funcionou tudo perfeitamente. Parabéns pelo Blog!!

  7. Muito boa a solução, embora eu use algo muito parecido, mas certamente me lembrarei desta forma no próximo sistema que eu for fazer, ou atualizão de algum ja feito, totalmente signed.

    meus parabéns!

    abração!

  8. Jhonatan Morais

    abril 25, 2012 at 11:35

    Cara sua ideia foi genial , funcionou perfeitamente. obrigado!

  9. Boa noite,

    Valeu pela dica, justamente o que eu precisava. funcionou perfeitamente.

    Obrigado!

  10. Manolo, muito boa sua solução, funciona de boa e é muito simples.

    Valeu.

  11. cara usa um array

    $c['nome'] = $_POST['nome'];
    
    if(in_array('',$c){
      echo 'Digite seu nome';
    }else{
      create('tabela', $c);
    }

    pra q tanto código desnecessário

  12. Misael Pereira

    março 31, 2015 at 21:22

    Valeu cara. Tive uma “situação problema” parecida com esta em um sistema com requisições single page em jquery.

    Utilizei a mesma lógica para solucionar. Ignore os “solucionadores de códigos prontos” que chegaram aqui procurando a mesma solução, mas sempre sabem ‘muito’.

  13. ajudou bastante! parabéns pelo tutorial

  14. Muito bom cara! Perfeito o código, sempre fazia um rolê completamente diferente e chato.

  15. Boa Tarde Bruno.
    Muito obrigado por compartilhar sua experiência. Eu utilizei sua ideia em um sistema e resolveu meu problema com duplicidades. Mais uma vez um muito obrigado.

  16. Cara, quase perfeito! rsrs
    Obrigado por compartilhar seu conhecimento (próprio e adquirido). Sempre visito sua página, ótimos conteúdos. Grande abraço!
    coffee++ ;D

  17. Neemias Carvalho

    agosto 11, 2016 at 23:22

    Olá wbruno! Parabéns pelo post! Teria como mostrar a mesma solução em Ajax? Estou tendo dificuldades para me livrar do reenvio de formulário. Só que minha extensão é .cshtml. Obrigado!

Deixe uma resposta

Your email address will not be published.

*