ActionController: Equivalent of J2EE Servlet getRequestDispatcher.forward?

36 views
Skip to first unread message

John G. Norman

unread,
Nov 16, 2007, 12:13:08 PM11/16/07
to boston-r...@googlegroups.com
Hey folks.

Sanity check:

A student in my Rails class asked me if there is an equivalent to the J2EE/Servlet getRequestDispatcher.forward. In the Servlet spec., this asks the container to hand off control to another resource, most typically, though not necessarily, another Servlet. ( http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/RequestDispatcher.html#forward(javax.servlet.ServletRequest,%20javax.servlet.ServletResponse) )
Struts has a similar idea.

The answer seems to be: No.

In ActionController, you can ask for the rendering to be that associated with another Action. Fine. But that's not a forward.

And obviously you can do a redirect. Not an "internal" forward.

But Servlet "forward" means: Have the *container* (i.e., the Servlet dispatcher; or, in Struts, the main Struts controller) pass control to another Servlet, Struts Action, whatever.

It would be like doing a method call on another controller, but that transfer of control is managed by the component that instantiates and passes control to the original Servlet. So the relationship between the two Servlets is decoupled.

It seems that this isn't done in ActionController.

Right?

(To be sure, in ActionController, to get another action on the same controller, you could just call the method.)

If there was such a mechanism, you would see it in dispatcher.rb, where the controller is picked based on the routing. Somewhere in there would be a mechanism to pick up the name of a new controller which would then be given control. But I checked, and don't see it.

I can think of lots of good reasons why Rails doesn't provide Servlet-style forwarding and/or including.

John

ni...@hanoian.com

unread,
Nov 16, 2007, 8:11:25 PM11/16/07
to boston-r...@googlegroups.com
What about:
render :controller=> other_controller, :action => some_action, :params =>
params


It will do the same with RequestDispatcher: Use the server to call to
another controller.


> Hey folks.
>
> Sanity check:
>
> A student in my Rails class asked me if there is an equivalent to the
> J2EE/Servlet getRequestDispatcher.forward. In the Servlet spec., this asks
> the container to hand off control to another resource, most typically,
> though not necessarily, another Servlet. (
> http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/RequestDispatcher.html#forward(javax.servlet.ServletRequest,%20javax.servlet.ServletResponse)

> <http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/RequestDispatcher.html#forward%28javax.servlet.ServletRequest,%20javax.servlet.ServletResponse%29>

John G. Norman

unread,
Nov 16, 2007, 8:50:45 PM11/16/07
to boston-r...@googlegroups.com
Please correct me if I'm wrong, but render means: Use the VIEW for a
particular action. From the API docs:

# Renders the template for the action "goal" within the current controller
render :action => "goal"

http://api.rubyonrails.org/classes/ActionController/Base.html#M000272

See? "Renders the TEMPLATE."

What I am talking about is actually getting
OtherController#some_action to run; i.e., the method definition.

John G. Norman

unread,
Nov 16, 2007, 9:43:58 PM11/16/07
to boston-r...@googlegroups.com
Just as a clarification: Someone wrote me and said 'render :action=>
"goal" will call the method "goal" in the same controller.'

That isn't what the docs say, and when I looked at the Rails source,
that doesn't seem to be what ActionController::Base or dispatcher.rb
wants to do.

So my test case is:

def action1
print "\n\n**** Action1 ****\n\n"
end

def action2
print "\n\n**** Action2 ****\n\n"
render :action => :action1
end

browse to . . . . /action2 , and you will never see on the console
*** Action1 ***, which supports what the docs say. render :action
means pick the template for the other action. It doesn't mean run the
other action.

I'm not making some big claim -- the behavior seems obvious enough
from the documentation and the Rails source -- just trying to
emphasize what a J2EE person would perceive as a gap.

But, of course, I wish to stand corrected. :-)

ni...@hanoian.com

unread,
Nov 16, 2007, 10:52:35 PM11/16/07
to boston-r...@googlegroups.com
I have 2 controllers TestOneController and TestTwoController like this:

File test_one_controller.rb
---------------------------------------------------
class TestOneController < ApplicationController
def action_one
render :action => :action_two
end

def action_two

end

def action_dispatch
render_component :controller => "test_two", :action => :action_three
end
end
----------------------------------------------------


File test_two_controller.rb
----------------------------------------------------
class TestTwoController < ApplicationController
def action_three

end


end
----------------------------------------------------

You will need to create 3 template files to test
app/views/test_one/action_one.rhtml
app/views/test_one/action_two.rhtml
app/views/test_two/action_three.rhtml


In each file you just need to write "Action One", "Action Two" and "Action
Three" respectively to distinguish them.


So, if you try
http://localhost:3000/test_one/action_one -> You will see "Action Two". It
means that the method action_two is called.

http://localhost:3000/test_one/action_two -> You will see "Action Two".

http://localhost:3000/test_one/action_dispatch => You will see "Action
Three". It means the method action_three in the controller
TestTwoController is called, although you start with TestOneController.


If you still don't believe me, should we make a friendly bet for a beer or
a sushi dinner? Next time we have Boston Group meeting, I will bring the
code.

John G. Norman

unread,
Nov 16, 2007, 11:05:11 PM11/16/07
to boston-r...@googlegroups.com
No, I believe you that render_component will do it. Thanks a bunch.
I'm happy-ish now. Glad to do the beer and sushi.

That was exactly what I was trying to find, which render wasn't doing
for me. In other words, I think everyone I wrote was correct, I had
just completely spaced off render_component.

(Now, as to forgetting render_component: It is probably because the
new edition of Agile Web Development with Rails is hostile to
components, and says it will be ejected from Rails core for 2.0.0,
turned into a separate plugin -- which I haven't checked. True? Are
you getting a deprecation warning for render_component in 1.2.5?)

ni...@hanoian.com

unread,
Nov 16, 2007, 11:43:02 PM11/16/07
to boston-r...@googlegroups.com
Hi John,
You are welcome.
I think that there is certain not-so-good thing when Rails renders
components, so the book advised against it.

I don't really use render component, so I don't notice about it. I write
code in certain way to re-use partials and dynamically pass params to
avoid using components. However, I will make a test to see if it is
deprecated in 1.2.5.

About beer, when we have the group meeting, it is very likely we will have
beer afterward anyway, so we can chat more about Ruby and Rails.

Have a great weekend.

ni...@hanoian.com

unread,
Nov 17, 2007, 1:31:54 AM11/17/07
to boston-r...@googlegroups.com
Hi,
I believe that in 2 cases:
1) render :action=> some_action
2) render_component :controller => 'other_controller', :action => some_action

