What is the difference between redirect and navigation/forward and when to use what?
Asked Answered
B

1

35

What is difference between a navigation in JSF

FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().getNavigationHandler().handleNavigation(context, null, url);

and a redirect

HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.sendRedirect(url);

and how to decide when to use what?

The issue with navigation is that page URL does not change unless faces-redirect=true is added to the query string of the navigation URL. However, in my case appending faces-redirect=true throws error if I want to redirect to a non-JSF page like a plain HTML page.

And another option is as BalusC suggested at JSF 2.0 redirect error

Brumley answered 30/6, 2012 at 20:19 Comment(0)
S
84

First of all, the term "redirect" is in web development world the action of sending the client an empty HTTP response with just a Location header with therein the new URL on which the client has to send a brand new GET request. So basically:

  • Client sends a HTTP request to somepage.xhtml.
  • Server sends a HTTP response back with Location: newpage.xhtml header
  • Client sends a HTTP request to newpage.xhtml (this get reflected in browser address bar!)
  • Server sends a HTTP response back with content of newpage.xhtml.

You can track it with the webbrowser's builtin/addon developer toolset. Press F12 in Chrome/IE9/Firebug and check the "Network" section to see it.

The JSF navigationhandler doesn't send a redirect. Instead, it uses the content of the target page as HTTP response.

  • Client sends a HTTP request to somepage.xhtml.
  • Server sends a HTTP response back with content of newpage.xhtml.

However as the original HTTP request was to somepage.xhtml, the URL in browser address bar remains unchanged. If you are familiar with the basic Servlet API, then you should understand that this has the same effect as RequestDispatcher#forward().


As to whether pulling the HttpServletResponse from under the JSF hoods and calling sendRedirect() on it is the proper usage; no, that isn't the proper usage. Your server logs will get cluttered with IllegalStateExceptions because this way you aren't telling JSF that you've already taken over the control of the response handling and thus JSF shouldn't do its default response handling job. You should in fact be executing FacesContext#responseComplete() afterwards.

Also, everytime whenever you need to import something from javax.servlet.* package in a JSF artifact like a managed bean, you should absolutely stop writing code and think twice if you're really doing things the right way and ask yourself if there isn't already a "standard JSF way" for whatever you're trying to achieve and/or if the task really belongs in a JSF managed bean (there are namely some cases wherein a simple servlet filter would have been a better place).

The proper way of performing a redirect in JSF is using faces-redirect=true query string in the action outcome:

public String submit() {
    // ...
    return "/newpage.xhtml?faces-redirect=true";
}

Or using ExternalContext#redirect() when you're not inside an action method such as an ajax or prerender listener method:

public void listener() throws IOException {
    // ...
    ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
    ec.redirect(ec.getRequestContextPath() + "/newpage.xhtml");
}

(yes, you do not need to put a try-catch around it on IOException, just let the exception go through throws, the servletcontainer will handle it)

Or using NavigationHandler#handleNavigation() in specific cases if you're using XML navigation cases and/or a custom navigation handler with some builtin listener:

public void listener() {
    // ...
    FacesContext fc = FacesContext.getCurrentInstance();
    NavigationHandler nh = fc.getApplication().getNavigationHandler();
    nh.handleNavigation(fc, null, "/newpage.xhtml?faces-redirect=true");
}

As to why the navigation handler fails for "plain HTML" files, that is simply because the navigation handler can process JSF views only, not other files. You should be using ExternalContext#redirect() then.

See also:

Socio answered 30/6, 2012 at 20:36 Comment(5)
Thanks Balu for elaborate explanation. I am new to JSF and this helps me understand the nuances. Also referred your comment on the link stackoverflow.com/questions/9458911/… - adding an empty string does return me to the starting page but also displays error message as invalid handler found. I cannot use faces-redirect=true here since i want to return some message back to the start page. Any pointers?Brumley
If the redirect is on the same path, then you could use Flash#setKeepMessages() to keep all FacesMessages in the flash scope.Socio
Thanks BalusC, you have just saved me from suicide by Ajax. I have been fighting un-responsive buttons for months on two different projects. The new pages, when launched by JSF, were having all of their events pre-consumed unless a random selection of command buttons had ajax="false" liberally scattered about. Having re-read your reply to this post, it occurred to me to try the re-direct to clear the form workspace inside JSF. It worked an absolute treat both in page and from backing bean actions. The downside is that I now have to re-visit all of my code.Ciapha
I think there is a typo here: "Client sends a HTTP request to somepage.xhtml. Server sends a HTTP response back with content of newpage.xhtml. " You sent request to "Somepage.xhtml" but got the content of "newpage.xhtml", So, am i right? or there is something i am missing =?Collbaith
@Muntaser: nope, no typo. The statement "You sent request to "somepage.xhtml" but got the content of "newpage.xhtml"" is correct. If you're still not understanding it, take a step back and learn basic Servlets first before diving deeper into JSF.Socio

© 2022 - 2024 — McMap. All rights reserved.