How to access static resources when mapping a global front controller servlet on /*
I've run into this also and never found a great solution. I ended up mapping my servlet one level higher in the URL hierarchy:
<servlet-mapping>
<servlet-name>home</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
And now everything at the base context (and in your /res directory) can be served up by your container.
How to prevent static resources from being handled by front controller servlet which is mapped on /*
You have 2 options:
Use a more specific URL pattern such as
/app/*
or*.do
and then let all your page requests match this URL pattern. See also Design Patterns web based applicationsThe same as 1, but you want to hide the servlet mapping from the request URL; you should then put all static resources in a common folder such as
/static
or/resources
and create a filter which checks if the request URL doesn't match it and then forward to the servlet. Here's an example which assumes that your controller servlet is a@WebServlet("/app/*")
and that the filter is a@WebFilter("/*")
and that all your static resources are in/resources
folder.HttpServletRequest req = (HttpServletRequest) request;
String path = req.getRequestURI().substring(req.getContextPath().length());
if (path.startsWith("/resources/")) {
chain.doFilter(request, response); // Goes to default servlet.
} else {
request.getRequestDispatcher("/app" + path).forward(request, response); // Goes to your controller.
}See also How to access static resources when mapping a global front controller servlet on /*.
How to allow access to static content when having default servlet
When you map /* to a specific servlet, all requests will be forwarded to that servlet, unless you provide a more explicit mapping to another servlet.
That is, if you have /* mapped to ServletA, and /static/* mapped to ServletB, then following Servlets will get called.
http://localhost:8080/abc.jpg -> ServletA
http://localhost:8080/static/abc.jpg -> ServletB
http://localhost:8080/xyz/abc.jpg -> ServletA
So one option you have is to write a Servlet to handle the static content, which will grab the file and return it as response. You can map that servlet to a prefixed by something like /static/*. This requires that all URL references to your static files to be updated to contain this '/static' part.
If that is not feasible for you, then probably you can use the same servlet, but mapped to multiple URL patterns (probably by extension) as follows.
<servlet>
<servlet-name>static-servlet</servlet-name>
<servlet-class>xxx.yyy.StaticServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
If you want this to be fine-grained to the level of each file, you can map the servlet to your file URL as well.
Match for root url and serving of static resources
I will begin with a preliminary remark about how DefaultServlet
works. According to Servlet 3.0 specifications, containers generally provide a default servlet, that has lowest priority and serves static context. The mapping /
is the implicit mapping for this default servlet.
Now for the solutions :
Map spring controllers to a sub-hierarchy
That is the easiest solution : you map Spring DispatcherServlet
to /pages
, or to /pages
and /api
for example. The default servlet will then serve all other URLs (including root). To serve root controller, you can map a controller to /home
(for example) and have a /index.jsp
containing <jsp:forward page="/home"/>
- this is a method of current use in other frameworks using extension mapping such as Struts (*.do
for old Struts1).
Drawbacks : having url stating with /pages
is not very nice.
Map resources to a sub-hierarchy
This solution is highly used in the referenced pages. Spring DispatcherServlet
is mapped to /*
and so gets all the requests (unless a more specific mapping exists). To serve static resources, you just declare a ResourceHttpRequestHandler
, using in XML :
<mvc:resources mapping="/resources/**" location="/public-resources/"/>
or in java configuration :
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/");
}
}
This works very fine, and you can map a Spring controller to /
directly.
Drawbacks : you cannot serve static resources that would be directly under root context.
Map DispatcherServlet
as the default servlet
Mapping Spring DispatcherServlet
to /
is in fact replacing the default servlet from the container to process all not already processed URLs. With this mapping, Spring can fallback to the original default servlet for URLs not mapped to controllers. For that to work, you have to configure a DefaultServletHttpRequestHandler
with a URL mapping of "/**"
and the lowest priority. You do it using XML :
<mvc:default-servlet-handler/>
or in java configuration :
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
That way, DispatcherServlet
normally calls all controllers, and have the original default servlet to serve static (not mapped) resources. Unfortunately, this does not work for the root URL, and you must use the <jsp:forward page="..."/>
trick like for first solution.
Drawbacks :
- cannot directly map root URL and need the
index.jsp
<jsp:forward page="..."/>
trick - As Spring as replaced original container default servlet, it must call it by name. It works for common containers (including Tomcat, Jetty, GlassFish, JBoss, Resin, WebLogic, and WebSphere), or you can also give the name for default servlet as an attribute in XML config (
<mvc:default-servlet-handler default-servlet-name="customDefaultServlet"/>
) or as a parameter if java configuration:configurer.enable("customDefaultServlet");
References : Spring Reference Manual / Web MVC framework / Serving of Resources
Servlet for serving static content
I ended up rolling my own StaticServlet
. It supports If-Modified-Since
, gzip encoding and it should be able to serve static files from war-files as well. It is not very difficult code, but it is not entirely trivial either.
The code is available: StaticServlet.java. Feel free to comment.
Update: Khurram asks about the ServletUtils
class which is referenced in StaticServlet
. It is simply a class with auxiliary methods that I used for my project. The only method you need is coalesce
(which is identical to the SQL function COALESCE
). This is the code:
public static <T> T coalesce(T...ts) {
for(T t: ts)
if(t != null)
return t;
return null;
}
Related Topics
Why Must Wait() Always Be in Synchronized Block
How to Parse a Dynamic JSON Key in a Nested JSON Result
How to Set Addsnapshotlistener and Remove in Populateviewholder in Recyclerview Item
Tomcat Doesn't Stop. How to Debug This
Java -Xbootclass Path Is No Longer a Supported Option
Benefits/Drawbacks to Running 64-Bit Jvm on 64-Bit Linux Server
What Could Be Reason for "Prism-Es2 Error:Gl_Version (Major.Minor) = 1.4"
Connection Pooling Options With Jdbc: Dbcp VS C3P0
How to Change a Jfreechart'S Size
Android Listview Selected Item Stay Highlighted
Recyclerview Horizontal Scrolling to Left
Android N Change Language Programmatically
Problems with Java3D Lib Configuration