Repitam comigo: Redirect não é forward

É engraçado o número de desenvolvedores que se utilizam da tag de navegação <redirect/> quando configuram suas regras de navegação no faces-config.xml sem entender o porquê de sua finalidade, na maioria das vezes a única coisa que eles tem conhecimento -e acreditam que esta é finalidade da tag- é que ao utiliza-la a url na barra de endereços do browser muda.

Depois disso eles não entendem porque os valores não existem mais no managed bean ou porque as mensagens de erro não são mais exibidas ao usuário ou mesmo porque uma nova instância do managed bean foi criada, enfim, eles não entendem porque afinal a aplicação parou de funcionar!

Tentarei com este post esclarer a diferença entre um redirect e um forward e como contornar o problema acima para que novos desenvolvedores não caiam em “maus lençóis”.

Redirect não é forward

Um servlet (controller) pode executar tanto uma operação de um redirect como de um forward no final do processamento de uma requisição, e isso influencia como as páginas no browser serão recarregadas. É importante que um desenvolvedor entenda os fundamentos por trás de uma aplicação web, e um deles é a diferença entre redirect e um forward.

Segue abaixo a diferença entre eles:

Forward

  • É executado internamente pelo servlet (controller);
  • O browser não sabe o que está ocorrendo durante o processamento no servidor, ou seja, não sabe por quais servlets ou páginas a requisição está passando;
  • No final do processamento da requisição a url da barra de endereços do browser não muda;
  • O reload da página resultante irá executar a requisição original;

Redirect

  • É um processo de dois passos, ao receber uma requisição a aplicação web “pede” ao browser para acessar uma segunda url, por isso a url muda;
  • O reload de página não repetirá a requisição original, mas sim a nova url (2ª requisição);
  • É um processo muito mais lento que um forward, pois são necessárias duas requisições, e não uma;
  • Objetos colocados no escopo do request original são perdidos durante o segundo request;

Resumindo, um redirect é uma nova requisição que o cliente (browser) faz a pedido da aplicação web, logo ele fica ciente sobre como está ocorrendo a navegação e para onde ele está sendo redirecionado, enquanto um forward pode executar várias requisições no lado servidor sem o conhecimento do cliente e no final retornar uma página qualquer. Atentem também que um forward mantém os atributos e parâmetros do request original, já um redirect não.

Como podem ver, de maneira sútil eles fazem a mesma coisa, mas são bem diferentes.

O problema

Vamos observar um caso clássico abaixo:

Imagine que temos um managed bean configurado em escopo de request e que temos uma regra de navegação no nosso faces-config.xml na qual explicitamos o uso de redirect através da tag <redirect/>:

<navigation-rule>
	<from-view-id>/pages/pageX.jsp</from-view-id>
	<navigation-case>
		<from-outcome>nova_pagina_com_redirect</from-outcome>
		<to-view-id>/pages/pageY.jsp</to-view-id>
		<redirect />
	</navigation-case>
</navigation-rule>

E em uma determinada página X o usuário clica em um botão que executa um método no managed bean e depois disso o usuário é enviado para uma outra página Y com uma mensagem de sucesso (ou erro) e alguns objetos populados no managed bean para serem exibidos, logo teríamos um método no managed bean semelhante a isso:

public String submit() {
	// processa algo
	BacalhauService.processaRequisicao();

	// popula atributo do managed bean
	this.att1 = "A ligeira raposa marrom saltou sobre o cão preguiçoso.";

	// adiciona mensagem de sucesso
	FacesContext ctx = FacesContext.getCurrentInstance();
	ctx.addMessage(null, new FacesMessage("Operação concluída com sucesso."));

	return "nova_pagina_com_redirect";
}

Imagine que quando o usuário executou a ação o processamento no método do managed bean ocorreu, a navageção para a página Y funcionou perfeitamente e a url na barra de endereços do browser mudou como previsto, mas espere um pouco! “What the hell is this?” A mensagem de sucesso e os dados contidos no managed bean não foram exibidos, onde elas foram parar afinal de contas?

No caso acima nós navegamos da página X para a página Y através de um redirect (lembram da tag <redirect/> na regra de navegação?), e já sabemos que quando há um redirect todos os dados no escopo de request são perdidos, logo o nosso managed bean e nossa mensagem de sucesso foram perdidos.

Agora que já entendemos o funcionamento do redirect não foi uma surpresa o managed bean ter sido perdido (reinstanciado) pois sabiamos que ele estava configurado com escopo de request, mas e a mensagem de sucesso? Bem, isso fica óbvio ao saber que as mensagens do JSF são adicionadas no escopo de request, mesmo que seu managed bean esteja noutro escopo.

