Neil McGuigan
Neil McGuigan

Reputation: 48246

How to start a Spring Web Flow from a URL like this /resource/edit/n

I'd like to use Spring Web Flow mostly for its PRG abilities.

Let's say I have URLs like /widget/edit/99 or /widget/edit/123 , where the number represents the id of the widget.

How can I start the /widget/edit flow , passing in the id?

By default it looks like the flow URL must match the flow name folder structure.

I'd like to keep the url at /widget/edit/99 and not redirect.

(Using v 2.4)

Upvotes: 2

Views: 2551

Answers (2)

Prasad
Prasad

Reputation: 3795

You need to try with FlowController and DefaultFlowUrlHandler.

FlowController - It acts as gateway to Web Flow defined control logic and DispatcherServlet.

As per the documentation, in createFlowDefinitionUrl method in DefaultFlowUrlHandler:

    The flow definition URL for the given flow id will be built by appending 
    the flow id to the base app context and servlet paths.

    Example - given a request originating at:
    http://someHost/someApp/someServlet/nestedPath/foo 

    and a request for the flow id "nestedPath/bar", the new flow definition URL
    would be:
    http://someHost/someApp/someServlet/nestedPath/bar      

So if request is like: somehost/yourContextPath/yourServletContext/widget/edit/99 and flow id is widget/edit, new flow definition URL would be: somehost/yourContextPath/yourServletContext/widget/edit

Assuming certain configurations as:

The web.xml configuration maps "/widgetinventory/*" requests to the yourServletContext servlet:

    <servlet>
        <servlet-name>yourServletContext</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>yourServletContext</servlet-name>
        <url-pattern>/widgetinventory/*</url-pattern>
    </servlet-mapping>

yourServletContext-servlet.xml: All requests with a servlet path matching "/widgetinventory//" are mapped to the "flowController" bean.

    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="alwaysUseFullPath" value="true" />
        <property name="mappings">
            <value>/app/**/**=flowController</value>
        </property>
    </bean>

    <bean name="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
        <property name="flowExecutor" ref="flowExecutor" />
        <property name="flowUrlHandler" ref="customDefaultUrlhandler" />
    </bean>

    <bean id="customDefaultUrlhandler" class="package.CustomDefaultFlowUrlHandler"/>

    <flow:flow-executor id="flowExecutor" flow-registry="flowRegistry"/>

    <flow:flow-builder-services id="flowBuilderServices" view-factory-creator="viewFactoryCreator" validator="validator"/>

    <!-- Create the registry of flow definitions -->   
    <flow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows/" flow-builder-services="flowBuilderServices">  
        <flow:flow-location-pattern value="**/*-flow.xml"/>
    </flow:flow-registry>

See the flowUrlHandler being mapped to customDefaultUrlhandler which is an extension of DefaultFlowUrlHandler. The two methods which help to alter the flow url you specified in it are: getFlowId and createFlowDefinitionUrl.

Override the flowId and createFlowDefinitionUrl methods in DefaultFlowUrlHandler.

But this is all assuming your flow url is of type: somehost/{yourContextPath}/{yourServletContext}/widget/edit/99 where widget/edit is flowId and 99 is some widgetId.

    public class CustomDefaultUrlhandler extends DefaultFlowUrlHandler{

    You need to alter flowId and createFlowDefinitionUrl if request is something like this:
    http://host/{yourContextPath}/{yourServletContext}/widget/edit/99 to direct to widget/edit flow

    @Override
    public String  getFlowId(HttpServletRequest request) {
        String pathInfo = request.getPathInfo(); // /widget/edit/99
        if (pathInfo != null) {
            String widgetId = pathInfo.substring(pathInfo.lastIndexOf("/") + 1);
            if(widgetId != null){
                return pathInfo.substring(1,pathInfo.lastIndexOf("/")); //return widget/edit by stripping /99
            }else{
                return pathInfo.substring(1); //return widget/edit
            }
        } else {
            String servletPath = request.getServletPath();
            if (StringUtils.hasText(servletPath)) {
                int dotIndex = servletPath.lastIndexOf('.');
                if (dotIndex != -1) {
                    return servletPath.substring(1, dotIndex);
                } else {
                    return servletPath.substring(1);
                }
            } else {
                String contextPath = request.getContextPath();
                if (StringUtils.hasText(contextPath)) {
                    return request.getContextPath().substring(1);
                } else {
                    return null;
                }
            }
        }
    }

    @Override
    public String  createFlowDefinitionUrl(String flowId, AttributeMap input, HttpServletRequest request) {
        //now flowId = "widget/edit"
        StringBuffer url = new StringBuffer();
        if (request.getPathInfo() != null) {
            //for /{yourContextPath}/{yourServletContext}/widget/edit/99 - pathInfo is /widget/edit/99          
            url.append(request.getContextPath());
            url.append(request.getServletPath());
            url.append('/');                    
            url.append(flowId);
        } else {
            String servletPath = request.getServletPath();
            if (StringUtils.hasText(servletPath)) {
                url.append(request.getContextPath());
                url.append('/');
                url.append(flowId);
                int dotIndex = servletPath.lastIndexOf('.');
                if (dotIndex != -1) {
                    url.append(servletPath.substring(dotIndex));
                }
            } else {
                url.append('/');
                url.append(flowId);
            }
        }
        if (input != null && !input.isEmpty()) {
            url.append('?');
            if (request.getPathInfo() != null) {
                //append your widget id here and retrieve this in flow by requestParameters el.
                String widgetId = pathInfo.substring(pathInfo.lastIndexOf("/") + 1);    
                url.append("widgetId ="+widgetId);
            }
            appendQueryParameters(url, input.asMap(), getEncodingScheme(request));
        }
        return url.toString();
    }
    }

Basically we are customizing the flowid from your URL and passing the ids as request parameters.

Make sure your flow id is widget/edit in your scenario.

Check this link about how to get the flow id in desired format here : Spring Webflow - How to Get List of FLOW IDs

Upvotes: 6

ronielhcuervo
ronielhcuervo

Reputation: 715

See, this topic Consider using spring mvc instead, In Spring MVC you can use the @PathVariable annotation on a method argument to bind it to the value of a URI template variable:

e.g

@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
 Owner owner = ownerService.findOwner(ownerId);
 model.addAttribute("owner", owner);
 return "displayOwner";
}

The URI Template "/owners/{ownerId}" specifies the variable name ownerId. When the controller handles this request, the value of ownerId is set to the value found in the appropriate part of the URI. For example, when a request comes in for /owners/fred, the value of ownerId is fred.

A @PathVariable argument can be of any simple type such as int, long, Date, etc. Spring automatically converts to the appropriate type or throws a TypeMismatchException if it fails to do so. You can also register support for parsing additional data types. See the section called “Method Parameters And Type Conversion” and the section called “Customizing WebDataBinder initialization”.

See Here documentation about PathVariable.

Upvotes: 1

Related Questions