Spring Web Flow ExternalRedirect using Expression

Dave Jarvis picture Dave Jarvis · Feb 20, 2015 · Viewed 9.4k times · Source

Background

A user must submit report parameters to the server. The server redirects the user to a URL. That URL runs Oracle Reports to produce a PDF (or web page).

The system uses a relatively slow authentication mechanism. But I'm not looking to restart the web flow, so the authentication should not otherwise interfere with the redirect.

JSP

The "Run Report" button is coded as follows:

<button type="submit" id="run" name="_eventId_run">
    <fmt:message key="form.button.report.run" />
</button>

The rest of the page binds the parameters to a map in the DAO, such as:

<form:radiobutton path="parameters['service']" class="service" value="..." />

Submitting the form shows that the bind variables are set in the DAO map correctly. Further, the report is able to generate the URL used for redirection.

The form itself resembles:

<form:form modelAttribute="report" action="${flowExecutionUrl}"
    method="post">
    <fieldset>
        <%-- secret tokens --%>
        <tiles:insertAttribute name="hidden" />

        <%-- includes the requested report form parameters --%>
        <jsp:include page="${reportKey}.jsp" />

        <%-- includes the aforementioned submit button %-->
        <tiles:insertAttribute name="reportButtons" />
    </fieldset>
</form:form>

Flow

The flow has three view states: list reports, enter parameters, and run report. The last two are pertinent:

<view-state id="parameters" model="report" view="flow/reports/parameters">
    <transition on="run" to="redirect">
        <evaluate expression="reportService.run(report)" result="flowScope.url" />
    </transition>
</view-state>

<view-state id="redirect" view="externalRedirect:#{flowScope.url}"/>

The reportService.run(report) method is being called. The report parameters are being bound. The return result is, indeed, the correct URL.

Java

The report service itself is reasonably trivial:

public String run(Report report) {
    Map<String, String> parameters = report.getParameters();

    String url = getConfigurationValue("ReportUrl");

    for (String key : parameters.keySet()) {
        info("Report Parameter: {} = {}", key, parameters.get(key));
    }

    return url;
}

Again, the correct url is being returned. There is no controller class. The Report DAO could hardly be simpler:

public class Report extends DAOBase implements Serializable {
    /** Values to pass into the report. */
    private Map<String, String> parameters;

    public Report() {
        setParameters(createParameters());
    }

    // standard accessors and serial version not shown
}

Problem

It appears that the application is performing a POST-Redirect-GET (PRG). The GET does not direct the browser to the URL set by flowScope.url. (I have not verified that flowScope.url contains valid data.) When the POST operation completes, the application is redirected to the parameter flow, rather than the run button redirecting to the redirect flow.

From the schema definition, everything seems correct.

Questions

Using Spring 4.1.2, what needs to change so that the externalRedirect sends the browser to the URL returned by reportService.run(...)?

Does the form need to supply a different value for flowExecutionUrl?

Attempts

Here are some of the various changes that have been made to no avail:

  • Use $ in the EL, instead of # (i.e., externalRedirect:${flowScope.url})
  • Use <end-state id="redirect" view="externalRedirect:#{flowScope.url}"/>
  • Use <view-state id="redirect" view="externalRedirect:http://google.com"/>
  • Use form:button instead of button

Changing the redirect view-state and removing the expression evaluation causes the redirect to fire. For example:

<transition on="run" to="redirect">
    <!-- <evaluate expression="reportService.run(report)" result="flowScope.url" />  -->
</transition>
...
<view-state id="redirect" view="externalRedirect:http://google.com"/>

This, of course, means that the report parameters are never used, which won't work.

Answer

Dave Jarvis picture Dave Jarvis · Feb 20, 2015

Remove the evaluation from the transition:

<transition on="run" to="redirect"/>

Call the run method within an EL statement to generate the URL:

<view-state id="redirect" view="externalRedirect:#{reportService.run(report)}"/>