A solução

Como resolver isso? Bem, existem algumas soluções, porém a mais simples sem dúvida é remover a tag <redirect/> da regra de navegação, assim a navegação entre páginas ocorrerá através de forward (que é o default do framework), e os dados do seu managed bean e mensagens serão exibidos ao usuário como esperado.

Na maioria das aplicações web um dos motivos para se utilizar um redirect é para evitar que no reload/refresh da página ocorra uma resubmissão do form, evitando-se assim resultados inesperados na aplicação, sendo, uma solução para isso seria a utilização do Post-Redirect-Get Pattern (PRG). Uma excelente solução é a implementação do padrão PRG através deste Phase Listener implementado pelo BalusC.

Não tenho certeza, mas acho (quase certeza) que o JBoss Seam já tem algo implemetado para solucionar o problema. Se alguém puder confirmar, eu ficaria grato 🙂

Felizmente hoje em dia as aplicações web estão caminhando para uma GUI mais rica e versátil, e ao abrir mão da “velha escola” conseguimos contornar os problemas citados acima facilmente, como também obter inúmeras outras vantagens.

Concluindo

Como podem ver, o problema não era do JavaServer Faces (JSF) ou mesmo de qualquer outro framework MVC, mas certamente da falta de fundamentos sobre a web. Espero sinceramente que os desenvolvedores busquem entender os fundamentos dos frameworks, padrões e da web antes de desenvolver uma aplicação web, pois utilizar algo nas cegas provavelmente trará vários problemas mais cedo ou mais tarde.

Enfim, este é um problema decorrente na lista de discussão do javasf, e sempre acabamos por explicar a diferença entre as duas operações, que no final resolve-se apenas seguindo o caminho mais simples. Fico aqui e espero que este post tenha servido de ajuda a muitos desenvolvedores.

