Community   SpringSource   Projects    Downloads    Documentation    Forums    Training   Exchange   Blogs

Go Back   Spring Community Forums > Core Spring Projects > Web

Reply
 
Thread Tools Display Modes
  #1  
Old Oct 9th, 2005, 09:17 AM
andrew cooke andrew cooke is offline
Junior Member
 
Join Date: Oct 2005
Posts: 17
Default JstlView, extensionless URLs, /* matching and loops

Hi,

I would like to have URLs that are clean/clear to users. So I'd like
to have URLs like [root]/user/andrew or [root]/help/faq without file
extensions etc.

I'd also like to do this without Apache rewrite (ie just within
Tomcat). So I defined my servlet-mapping to match the url pattern /*.

However, I get stuck in a loop when I use JstlView to foward a request
to a particular JSP page like WEB-INF/jsp/root/index.jsp because that
is itself matches the /* url pattern of the servlet (at least, I
believe this is what happens).

In the examples I've found, this is avoided by using using /*.html as
a map and providing implementations in .jsp files. However, as I
said, I'd like to avoid that if possible.

Is there a solutuion without using apache and mod_rewrite? If not, is
there anything I ought to take care of now, while developing with
Tomcat, so that mod_rewrite is easy to add later?

Thanks,
Andrew
Reply With Quote
  #2  
Old Oct 11th, 2005, 10:40 AM
jeoffw jeoffw is offline
Junior Member
 
Join Date: Oct 2005
Posts: 11
Default In search of extension-less URLs

Hey Andrew, did you figure out a solution to this problem? I too am searching for extension-less URLs, and we're not the only ones:

"URL mapping, why don't I get it?"
http://forum.springframework.org/showthread.php?t=18004

"directory like URL, no extension?"
http://forum.springframework.org/showthread.php?t=18755

Last edited by robyn; May 14th, 2006 at 09:08 PM.
Reply With Quote
  #3  
Old Oct 11th, 2005, 10:56 AM
andrew cooke andrew cooke is offline
Junior Member
 
Join Date: Oct 2005
Posts: 17
Default

what i've done so far is:
- develop everything with ".html" as external url extensions
- use ".jsp" as the internal extension
- use the servlet name in the url path
(that's all "as normal")

what i intend to do is:
- generate urls to my own pages via a custom tag that will allow me to strip the servlet name and extension (maybe c:url allows this - i haven't looked yet).
- place the production implementation in apache
- use apache rewrite rules to add ".html" (and servlet name, although i guess that will be via the virtual host config rather than a rewrite rule) on incoming requests
- configure the custom tag to generate links without extensions (and without servlet name)

i think that should work.
Reply With Quote
  #4  
Old Oct 11th, 2005, 11:11 AM
jeoffw jeoffw is offline
Junior Member
 
Join Date: Oct 2005
Posts: 11
Default an alternate approach - multiple servlet-mapping elements

I think I'm probably just going to use multiple servlet-mapping elements in conjunction with the dispatcher handler's AlwaysUseFullPath property. Using your original example URLs...

In web.xml it would look something like this:
Code:
	<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/user/*</url-pattern>
	</servlet-mapping>   
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/help/*</url-pattern>
	</servlet-mapping>
In dispatcher-servlet.xml, something like this:
Code:
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="alwaysUseFullPath" value="true" />
        <property name="mappings">
            <props>
                <prop key="/users/*">usersController</prop>
                <prop key="/help/faq">faqController</prop>
                <prop key="/help/*">generalHelpController</prop>
            </props>
        </property>
    </bean>
That seems to me like it gives the desired flexibility. The cost is that you have to maintain potentially many servlet-mapping elements in web.xml, which also must not conflict with any file directories you want to serve.

Ideally the dispatcher would just catch all requests, handle only the ones for which it has a mapping, and then pass the rest through to the standard file/directory handler.

It makes me wonder how much the Spring people actually use their own framework; after all, this is a PHP forum...
Reply With Quote
  #5  
Old Oct 30th, 2005, 08:21 PM
andrew cooke andrew cooke is offline
Junior Member
 
Join Date: Oct 2005
Posts: 17
Default

thanks for the comments. i've not yet got round to this, but will try what you suggest (for some reason i thought i tried something similar and got stuck in a loop with redirects, but i can't remember the details, and it was early on in the project - i understand a bit more now...)
Reply With Quote
  #6  
Old Nov 29th, 2006, 06:24 AM
andrew cooke andrew cooke is offline
Junior Member
 
Join Date: Oct 2005
Posts: 17
Default My final solution

Someone just sent me an email asking what I finally did, so I thought I should post here. I don't think I tried everything above (in fact I may have asked in a different thread, because I thought the solution I am using came from here, but didn't see it glancing quickly through this thread), so it is probably not the only way.

Anyway, in my web.xml I have:

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

In urlrewrite.xml (also in WEB-INF) I have

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

<urlrewrite>
<rule>
<note>any url without a final file name goes to index</note>
<from>^([^?]*)/(\?.*)?$</from>
<to last="false">$1/index$2</to>
</rule>
<rule>
<note>any url without a final file extension goes to .dynamic</note>
<from>^([^?]*)/([^?/\.]+)(\?.*)?$</from>
<to last="false">$1/$2.dynamic$3</to>
</rule>
</urlrewrite>

In my appname-servlet.xml I have:

<bean id="urlMapping"
class="org.springframework.web.servlet.handler.Sim pleUrlHandlerMapping">
<property name="order" value="1"/>
<property name="alwaysUseFullPath" value="true"/>
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor"/>
<ref bean="themeChangeInterceptor"/>
<ref bean="tzChangeInterceptor"/>
<ref bean="initialUrlInterceptor"/>
<ref bean="taglineInterceptor"/>
</list>
</property>
<property name="mappings">
<props>
<prop key="/new-member.dynamic">newUserController</prop>
<prop key="/new-member-email.dynamic">newUserEmailController</prop>
<prop key="/new-member-thanks.dynamic">rootController</prop>
<prop key="/help/*.dynamic">helpController</prop>
<prop key="/index.dynamic">rootController</prop>
<prop key="/communities.dynamic">communityIndexController</prop>
<prop key="/members.dynamic">memberIndexController</prop>
</props>
</property>
</bean>

etc.

Hope that makes sense. I can't remember in detail what it all means, but basically I map incoming URLs to add a ".dynamic" and then match that. Internally I use ".jsp" for jsp pages.
Reply With Quote
  #7  
Old Nov 29th, 2006, 11:01 AM
ndp ndp is offline
Junior Member
 
Join Date: Nov 2006
Posts: 1
Default Different solution

I was the one who asked... after sending the note, I kept working and came up with a different Tomcat-dependent solution. I'm still testing and thinking through potential problems. But here it is in its nascent form.

In web.xml catch /* and send it to Spring.


In your servlet context file you need catchall mappings. Spring provides a convenient class for this:
<!-- JSP files are compiled and run -->
<bean name="/**/*.jsp" class="org.springframework.web.servlet.mvc.Servlet ForwardingController">
<property name="servletName" value="jsp"/>
</bean>

