Forwarding requests between contexts in Tomcat

2019-03-21 12:24发布

问题:

I'd like to be able to do cross-context request forwarding in Tomcat with the Tuckey URLRewrite filter. For example, I'd like to be able to route an incoming request with an SEO-/user-friendly URL like http://example.com/group-elements/300245/some-descriptive-text, where "group-elements" isn't the name of a deployed application, to a URL mapped to a Java Spring controller method for application 'foo', like http://example.com/foo/app/group/300245/elements. I'm using Tomcat 7.0.27 and URLRewrite 3.2.0; I'm working with Java Spring 3.1 web applications.

The URLRewrite 3.20 documentation notes an optional 'context' attribute for the 'to' filter parameter element:

If your application server is configured to allow "cross context" communication then this attribute can be used to forward (and only forward, not redirect or other "to" types) requests to a named servlet context.

On Tomcat, for instance, the application contexts in the server configuration (server.xml or context.xml) need the option crossContext="true". For instance, the two applications mentioned before ("app" and "forum") have to be defined as:

<Context docBase="app" path="/app" reloadable="true" crossContext="true"/>
<Context docBase="forum" path="/forum" reloadable="true" crossContext="true"/>

Given that and the original discussion about the feature, the 'context' attribute seems to be what I'm looking for. However, I haven't been able to properly enable cross-context request forwarding.

Here's my 'Context' entry application 'foo' in conf/server.xml:

<Context docBase="foo" path="/foo" reloadable="true" crossContext="true"/>

I have my urlrewrite.xml file and web.xml file in webapps/ROOT/WEB-INF/. Here's what they look like:

urlrewrite.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN" 
"http://tuckey.org/res/dtds/urlrewrite3.2.dtd">

<urlrewrite>    
    <rule>
        <from>baz</from>
        <!-- Note: this 'to' element's value has an error.  See the edit at bottom of this post for corrected version. -->
        <to context="foo">/foo/app/group/300245/elements</to> 
    </rule> 
</urlrewrite>

web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd" version="2.5">

    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
        <init-param>
            <param-name>logLevel</param-name>
            <param-value>WARN</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

The rule defined above in urlrewrite.xml is intentionally basic and hard-coded. In this case I'm simply trying to get the cross-context aspect of the rule working before developing the regular expressions in 'to' and 'from'.

When I request http://example.com/baz with that rule in place, Tomcat returns a 404 error saying "The requested resource (/baz) is not available." I've tried a few variations in the 'to' filter parameter, but nothing has worked yet. And I haven't been able to find any examples of how 'context' should be used.

Any ideas on how I could get this kind of cross-context request filtering to work? Is it even possible? I imagine I might be able to achieve what I'm trying to do by renaming foo.war to ROOT.war or changing the root application as mentioned here, but I'd like to try doing this via URLRewrite unless doing so is infeasible or a bad idea on its face.

If showing more of my configuration would help, please let me know. Thanks in advance for any input.

Edit:

Thanks to Christopher Schultz for the helpful answer. In my case, the problem was caused by two things: 1) not having a context.xml file in webapps/ROOT/META-INF, and 2) having an error in the 'to' element in the URL rewrite rule in webapps/ROOT/WEB-INF/urlrewrite.xml.

The fix involved putting a proper context.xml file in webapps/ROOT/META-INF. For reference for anyone else that encounters this problem, that file ended up looking like this:

webapps/ROOT/META-INF/context.xml

<?xml version='1.0' encoding='utf-8'?>

<Context docBase="ROOT" path="/" reloadable="true" crossContext="true" />

As Schultz mentions, it's only necessary for a context with crossContext="true" to be defined for the context implied in the 'from' element in a given URL rewrite rule (here, that's ROOT). It is not necessary to explicitly define a context for the application in the 'to' URL rewrite rule. In other words, you shouldn't need to manually create a context.xml file for that application -- so continuing the example above, you would not need to manually define and put a context.xml file into webapps/foo/META-INF/.

Schultz's answer reflects the recommendations for defining a context in the official Tomcat documentation: http://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Defining_a_context.

The problem was also caused by the fact that the URL rewrite rule in my initial post had an error. The correct version should have been:

urlrewrite.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN" 
"http://tuckey.org/res/dtds/urlrewrite3.2.dtd">

<urlrewrite>    
    <rule>
        <from>baz</from>
        <!-- Note: the use of '/app' instead of '/foo/app/' below -->
        <to context="foo">/app/group/300245/elements</to> 
    </rule> 
</urlrewrite>

回答1:

If your (real) webapp is deployed to /foo and you want to on-the-fly rewrite URLs like /group-elements/baz to forward (not redirect) to /foo/app/group/300245/elements, then you're going to have to deploy your rewrite filter to one of two places: /group-elements or /.

The above configuration appears to be deploying to ROOT (which is /) but then mapping the URL /baz to /foo/app/group/300245/elements. Instead, you probably want this:

<rule>
    <from>/group-elements/baz</from>
    <to context="foo">/foo/app/group/300245/elements</to>
</rule>

It looks like you were trying to hit http://example.com/baz which I would have expected to work. The last bit of magic is going to be making the ROOT context cross-context (note that your webapp does NOT need to be cross-context: only the urlrewrite one does). You can change the ROOT webapp to be cross-context by addint crossContext="true" to webapps/ROOT/META-INF/context.xml.

Finally, you should really stop putting <Context> elements in server.xml: leaving them in there basically means you need to restart Tomcat in order to change your webapp deployments.



回答2:

After the comments I come do this possible conclusion:

I think you are mixing the concepts of a reverse proxy with cross-context. Cross-context is method to share data between two web-applications within the same application server. A reverse-proxy like 'Apache http' can rewrite a url to pass it to a certain server behind it, effectively hiding any unwanted parts or performing other operations like load-balancing.

The infrastructure would be: client --> reverse proxy --> application server