tags:

views:

123

answers:

1

Consider a page webapp/myPage.xhtml:

...
<h:form id="myForm">
    ...
    <h:selectOneMenu id="..." required="true" value="#{myController.aValue}">
       <f:selectItems value="#{...}" var="..." itemValue="#{...}" itemLabel="#{...}"/>
    </h:selectOneMenu>
    ...
    <h:commandButton value="Go for it!" action="#{myController.goForIt(...)}"/>
    ...
</h:form>
...

The button action is bound to a controller method MyController.goForIt():

@ManagedBean(name = "myController")
@RequestScoped
public class MyController {
   public String goForIt(...){
      if (myCondition){
         try {
            FacesContext.getCurrentInstance().getExternalContext()
                        .redirect("http://www.myTarget.com/thePage.html");
         } catch (IOException e) {
           ...
         }
      }
      return "myPage.xhtml"   
   }
}

My first question is: Does the above makes sense? Is this a correct way of using redirect()?

I want to redirect the user to http://www.myTarget.com/thePage.html in case myCondition is true. If myCondition is false, he will have to stay on myPage.xhtml.

If so, I would like to understand better what happens... When using Live HTTP Headers in Firefox, I observe that when clicking the button

  1. a POST to webapp/myPage.xhtml occurs
  2. the server replies with 302 Moved Temporarily - Location: www.myTarget.com/thePage.html
  3. the browser GET's www.myTarget.com/thePage.html

So far, Is this the expected behaviour?

What I find annoying now is that webapp/myPage.xhtml is called. More specifically, that the preRenderView-event is called here again. (I have a bit of code in the preRenderView-listener that should be executed only once.)

Does the above make sense? Does anybody see a way to improve this?

Thank you! J.

+4  A: 

So far, Is this the expected behaviour?

A redirect basically instructs the client to fire a new HTTP request on the URL as specified in the Location response header. So yes, the behaviour as you're seeing is correct.

However, your URL is wrong. It's relative to the current request URL. So if the current URL is for example http://example.com/context/page.jsf, then this redirect will basically point to http://example.com/context/www.myTarget.com/thePage.html which is obviously wrong. When the new URL concerns a different domain, you should redirect to an absolute URL. In other words, prefix it with the http:// scheme.

What I find annoying now is that webapp/myPage.xhtml is called.

This should in theory not happen when the redirect has taken place. Try adding return null to the try block.

BalusC
Thank you BalusC. You're right about the redirect URL. I actually use an absolute URL. I oversimplified my example. (Fixed it now in the original question.)
Jan
Interesting that you say that the preRenderView shouldn't be called again during the redirect. I tried to *return null* after the redirect, but that doesn't really change the behaviour.
Jan
Isn't it just called *before* the redirect and you interpreted/debugged it wrongly? You're namely submitting to the same page and the bean is request scoped.
BalusC
That is true. The preRenderView-listener is called a second time, *before* the redirect should take place. That's easy to confirm as it crashes upon the second call. The flow doesn't even reach the redirect. I wan't clear enough about this?...
Jan
I think I see the confusion. The preRenderView-listener is first called by the initial display of myPage.xhtml. The second call is caused by the POST that preceeds the redirect.
Jan
In theory, making the bean `@ViewScoped` and putting logic in bean's constructor or `@PostConstruct` should achieve what you want.
BalusC
Hmm... thank you for the suggestions! Actually, what I'm doing in the preRenderView-listener, is to persist values coming in from `<f:viewParam>`'s. (It's OK to persist them on the first call, but not on the second.) `@PostConstruct` is too early for this, because the `<f:viewParam>`'s are bound to the backing bean only after that. On the other hand, maybe there is a better alternative to the `preRenderView`-event maybe...
Jan
OK, I see what you mean. Well, maybe you'd like to check [`FacesContext#isPostback()`](http://download.oracle.com/javaee/6/api/javax/faces/context/FacesContext.html#isPostback%28%29) to determine whether it's a postback (POST request) and if not, then persist them.
BalusC
Indeed! That seems to work. You're the best! +1 +1 +1 :-)
Jan
You're welcome.
BalusC
In the mean time, I have learned a bit more. Initially, You were surprised that the preRenderView event was triggered. Indeed, this event isn't always triggered. In `myPage.xhtml`, there is a listbox `<f:selectItems value="#{...}"`. Now, it the value to populate that listbox is a simple reference to a controller attribute, then the preRenderView event is *not* triggered. If the value to populate the listbox is bound to a controller-*method*, then the preRenderView event *is* raised.
Jan
Is there place where all this is documented?
Jan
I'd start with [JSR314, JSF 2.0 specification](http://jcp.org/aboutJava/communityprocess/final/jsr314/index.html).
BalusC
Yeah right :-) On page 64 I read that PreRenderViewEvent is published by RenderResponse Phase. Remains the question why I have a RenderResponse case in one scenario, and not in the other. I think I'll rest my case. Thx again!
Jan
What actually happened: By populating the listbox through a controller-method, instead of a controller-attribute, the list was wrongly populated and the (correct) list-value didn't match the list of valid values. => Validation Error and a direct jump to the RenderResponse phase (with the unexpected preRenderView event as a consequence). If the validation were succesful, the redirect would have happened in the InvokeApplication phase.
Jan