The familiar code:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My understanding is that /* maps to http://host:port/context/*.
How about /? It sure doesn't map to http://host:port/context root only. In fact, it will accept http://host:port/context/hello, but reject http://host:port/context/hello.jsp.
Can anyone explain how is http://host:port/context/hello mapped?
<url-pattern>/*</url-pattern>
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
<url-pattern>/</url-pattern>
The / doesn't override any other servlet. It only replaces the servletcontainer's built in default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's built in default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's built in JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
<url-pattern></url-pattern>
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
Front Controller
In case you actually intend to have a front controller servlet, then you'd best map it on a more specific URL pattern like *.html, *.do, /pages/*, /app/*, etc. You can hide away the front controller URL pattern and cover static resources on a common URL pattern like /resources/*, /static/*, etc with help of a servlet filter. See also How to prevent static resources from being handled by front controller servlet which is mapped on /*. Noted should be that Spring MVC has a built in static resource servlet, so that's why you could map its front controller on / if you configure a common URL pattern for static resources in Spring. See also How to handle static content in Spring MVC?
I'd like to supplement BalusC's answer with the mapping rules and an example.
Mapping rules from Servlet 2.5 specification:
Map exact URL
Map wildcard paths
Map extensions
Map to the default servlet
In our example, there're three servlets. / is the default servlet installed by us. Tomcat installs two servlets to serve jsp and jspx. So to map http://host:port/context/hello
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Doesn't match any extensions, next.
Map to the default servlet, return.
To map http://host:port/context/hello.jsp
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Found extension servlet, return.
Perhaps you need to know how urls are mapped too, since I suffered 404 for hours. There are two kinds of handlers handling requests. BeanNameUrlHandlerMapping and SimpleUrlHandlerMapping. When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. One thing we need to know is these two handlers share a common property called alwaysUseFullPath which defaults to false.
false here means Spring will not use the full path to mapp a url to a controller. What does it mean? It means when you define a servlet-mapping:
<servlet-mapping>
<servlet-name>viewServlet</servlet-name>
<url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
the handler will actually use the * part to find the controller. For example, the following controller will face a 404 error when you request it using /perfix/api/feature/doSomething
#Controller()
#RequestMapping("/perfix/api/feature")
public class MyController {
#RequestMapping(value = "/doSomething", method = RequestMethod.GET)
#ResponseBody
public String doSomething(HttpServletRequest request) {
....
}
}
It is a perfect match, right? But why 404. As mentioned before, default value of alwaysUseFullPath is false, which means in your request, only /api/feature/doSomething is used to find a corresponding Controller, but there is no Controller cares about that path. You need to either change your url to /perfix/perfix/api/feature/doSomething or remove perfix from MyController base #RequestingMapping.
I think Candy's answer is mostly correct. There is one small part I think otherwise.
To map host:port/context/hello.jsp
No exact URL servlets installed, next.
Found wildcard paths servlets, return.
I believe that why "/*" does not match host:port/context/hello because it treats "/hello" as a path instead of a file (since it does not have an extension).
The essential difference between /* and / is that a servlet with mapping /* will be selected before any servlet with an extension mapping (like *.html), while a servlet with mapping / will be selected only after extension mappings are considered (and will be used for any request which doesn't match anything else---it is the "default servlet").
In particular, a /* mapping will always be selected before a / mapping. Having either prevents any requests from reaching the container's own default servlet.
Either will be selected only after servlet mappings which are exact matches (like /foo/bar) and those which are path mappings longer than /* (like /foo/*). Note that the empty string mapping is an exact match for the context root (http://host:port/context/).
See Chapter 12 of the Java Servlet Specification, available in version 3.1 at http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html.
I am having a problem with url mappings and thought somebody might help me :-)
My Spring MVC application has a dispatcherServler's mapping as follows:
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Then I have a controller servlet with a method annotated like this:
MyServlet {
....myMethod
#RequestMapping(value = "/qwert/request", method = RequestMethod.POST)
To conclude I have a DelegatingFilterProxy with a mapping:
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/qwert/request</url-pattern>
</filter-mapping>
whose target is to intercept all requests directed to the aforementioned MyServlet's method.
The application is working fine for the typical request localhost:port/MyApp/qwert/request which means that the filter is intercepting requests and doing its business.
The problems is that a request like this localhost:port/MyApp/qwert/request.do is getting directly into the Servlet (MyServlet) method without passing through the Filter. My #RequestMapping is not /qwert/request.do, how can the request end up arriving in the servlet?
Does anyone have any idea how to solve this without changing my dispatcherServlet mapping to something like *.do and making other changes accordingly.
I would like my application to serve requests under localhost:port/MyApp/qwert/request and not localhost:port/MyApp/qwert/request.whatever and I cannot change the filter mapping to /* since there are other methods that do not require the filter intervention.
Thanks
Update 1:
Yes, I tried to introduce a filter's url-pattern like /qwert/request.* but in that case the filter does not intercept any request. Neither localhost:port/MyApp/qwert/request nor localhost:port/MyApp/qwert/request.whatever (being the first one the one normal callers should be using)
Solution
At the end I found what the problem was, #Jhonathan pointed me in the right direction
I had to define a RequestMappingHandlerMapping instead of a DefaultAnnotationHandlerMapping
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
// no dot like names will be matched
mapping.setUseSuffixPatternMatch(false);
// no trailing slash will be matched
mapping.setUseTrailingSlashMatch(false);
return mapping;
}
That did the trick and I can now see internally that the pattern does not mach "wrong" requests like the ones I mentioned at the beginning.
Thank you all
First Question
My #RequestMapping is not /qwert/request.do, how can the request end up arriving in the servlet?
Spring by default take
/qwert/request.do
/qwert/request.whatever
/qwert/request.*
like
/qwert/request
therefore your #RequestMapping(value = "/qwert/request", method = RequestMethod.POST)take request. Change in your
DefaultAnnotationHandlerMapping for change this default option:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
From Spring Source:
setUseDefaultSuffixPattern
public void setUseDefaultSuffixPattern(boolean useDefaultSuffixPattern)
Set whether to register paths using the default suffix pattern as well: i.e. whether "/users" should be registered as "/users." and "/users/" too.
Default is "true". Turn this convention off if you intend to interpret your #RequestMapping paths strictly.
Note that paths which include a ".xxx" suffix or end with "/" already will not be transformed using the default suffix pattern in any case.*
I have a requirement to rewrite HTML generated by a web application. The requirement applies to all pages equally so naturally we went for a Filter.
I cribbed the stream wrapping approach from this Oracle documentation on filters and this works for most cases. Unfortunately, if the servlet throws an exception the flow of execution leaves my filter and the rewriting logic is not executed. This means the HTML of error pages is not modified.
I want to intercept the error page response as well. How do I do that?
Try adding this to your filter-mapping:
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
This should be relatively simple to do but I've yet to find a description of how to do it.
My setup is a simple web app that processes every request through a servlet (I'll call it MyEverythingServlet for this question). Here's a slightly modified version of my web.xml:
<servlet>
<servlet-name>MyEverythingServlet</servlet-name>
<servlet-class>blah.blah.blah.MyEverythingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyEverythingServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Right now, the servlet is pretty simple and either does some work (when work.do is part of the path) and if a .txt file is specified in the path, we'll do some validation and then load the file and send the text as the response:
response.getOutputStream().print( content );
What I'd like to do is either:
Inside the servlet, if the request is a URL to a .jsp file, I'd like to be able to have the container interpret the JSP scriptlet parts/taglib stuff before I write the String to the response.
Change my web.xml to have it process .jsp files outside of MyEverythingServlet.
Inside the servlet, if the request is a URL to a .jsp file, I'd like to be able to have the container interpret the JSP scriptlet parts/taglib stuff before I write the String to the response.
There's no direct API available which processes JSP files programmatically like that. In theory, you'd like to have the JSP in public webcontent and forward the request to the JSP. You can do this with RequestDispatcher#forward().
if (request.getServletPath().endsWith(".jsp")) {
request.getRequestDispatcher(request.getServletPath()).forward(request, response);
} else {
// Write "plain" to response as you did.
}
You may only want to do some checks on the correctness of the paths, but this should at least give you the generic idea. There is however a little problem: the servlet will be invoked recursively since it's mapped on /*. You'd rather replace this MyEverythingServlet by a Filter which just delegates the remnant of the job to the appserver's builtin default servlet. Having a Servlet to listen on /* is already a design-smell indication that it should have been a Filter from the beginning on ;)
Change my web.xml to have it process .jsp files outside of MyEverythingServlet.
You can't have a "negative" url-pattern in web.xml. Best what you can do is to let the servlet listen on a more specific url-pattern like *.txt or /static/* and keep the JSP files there outside.
I'm attempting to integrate SiteMesh into a legacy application using Tomcat 5 as my a container. I have a main.jsp that I'm decorating with a simple decorator.
In decorators.xml, I've just got one decorator defined:
<decorators defaultdir="/decorators">
<decorator name="layout-main" page="layout-main.jsp">
<pattern>/jsp/main.jsp</pattern>
</decorator>
</decorators>
This decorator works if I manually go to http://example.com/my-webapp/jsp/main.jsp. However, there are a few places where a servlet, instead of doing a redirect to a jsp, does a forward:
getServletContext().getRequestDispatcher("/jsp/main.jsp").forward(request, response);
This means that the URL remains at something like http://example.com/my-webapp/servlet/MyServlet instead of the jsp file and is therefore not being decorated, I presume since it doesn't match the pattern in decorators.xml.
I can't do a <pattern>/*</pattern> because there are other jsps that do not need to be decorated by layout-main.jsp. I can't do a <pattern>/servlet/MyServlet*</pattern> because MyServlet may forward to main.jsp sometimes and perhaps error.jsp at other times.
Is there a way to work around this without expansive changes to how the servlets work? Since it's a legacy app I don't have as much freedom to change things, so I'm hoping for something configuration-wise that will fix this.
SiteMesh's documentation really isn't that great. I've been working mostly off the example application that comes with the distribution. I really like SiteMesh, and am hoping I can get it to work in this case.
My understanding is that SiteMesh is integrated into the application as a Servlet filter. By default, servlet filters are only invoked against the original incoming request (in your case, the request to the servlet). Subsequent forward or include requests are not passed throuh the filter, and therefore will not be passed through sitemesh.
You can, however, instruct the filter to be invoked on forwards, using something like this:
<filter-mapping>
<filter-name>sitemesh</filter-name>
<servlet-name>MyServlet</servlet-name>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Which instructs the container to only operate on FORWARD requests. The other options are INCLUDE and REQUEST, you can have several elements.
So your options are to either change your filter config to specify FORWARD, or to change your filter-mapping to match the servlet path, rather than the JSP path. Either one should work.