<!-- All other resources are served up plain -->
<bean name="/**/*" class="org.springframework.web.servlet.mvc.Servlet ForwardingController">
<property name="servletName" value="default"/>
</bean>

The disadvantage of this is that everything is running through Spring, but I don't see that as a problem for us at this time. If your usage of the default mappings is more complicated, though, you'd have to redirect back to the container for other resources. I'll reply if I change course...

Last edited by ndp; Nov 29th, 2006 at 11:10 AM.
Reply With Quote
  #8  
Old Feb 8th, 2007, 05:50 AM
janskyview@gmail.com janskyview@gmail.com is offline
Junior Member
 
Join Date: Feb 2007
Posts: 2
Default My Solution

Hi guys,

I had the same requirements about having clean URLs and I really believe it's an issue about spring URL handler since mapping other web framework servlet to /* and call URLs like /user/25 /listusers and so on works like a charm.
My solution is surely not the best nor the cleanest but it's application- server independent and so far it's working with no overload.
So here it is:

web.xml:
<!-- Spring Filter -->
<filter>
<filter-name>Spring Filter</filter-name>
<filter-class>com.mycompany.web.SpringFilter</filter-class>
<init-param>
<param-name>springMapping</param-name>
<param-value>/spring</param-value>
</init-param>
<init-param>
<param-name>resourceExtensions</param-name>
<param-value>jpg,gif,png,css,js</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>Spring Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Spring Servlet -->
<servlet>
<servlet-name>Spring Servlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>Spring Servlet</servlet-name>
<url-pattern>/spring/*</url-pattern>
</servlet-mapping>

SpringFilter.java:
package com.mycompany.web;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;


public class SpringFilter implements Filter {


private FilterConfig filterConfig;
private static String springMapping;
private static List resourceExtensions;

private final Logger logger = Logger.getLogger(getClass().getName());


public void init(FilterConfig filterConfig) throws ServletException {

this.filterConfig = filterConfig;
springMapping = filterConfig.getInitParameter("springMapping");
resourceExtensions = getResourceExtensions(filterConfig.getInitParamete r("resourceExtensions"));
}

public void doFilter(final ServletRequest request, final ServletResponse response, FilterChain chain) {

HttpServletResponse httpServletResponse = (HttpServletResponse)response;
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
String requestUri = httpServletRequest.getRequestURI();

try {

if (requestUri.indexOf(springMapping) == -1 && isNotAResource(requestUri)) {

String forwardUrl = springMapping + requestUri.substring(httpServletRequest.getContext Path().length());
}
logger.debug("forwarding to " + forwardUrl);
filterConfig.getServletContext().getRequestDispatc her(forwardUrl).forward(httpServletRequest, httpServletResponse);

} else {

logger.debug("no need to forward url " + requestUri);
chain.doFilter(request, response);
}

} catch (ServletException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();
}


}

private static boolean isNotAResource (String requestUri) {

Iterator iterator = resourceExtensions.iterator();
while (iterator.hasNext()) {

if (requestUri.endsWith(iterator.next().toString())) {

return false;
}

}

return true;
}

private List getResourceExtensions (String extensions) {

StringTokenizer stringTokenizer = new StringTokenizer(extensions, ",");
List list = new ArrayList(stringTokenizer.countTokens());
while (stringTokenizer.hasMoreTokens()) {

list.add(stringTokenizer.nextToken());
}

return list;
}

public void destroy() {

this.filterConfig = null;
}


}

That's it. When you call a url like home the filter will turn it into /spring/home within the context path but if the url it's an image or css or js file there's no need change it.

Cheers,

Gianmaria
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 02:18 AM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.