PDA

View Full Version : How can I apply Post-Redirect-Get pattern in SWF?



slhynju
Jun 15th, 2005, 08:58 PM
A typical wizard, user submits the 1st form, and 2nd page is displayed.
With current SWF implementation, a transition is triggered, some actions are executed and finally the 2nd view-state is rendered. All these are done in server-side in a forward-like style.
So, if the user clicks the "refresh" button in the 2nd page, browser will popup a warning dialog, which is not acceptable by our customer.
Ideally the Post-Redriect-Get solution can solve this problem, but how can I apply the pattern in the SWF without breaking the flow?

Keith Donald
Jun 15th, 2005, 10:34 PM
You can do a redirect in a end-state -- I believe the birthdate sample illustrates this.

Keith

slhynju
Jun 16th, 2005, 12:27 AM
I did redirect in the end-state. However, what customer expected is not only the final result page can be refreshed, but also the forms during the wizard. For example, a typical wizard in my project contains 3-4 pages, and the user may try to refresh the 2nd form page, before s/he finishes the whole flow. In this case, a browser warning dialogue will popup since the 2nd page is a response to POST request. And our customer don't like that dialogue.

I know the requirement seems meanless.
But customer is always correct.

klr8
Jun 17th, 2005, 03:50 PM
Well, one way to do it is use GET when submitting the form (to avoid the warning displayed by the browser when the form is refreshed) and then using continuations to make sure the flow keeps working in all situations when you refresh (check the sellitem sample app for an example).

So far we haven't really been able to find an elegant and generic way to support post-redirect-get with SWF. There has been quite a bit of discussion on this topic, also here on the forum, but so far it's not yet supported.

Erwin

slhynju
Jun 20th, 2005, 08:49 PM
Here is my solution:



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE webflow PUBLIC "-//SPRING//DTD WEBFLOW//EN"
"http&#58;//www.springframework.org/dtd/spring-webflow.dtd">

<webflow id="makePaymentFlow" start-state="setupForm">

<action-state id="setupForm">
<action bean="makePaymentFormAction"/>
<transition on="success" to="formPage"/>
</action-state>

<view-state id="formPage" view="redirect&#58;/epay/make_payment_flow.form?_eventId=get">
<transition on="get" to="formPageRedirectAction"/>
</view-state>


<action-state id="formPageRedirectAction">
<action bean="makePaymentFormAction" method="redirect"></action>
<transition on="success" to="formPageInternal"/>
</action-state>

<view-state id="formPageInternal" view="eautopay/make_payment_flow_step_1">
<transition on="submit" to="bindAndValidate"/>
<transition on="selectCreditCardAccount" to="selectCreditCardAccount"></transition>
<transition on="selectCheckingAccount" to="selectCheckingAccount"></transition>
</view-state>

...

</webflow>



Also I overwrite the prepareViewDescriptor() method in ServletFlowExecutionManager:
(Sorry Keith, I overwrite it again)


protected ViewDescriptor prepareViewDescriptor&#40;ViewDescriptor viewDescriptor, String flowExecutionId,
FlowExecution flowExecution&#41; &#123;
if &#40;flowExecution.isActive&#40;&#41; && viewDescriptor!=null&#41; &#123;
// make the unique flow execution id available in the model
viewDescriptor.addObject&#40;FLOW_EXECUTION_ID_ATTRIBU TE, flowExecutionId&#41;;
// make the flow execution itself &#91;b&#93;unavailable&#91;/b&#93; in the model so the length of redirect URL will not beyond 2047.
// viewDescriptor.addObject&#40;FLOW_EXECUTION_ATTRIBUTE, flowExecution&#41;;
// add some convenience values for views that aren't easily JavaBean aware
viewDescriptor.addObject&#40;CURRENT_STATE_ID_ATTRIBUT E, flowExecution.getActiveSession&#40;&#41;.getCurrentState&#40;&#41; .getId&#40;&#41;&#41;;
// also append the state id and flow execution id as parameter in order to trigger FlowExecutionManager.
viewDescriptor.addObject&#40;FLOW_EXECUTION_ID_PARAMET ER, flowExecutionId&#41;;
viewDescriptor.addObject&#40;ExternalEvent.CURRENT_STA TE_ID_PARAMETER, flowExecution.getActiveSession&#40;&#41;.getCurrentState&#40;&#41; .getId&#40;&#41;&#41;;

&#125;
return viewDescriptor;
&#125;


Also I configure the form object scope and errors scope as flow.

And everything works well.

doredson
Jul 6th, 2011, 10:27 AM
I'm curious why PortletExternalContext.requestFlowExecutionRedirec t() doesn't just allow the redirect to happen only if the current portletrequest is an instance of ActionRequest, and not do an assert.

This seems like to would make post-redirect-get just magically work.

Current code:
assertRedirectResponseAllowed();
flowExecutionRedirectRequested = true;
recordResponseComplete();

possibly workable code:
PortletRequestAttributes pra = (PortletRequestAttributes) RequestContextHolder.currentRequestAttributes();
PortletRequest pr = pra.getRequest();

if(pr instanceof ActionRequest) {
flowExecutionRedirectRequested = true;
recordResponseComplete();
} else {
flowExecutionRedirectRequested = false;
}