34 Replies to “Repitam comigo: Redirect não é forward”

  1. Boa Ponte :). Rapaz estou gostando de vê ;). Já está se garantindo nos posts eim.

    Muito bom mesmo, já está aprendendo a “escrever” tutoriais com uma boa explicação e didática :).

    Parabéns cara 😉 abraços.

  2. Bom post Rafael. Realmente a confusão é grande nesse assunto.

  3. Olá, Rafael, estou tendo um problema com as regras de navegação na minha aplicação. No Firefox e Chrome as regras no meu faces-config.xml funcionam, porém, quando executo no IE7 (somente a versão atualizada, sem os updates também funciona) o redirecionamento de páginas não é executado. A única maneira que consegui fazer funcionar foi incluindo o redirect no xml, porém isso acabou me trazendo outros problemas, então resolvi tirá-lo. Vi num fórum que você teve um problema como esse. Tem como você me passar o que é preciso ser feito para a navegação funcionar no IE7?

    Obrigada

  4. Ólá Priscilla,

    Eu tive esse problema faz um bom tempo mesmo. Pelo que me lembro o problema foi decorrido ao IE7 não se utilizar de AJAX nativo por default [ou era o contrário]. Logo, bastava algumas configurações no navegador [que também não me recordo] e a navegação funcionava corretamente.

    Acredito que hoje em dia já se tenha uma solução mais apropriada do que configurar explicitamente o navegador, na qual é realmente inviável para os desenvolvedores e principalmente para o usuário.

    Abraços e boa sorte.

  5. Belo post Rafael,

    Tenho o projeto que utiliza JSF+Facelets. Apenas consegui resolver o problema de navegação, utilizando o redirect na regra de navegação.

    Vc tem alguma outra solução?

    Obrigado!

  6. Rafael,

    Tenho uma tela de listagem e edição de uma pessoa, por exemplo. Elas herdam o cabeçalho e menu. Quando navego entre a tela de listagem e edição, o menu, por exemplo, para de funcionar.

  7. Parabéns, Rafael. Ótima explicação.

    Um grande abraço.

  8. Quando optei por não utilizar o redirect, algumas actions minhas pararm de funcionar. Estranho.

  9. Legal amigo, mas acho de grande valia citar os autores e fontes.
    um abraço

  10. Olá Rafael. Muito bom seu artigo, que além de fazer uma versão em português das diferenças apresentadas no tópico “Forward versus redirect” do Java Practices, ainda ilustrou muito bem o tratamento do problema em JavaServer Faces.

    Cito seu artigo nas referências de um artigo meu de coletânea de informações úteis relacionadas a cabeçalhos HTTP e tipos MIME, no tópico sobre redirecionamento. Fiz duas figuras que, creio, ilustram bem a diferença entre Redirect e Forward:
    http://www.mhavila.com.br/topicos/web/http_mime.html

    Parabéns pelo blog!

  11. Olá,

    É um tópico de 4 meses atrás mas eu recentemente passei por esta situação. Usando JBossRichFaces, o comportamento de chamadas Ajax precisa do redirect no faces-config.xml, o que acarreta os problemas que você mencionou. Especificamente:

    * Se há um botão a4j:commandButton que chama uma action de seu ManagedBean, E
    * Essa action retorna uma string que casa com algum navigation-case do seu faces-config.xml, E
    * O resultado desse navigation-case não é um redirect,

    ENTÃO o IE7 não “entende” o forward e fica na mesma tela. O Firefox3, por exemplo, entende e vai para a nova tela. (no nosso caso acredito que o problema é a biblioteca ajax A4J, mas pode ser que seja o navegador mesmo, como você mencionou).

    Adicionando a tag redirect no navigation-case, ambos os browsers funcionam, mas sendo um novo request, as mensagens de erro ou sucesso realmente se perdem.

    (Isso acontece também com erros de sessão expirada: uma chamada A4J no Firefox com uma sessão inválida vai corretamente para a tela de erro, enquanto que no IE7 nada acontece).

    Curioso que o guia de uso do JBossRichFaces recomenda o uso do redirect, provavelmente para resolver exatamente o problema acima, mas eles também admitem que isso faz as mensagens sumir. A solução no meu caso foi guardar as mensagens na sessão e “consumi-las” conforme elas eram exibidas para o usuário. Não é uma solução elegante mas funcionou…

    Parabéns pelo post!

    []’s

  12. Olá Rafael estou tendo problema com a tag redirect em um sistema na empresa em que trabalho o colega que a colocou me disse que sem ela nao funciona o https corretamente, porém há a perda de mensgens de erro. Retirando a tag as telas que necessitam do https aparecem sem o https o que necessita dá um refresh na tela para entrar o https. Li sobre o PRG mas ao implementar a solução do BalusC no sistema aconteceu alguns erros de perda de informações. Você poderia me ajudar neste caso?

    Obrigado pelo post que já me ajudou bastante a entender o erro que tinha.

  13. Olá Sidnei,

    Não tenho muito experiência com Https, mas ainda assim acho que seria interessante você explicitar melhor que dados você está tentando manter após um redirect e que erros ocorrem ao usar o PRG.

    Um abraço.

  14. Olá Rafael, desta vez resolvi postar, sou iniciante em JSF e sempre venho parar aqui, realmente não foi a primeira vez que eu procurava por algo relativamente simples e que talvez por este mesmo motivo não achava em lugar algum. Pois bem, meu redirect não funcionava e o que falta? Um simples . Totalmente útil o post, parabéns e obrigado.

  15. Bah! agora são 04:51 da manhã e não resisti para agradecer ….valeu cara, passsei a madrugada procurando um post desses. é o cara.
    Att,
    Luciano

  16. Excelente post, eu sempre confundia os 2 mto obrigado deu uma clareada legal aqui. vlw

  17. Boa noite, Rafale tudo bem Amigo? ótimo poste por sinal, bem estou desenvolvendo uma app com jsf 2 e gostaria de saber como eu faço para que minhas url (que apresentam no browser) sejam mais amigáveis e principalmente não deixe que um “mau feitor” tenha acesso direto alterando a url diretamente no browser, desde já agradeço a sua atenção, abraço.

  18. Oi Carlos,

    Não entendi bem o que você quer ou de qual problema você fala. Está falando das urls seguirem o mesmo caminho dos arquivos .xhtml?

  19. Rafael, o jsf 2.0 permite adicionar no facesconfig “” certo?
    Isso então resolve o problema descrito em 2008 por você correto?
    Esse artigo foi descrito baseado no jsf 1.0?

  20. ‘redirect include-view-params=true’

  21. Oi Maycon,

    Eu citei alguns problemas no post, entre eles as mensagens e beans/objetos em escopo de request que são descartados após um redirect. De qual deles você está falando exatamente?

    O que você citou somente garante que os parâmetros que estão na URL serão mantidos. Mas os objetos em request e as mensagens ainda são descartados. Para mantê-los entre requisições é necessário utilizar o escopo Flash.

    O post é de 2008 e na época eu usei o JSF 1.2. A versão 1.0 do JSF nem conta, foi uma catastrofe e provavelmente nem chegou a ser utilizada em produção pelas empresas. Somente a partir do JSF 1.1 é que tivemos uma versão minimamente estável.

Deixe um comentário