Announcement Announcement Module
Collapse
No announcement yet.
forwarding to a controller without RedirectView Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • forwarding to a controller without RedirectView

    My co-workers frequently ask me how to forward from one controller to another. My advice to them is to use a RedirectView which will send the browser a redirect to the next controller's URL. This works but it seems unnecessary since we do not need the browser to make an another request. What we really want to do is forward to the next controller on the server side in the same way we use an InternalResourceView to forward to a jsp.

    One specific example of a scenario where forwarding to another controller is useful is an administration tool to edit a User's preferences. On the front end, this requires two forms. One form selects the user to edit and the next form is used to edit the user's preferences. Currently we accomplish this task by having the select user form controller return a redirect view to the edit user page with a user ID encoded on the URL. What we would like to be able to do is simply return a server side forward to the Edit User page with a reference to the User command object we need to edit.

    Is a server side forward to another controller within the confines of Spring currently feasible today? If not, is this a feature we should consider adding? Thx.

    -karl

  • #2
    Isn't it possible to use InternalResourceView to forward to the other controller like this:

    Say your DispatcherServlet is mapped to "/dispatch/*" and you have a controller "editUser" that is mapped to "/editUser" using for instance the BeanNameUrlHandlerMapping. You can then forward to that controller using "/dispatch/editUser" as view name with a InternalResourceViewResolver.

    I haven't tested this buy I guess it should work.

    Erwin

    Comment


    • #3
      The main problem I see with that solution is that the view resolver configured in that web application context will not know to forward to a controller instead of an actual view. In the case of using jstl pages, the view resolver might look like:

      Code:
          <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="viewClass">
                 <value>org.springframework.web.servlet.view.JstlView</value>
              </property>
              <property name="prefix"><value>/WEB-INF/jsp/</value></property>
              <property name="suffix"><value>.jsp</value></property>
          </bean>
      Forwarding to "/dispatch/editUser" would cause the view resolver to look for a jsp /WEB-INF/jsp/dispatch/editUser.jsp" instead of forwarding to the named controller. Is there a way to get around this? Thx.

      -karl

      Comment


      • #4
        You could do view resolver chaining, but then you would't be able to use the InternalViewResolver for both JSPs and commands because it will resolve anything. Ofcourse you could work around this with a custom resolver that only resolves views ending with ".do" or something, but that wouldn't be very elegant.

        Erwin

        Comment


        • #5
          Another easy workaround would just be calling requestDispatcher forward with a url. Of course this does not stay within the confines of Spring. Spring currently has a RedirectView which sends a redirect response to the browser instead of rendering a view.

          I think Spring might need another View which allows for a server side forward to another controller. Perhaps a ControllerForwardView?

          Comment


          • #6
            I'm a Spring newbie so apologies if i'm missing the point.

            The issue to me is how to retrieve reference data. As long as the model name and the EL (or whatever) object path root match and the form action is set up correctly then the view is rendered correctly and will invoke the controller when equested. However, the reference data cannot be rendered. Of course, you can add the reference data to the model in the first controller but this duplicates the work of the new View's controller and i think breaks encapsulation somewhat.

            I think the View should have a hand in generating its own reference data but the very decoupled nature of the Views may make this difficult (and is probably way off topic here).

            Does the anyone know the rationale behind the use of redirects. They seem introduce more complexity than desirable. I've seen discussions of strategies of how to carry over error messages using the Session. Surely this is overkill when compared to some sort of internal forwarding. Another example is tables of form input. If this contains reference data then one must make the choice between breaking encapsulation as described above and storing the table data in the Session prior to doing a redirect. Again an internal forward that handles reference data seems much more desirable.

            The simplicitly and power of Spring MVC really appeals to me but this particular issue might mean that I have to drop it. The frequently with which this topic crops up must mean that something is amiss or has not been explained correctly. I'm going to investigate some sort of workaround forward() method based on AbstractFormController.showForm(). Any ideas will be gratefully received.

            Kev

            Comment


            • #7
              Wow. The ensuing silence tells me that my comments were either way off the mark or on a higher intellectual plane. I would bet on the former.

              One point I was trying to make was that there is the option of forwarding to another controller directly and letting that controller decide the view to return which is then resolved using the usual mechanisms. For me, this has the advantage that the controller can set up the reference data for the view and it is not necessary to use a redirect to do this.

              Here are method implementations of a ForwardeeController interface in a subclass of SimpleFormController that use showForm() to display the controller's formView populated by a model (and reference data!):

              Code:
              /** Forward to this controller ignoring any errors that were generated during the request */
              public ModelAndView forwardTo&#40;HttpServletRequest request, HttpServletResponse response, Object command&#41; throws Exception &#123;
                  return showForm&#40;request, response, new BindException&#40;command, getCommandName&#40;&#41;&#41;&#41;;
              &#125;
                   
              /** Forward to this controller carrying over any errors that were generated during the request */
              public ModelAndView forwardTo&#40;HttpServletRequest request, HttpServletResponse response, Object command,
              BindException errors&#41; throws Exception &#123;
                  String cmdName = getCommandName&#40;&#41;;
                  Map m = errors.getModel&#40;&#41;;
                  m.put&#40;cmdName, command&#41;;
                  request.setAttribute&#40;cmdName, command&#41;;
                  return showForm&#40;request, response, errors&#41;;
              &#125;
              The first method might be used when the request has been successful and so we don't believe there are any errors to display. The command parameter is the object which is bound to the view. The second method is used when we want to carry over the errors to the new view. In this case, there are two objects bound to the view. The bind tag expects these objects to be either wrapped by the errors object or to be a request attribute.

              This is how it might be used in another controller:

              Code:
              public void setEditController&#40;ForwardeeController fc&#41; &#123;
                  this.ec = ec;
              &#125;
              
              ...
              
              public void doSearch&#40;...&#41; &#123;
              
                  List l = searcher.find&#40;&#41;;
                  
                  if &#40;l.size&#40;&#41; == 1&#41; &#123;
                      ec.forwardTo&#40;request, response, l.get&#40;0&#41;&#41;;
                  &#125; else &#123;
                   ....
              &#125;
              I have this working in a prototype and so far it seems OK. Any comments ?

              Kev

              Comment


              • #8
                I've run into the same problem a number of times and created a small spring subproject to handle these annoyances (amonst other things it will also store all your actions in a central xml and releave you from the need of updating jsp's when your webflow will change)

                see http://www.competities.com/springworkflow/chaining.html for a way how I solved this.

                Something you might want to consider is that the forward isnt forwarded to another forward (and thus generating a endless forward loop) and that the models that are filled in by one controller are still copied over to the next controller. This way you can set your message in the controller or view where it should be and still pass that message along to the next controller/view and or the next controler/view etc, etc. See the WorkflowHandlerAdapter class for extended documentation

                HTH

                Comment


                • #9
                  The springworkflow looks like a great idea, but I still think something is missing from the spring web framework. I may be overlooking something but to me a new type of View which allows for server side forwarding to other controllers is an easy and worthwhile addition. Does anyone else agree?

                  Comment


                  • #10
                    A View that is not rendered but is used to forward or redirect to a controller seems to me to be some of architectural workaround. After all, a View is for presentation to the client. On the server side its the Model and Controller that matter. But if that's what it takes to fix this issue then why not vote for it in JIRA:

                    http://opensource.atlassian.com/proj...browse/SPR-387

                    Comment


                    • #11
                      I agree, a server side forward definitely is not a "view", but I think Spring has already gone down that path with the RedirectView.

                      Comment


                      • #12
                        Originally posted by kevinha
                        The simplicitly and power of Spring MVC really appeals to me but this particular issue might mean that I have to drop it.
                        Why so fatalistical? :wink: I've never had a problem that I couldn't find a solution for with Spring MVC.

                        Seriosly, let's consider the basic question at the root of this thread: why do we need to forward from one controller to another?

                        Some possible reasons:

                        1. Redirect: you want a new URL to show in the user's browser.
                        For example, a user clicked on a "Save" button, which submitted form data to be saved. When this request is handled without a redirect, the URL that the form was submitted to is displayed in the users browser. Even worse than the cosmetics are the implications of a page "reload"; the form would be re-submitted in most browsers. One workaround for this it to use session forms and the handleInvalidSubmit method. Another solution is to redirect to a different URL to prevent resubmission. IMHO this is about the only case in which a redirect is needed in the context of forwarding from one controller to another (the others that I know of are more obscure). Note that you can pass parameters through a redirect by supplying a model in addition to a RedirectView when constructing the ModelAndView. For example:

                        Code:
                        return new ModelAndView&#40;new RedirectView&#40;getSuccessView&#40;&#41;&#41;, modelMap&#41;
                        2. You have multiple controllers to handle a single request.
                        This case should be VERY rare. With the use of interceptors and/or filters, correct usage of referenceData() in form controllers, etc. multiple controllers per request can almost always be avoided. In the rare case that it must be done, I simply wire my Controller objects together in the Spring context. For example:

                        Code:
                        <bean id="controller1" ... >
                          ...
                        </bean>
                        
                        <bean id="controller2" ... >
                          <property name="controller1"><ref local="controller1"/></property>
                          ...
                        </bean>
                        Then in the controller I simply delegate:

                        Code:
                        public class Controller2 extends SimpleFormController &#123;
                          private Controller controller1;
                          
                          ...
                          
                          ModelAndView onSubmit&#40;...&#41; &#123;
                            return controller1.handleRequest&#40;...&#41;;
                          &#125;
                        
                        &#125;
                        A controller can transparently delegate to another controller and the second controller does not need to know anything about how it was invoked--it just receives a request and a response. The delegating controller can even inspect the ModelAndView object returned by handleRequest() and add additional model objects and/or change the view.

                        In a recent project, I used both of these techniques together to allow many forms to be submitted to the same URL while preventing form refreshes. I made a RedirectingController that saved request parameters, redirected to itself, then delegated to the correct form controller after the redirect. The trick was to make the original request parameters available to the delegate controller after the redirect, which was done by creating a wrapper for HttpServletRequest.

                        Comment


                        • #13
                          Controller To Controller very rare?

                          To drag one up from November :o

                          Dealing with point 2. I was just wondering whether the Spring coders had intended this kind of delegation between controllers, and if not, whether there was any risk that it may "upset" the underlying framework in any way. If controllers are multi-threaded singletons, then probably not. Just a thought.

                          One other question...

                          2. You have multiple controllers to handle a single request.
                          This case should be VERY rare.
                          Please consider the following "not so rare" scenario.

                          Page X. Requires a Controller X to get some data (from the business tier) so that a drop down list can be presented to the user in a JSP

                          Page Y. Requires a Controller Y to handle a form submission.

                          So, in my scenario, after the successful submission of the form in Page Y, the user should be presented with Page X. As far as I can see, this would be a common usage of a forward from one controller to another. I.E where a page requires a controller to set up data required in the page. I would not want to distribute the same set up code in multiple controllers.

                          Your solution will definately solve this, but I would be interested to know if I am missing a Spring related structural solution.

                          Any comments appreciated.
                          Adam

                          Comment


                          • #14
                            If you have a need to chain together multiple controller actions, combined with non-trivial page flow requirements (demanding wizards, master-detail forms, chunks of page flow that need to be reused in different contexts, dynamic page flows, flows whose execution need to be audited/observed), you seriously should look at Spring web flow, which we are integrating now for Spring 1.2. It exists for exactly this purpose.

                            A little background: Spring web flow was originally implemented by Ervin Vervaet (http://www.ervacon.com/products/springwebflow) - we liked his base page flow model so much, we ran with it and between Erwin, myself, Colin, and Rod have put together a even better, more powerful implementation we're very excited about.

                            For now, the code, with complete javadocs and test cases, is in the sandbox now in org.springframework.web.flow. We will promote it to the main src tree after 1.1.5 clears (I'd do it now if I could ;-)) I still need to make an official announcement, but between now and 1.2, we'd like as many people to evaluate it as possible.

                            Erwin and I are in the process of moving over his samples and tutorials into our documentation area, and bringing them in-line with the current implementation. His original xml-based flow definition format has changed very little (near backwards compatible between impls!), and there is a well-defined "builder strategy" for flows configured in other formats (like straight up java code or your favorite scripting language).

                            Comment


                            • #15
                              I was looking at the UrlBasedViewResolver and I noticed that with the release of 1.1.3, Spring now allows server side forwards by specificying the prefix forward: in the view name. As stated in the javadocs:

                              "Furthermore, forward URLs can be specified via the "forward:" prefix. E.g.: "forward:myAction.do" will trigger a forward to the given URL, rather than resolution as standard view name. This is typically used for controller URLs; it is not supposed to be used for JSP URLs - use logical view names there. "

                              Comment

                              Working...
                              X