Rails only renders the templates which are correspondent to the action. It
does not really call the action itself.

In the 1) case, it really does what it tells: Render the template of the
action, not calling the action. (I made an error about understanding right
here).

In the 2) case, even the Rails API document says that it forward the
request to the controller/action, and provides an example about it.
http://api.rubyonrails.org/classes/ActionController/Components/InstanceMethods.html#M000084

But that example doesn't work that way. It simply renders the template of
the controller/action, without calling any code in the method. I guess
that it is Rails implementation.

So eventually, it looks like Rails doesn't have anything like
RequestDispatcher.forward.

Jacob Burkhart

unread,
Nov 17, 2007, 4:05:20 PM11/17/07
to boston-r...@googlegroups.com
As far as I understand if from when I used to write web apps in Java
and J2EE, we used to use "forward" all over the place because it was
the only way to pass control to another Servlet.

For instance, we would have a Login servlet that would check your
login and then that would forward appropriately if you were in fact
logged in and the other servlets could forward to login if you weren't
logged in etc.

But the rails way of doing it with filters is much cleaner and clearer
and easier to test and follow.

I'm sure if anybody had a need it would be easy enough to hack the
rails dispatcher and implement a forward in the java style. But I see
so many superior alternatives, there's simply no need.

All forwarding and passing of control should be done directly via
method calls anyway, if I want to call another action I should just
call it. If I want to do some partial request processing and then
hand off to another action, I can do it in a filter.

In J2EE Servlets you couldn't pass control with a method call, you HAD
to use the "forward" mechanism and if you wanted to pass any arguments
had to stick them into the request object with some "key" that was
probably a constant from some giant constants file somewhere. We had
giant XML files configuring how instances of one servlet would forward
to specifically configured instance of some other servlet.

I think one of the "Big Enterprise" supposed reasons for Servlet
forwarding was that theoretically the Servlet container could decide
to forward to a completely different process on possibly even a
different machine to continue your request processing.... but, we
never actually used anything like that.

So I would tell you student, there's simply need to for dispatcher
forwarding in rails, if you think there's a problem that would be
solved by forwarding, it's probably more cleanly solved by something
else...

John G. Norman

unread,
Nov 17, 2007, 6:31:00 PM11/17/07
to boston-r...@googlegroups.com
I agree generally with your comments.

One pain point, though, about calling a method on another controller
is that you would have to instantiate it, which is kind of awkward.
(Or you'd have to write a handler of some kind to do that.) So I think
the dispatcher is the place to intervene.

In any case, what you describe is pretty much what I said to my
students: The J2EE dependence on forwards is an artifact of the
architecture, and can be done more elegantly, generally, using filters
and other means in Rails.

Thanks!!

Reply all
Reply to author
Forward
0 new messages