Thanks for the explanation Ted. What if I want to redirect to another server from an Action? I need to return an ActionForward with redirect=true. Will your solution handle absolute uris in that way?

Dave






From: Ted Husted <[EMAIL PROTECTED]>
Reply-To: "Struts Developers List" <[EMAIL PROTECTED]>
To: Struts Developers List <[EMAIL PROTECTED]>
Subject: Re: Forwards, Absolute URIs and Leading Slash [LONG]
Date: Mon, 18 Nov 2002 08:13:05 -0500

11/16/2002 9:19:51 PM, David Graham <[EMAIL PROTECTED]>
wrote:
>I was trying to fix
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=11021
>but got rather confused along the way. The problem seems to be
that
>RequestUtils.forwardURL() always prepends the context path even
for absolute
>urls. The version comments seem to go back and forth on how to
behave with
>a leading / in the url. Sometimes no leading / means don't
prepend the
>context, then that decision was reversed.
>
>Maybe I'm not the right person to fix this but we need to decide
how to
>determine if the user has entered an absolute url like
>http://www.google.com.



OK, let's start with the 1.0 behavior.

There are two ways to indicate a path, an ActionMapping.forward
and an ActionForward.

In the case of an ActionMapping.forward, we did this

* Get String (path) stored as the ActionMapping forward property.
* Give it to the RequestDispatcher.

which boils down to

String forward = ActionMapping.getForward();
// ... error checking
RequestDispatcher rd =
getServletContext().getRequestDispatcher(forward);
// ... error checking
rd.forward(request, response);

rd.forward allows you to forwards a request from a servlet to
another resource (servlet, JSP file, or HTML file) on the server.
Since we obtained it via getRequestDispatcher(), the
ServletRequest object has its path elements and parameters
adjusted to match the path of the target resource. So, all the
references here will *always* be context relative without our
having to munge the path in any way.

Also, this method does *not* support absolute URL with a schema
attached. It's meant to forward to another resource on the same
server, and since we were using getServletContext, to a resource
in the same application.

In the case of an ActionForward, in 1.0 we handle it like this:

* Retrieve the ActionForward bean
* get the path property
* if the ActionForward.redirect property is true and the path
starts with a slash, then we insert the context path before
redirecting. If the path does not start with a slash (e.g, it has
a schema), we leave it alone.
* if ActionForward.redirect is false (the default), we handle it
like the ActionMapping.forward
(getServletContext.getRequestDispatcher).

if (forward != null) {
String path = forward.getPath();
if (forward.getRedirect()) {
if (path.startsWith("/"))
path = request.getContextPath() + path;
response.sendRedirect(response.encodeRedirectURL
(path));
} else {
RequestDispatcher rd =
getServletContext().getRequestDispatcher(path);
if (rd == null) {
response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
internal.getMessage
("requestDispatcher",
path));
return;
}
rd.forward(request, response);
}

So, to get to another server, we had to set redirect=true and use
an ActionForward.

A third way to indicate a path was via the standard ForwardAction.
In 1.0, the behavior of the ForwardAction and IncludeAction
mimicks the ActionMapping.forward and ActionMapping.include with
any apparent value add. I don't remember why we did this. My guess
is that we did the Actions first and then extended the
ActionMappings, leaving the Actions behind for backward
compatability.

So, in 1.0, the only place we needed to munge a path was when

* We used an ActionForward (rather than ActionMapping.forward)
* Redirect was true
* The path started with a slash

We only munge it here because we are going through the
response.sendRedirect. Response.sendDirect doesn't know anything
about the ServletContext, and so we provide this bit ourselves. In
all other cases, RequestDispatcher does all the dirty work fr us.

In 1.1, we munge paths more often, since we need to inject the
module component. There are also times when we should not inject
the module component, so we added the contextRelative property to
the ActionForward.

When contextRelative is false (the default), we are implying that
the path is instead module relative.

In the case of a 1.1 ActionMapping.forward, we do this

* Get String (path) stored as the ActionMapping forward property.
* Inject the module prefix (which may be blank).
* Give it to the RequestDispatcher.

So, conceptually, this appears to be same behavior as 1.0. I can
only ActionMapping.forward (or include) to another resource in the
current application (or application module).

In the case of an 1.1 ActionForward, we would want to

* if contextRelative is false, insert the module prefix

and then

* if redirect is true and the path starts with a slash, insert the
context path.

Right now in RequestUtils.forwardURL, we're forcing a leading
slash if context-relative is true.

// Handle a ForwardConfig marked as context relative
StringBuffer sb = new StringBuffer();
if (forward.getContextRelative()) {
if (!path.startsWith("/")) {
sb.append("/");
}
sb.append(path);
return (sb.toString());
}

Then in RequestProcess.processForwardConfig, we're prepending the
context on any redirect.

if (forward.getRedirect()) {
response.sendRedirect
(response.encodeRedirectURL(request.getContextPath
() + uri));

I believe we that forwardURL is being "too helpful" and should
just return the original path when context-relative is true.

// Handle a ForwardConfig marked as context relative
if (forward.getContextRelative()) {
return path;
}

To take up the slack, processForwardConfig then needs to be a bit
more helpful, and expressly cover the alternatives. Something
like,

String uri = RequestUtils.forwardURL(request, forward);
if (forward.getRedirect()) {

// - response.sendRedirect
// - (response.encodeRedirectURL(request.getContextPath
() + uri));

String path = null;
if (uri.startsWith("/")) path =
request.getContextPath() + uri;
else path = uri;
response.sendRedirect(response.encodeRedirectURL
(path));

} else {
doForward(uri, request, response);
}

I don't have time to test the patch right now, but will take
responsibilty for trying this by the middle of week (if no one has
any input).

To make it easier to hook up with another server, like
http://google.com, I suggest we add a RedirectAction, which would
work the same as ForwardAction, but also set redirect=true.

So, to link to another server (like http://google.com/), you would
use the RedirectAction (or equivalent).

//...

type="org.apache.struts.actions.RedirectAction"
parameter="http://google.com";

//...

To include or forward to another servlet, you use the
IncludeAction or ForwardAction

type="org.apache.struts.actions.ForwardAction"
parameter="/myOtherServlet.whatever"

To forward *within* the same application/module (say to front a
JSP), you use the ActionMapping.forward property.

<action path="/whatever" forward="/pages/whatever.jsp" />

[where whatever.jsp is stored at $MODULE/pages]

I will also take responsibility for clarifying that throughout the
documentation this week.

When this shows up in the archive, I'll also add a link to
Bugzilla ticket, so there is a running record.

-Ted.




--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

_________________________________________________________________
The new MSN 8: advanced junk mail protection and 2 months FREE* http://join.msn.com/?page=features/junkmail


--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to