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.
Tags: faces-config.xml, foward, fundamentos, Java, JavaServer Faces, Javasf, JEE, JSF, navigation, pattern, post, post-redirect-get, prg, redirect, rule, tag, web
July 15th, 2008 at 6:49 am
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.
September 20th, 2008 at 9:08 am
Bom post Rafael. Realmente a confusão é grande nesse assunto.
September 28th, 2008 at 8:34 pm
[...] simples de não dar um forward após a inclusão do dado. Acabei caindo numa boa explicação do Rafael Pontes sobre a diferença entre o forward e [...]
November 14th, 2008 at 12:11 pm
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
November 16th, 2008 at 3:28 pm
Ó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.
November 27th, 2008 at 10:01 am
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!
November 29th, 2008 at 6:49 am
Que problema de navegação você se refere exatamente?
December 5th, 2008 at 6:12 am
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.
December 10th, 2008 at 9:42 am
Parabéns, Rafael. Ótima explicação.
Um grande abraço.
December 10th, 2008 at 10:33 am
Quando optei por não utilizar o redirect, algumas actions minhas pararm de funcionar. Estranho.
January 16th, 2009 at 5:31 am
Muito bom Rafael, com certeza este post irá ajudar muitos desenvolvedores.
January 19th, 2009 at 12:59 pm
Legal amigo, mas acho de grande valia citar os autores e fontes.
um abraço
February 10th, 2009 at 9:42 am
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!
February 10th, 2009 at 9:48 am
[...] referências do tópico, há o artigo em português de Rafael Ponte e o tópico em Java Practices, com mais informações a respeito do tema. [...]
June 17th, 2009 at 1:46 pm
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
December 7th, 2009 at 4:27 am
[...] Para maiores informações sobre a diferença dos dois tipos leia este artigo do Rafael Ponte. [...]
December 16th, 2009 at 4:57 am
[...] maiores informações sobre a diferença dos dois tipos leia este artigo do Rafael Ponte. [...]