Serving static content in Spring MVC (3.1.1) - spring

My dispatcher servlet maps to the root of the app.
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
I have a folder called 'static' in my webroot. It contains CSS, JS and image files. However, because of the dispatcher servlet mapping, the requests for static contents end up in 404s.
I know the solutions lying around to address this.
Make dispatcher map to a more specific URL, say :context:/app/, and then write a filter to intercept requests, and map conditionally to the default servlet, or else delegate to the spring dispatcher.
The URL rewrite trick.
using <mvc:resources />
Problem is, my mappings are XML based, and I will absolutely not scatter my mappings config all over the place in the name of using annotations. So if I use <mvc:resources />, my xml based mappings break, and all url mappings to different controllers are lost.
This is becase <mvc:resources /> overrides some settings and applies its own. But it is also the cleanest solution for static content.
Any way available to tell <mvc:resources /> to not override my xml based mappings?

<mvc:resources /> appears to be a perfect fit for your problem.
From what I understand, your DispatcherServlet is handling all requests to your server. So the resource tag should return the files at the location specified in the mvc:resources location attribute. It should not be catching anything other than what's mapped.
Are you using something along the lines of
<mvc:resources mapping="/static/**" location="/static/"/>
If it is overriding settings that aren't configurable in the tag consider instantiating your own org.springframework.web.servlet.resource.ResourceHttpRequestHandler

I do have this in the web.xml
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>

You can provide a file extention for your controllers, e.g.
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>
Then all URLs ending in .do will go through springs DispatcherServlet.

add <mvc:default-servlet-handler/> towards the top of your web.xml file
or if you are you using annotations
#Configuration
#EnableWebMvc
public class MVCConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable("default");
}
}

Related

Spring DispatcherServlet matches for wrong url-pattern

I have following configuration in the Spring based project -
web.xml -
<servlet>
<servlet-name>spring</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-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
Handler mapping from spring-servlet.xml -
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true" />
</bean>
and Spring Controller as -
#RestController
public class TestController {
#PostMapping("/test")
public String test() {
return "hello";
}
}
Now when I send request to
GET http://localhost:8080/test
I get response as hello which is fine but when I send request on following URL's -
GET http://localhost:8080/api/test
GET http://localhost:8080/img/test
then also I get hello response i.e. controller code gets executed for above mentioned wrong URLs.
Is there anything wrong in the configuration or it's expected behaviour?
The mapping of an URL to a handler (a method) in Spring MVC is a two-step process:
first the servlet container must decide whether to forward the request to the DispatcherServlet according to the <servlet-mapping>,
then the DispatcherServlet consults its HandlerMappings to see which one matches the request. A HandlerMapping can do anything, but usually calls UrlPathHelper#getLookupPathForRequest to transform the request into a path.
The default behavior of the UrlPathHelper depends on the <servlet-mapping> used:
if you used an exact mapping as in the case of /test, a request for /test will give a path of /test,
if you used a prefix mapping /api/*, a request for /api/foo/bar will result in a path of only /foo/bar.
As mentioned in the comment by M. Deinum you need to set the alwaysUseFullPath property to true on the RequestMappingHandlerMapping instance used by the DispatcherServlet.
In your XML application context configuration you define a bean of class RequestMappingHandlerMapping, while you should overwrite the bean used by the DispatcherServlet. Therefore you need to use the same id internally used by Spring:
<mvc:annotation-driven/>
<bean id="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
</bean>
Remark: This solution should work unless you invert the order of the definitions in your XML. In such a case the default definition will overwrite yours. Set the logger org.springframework.beans.factory.support do DEBUG to check which definition overwrites the other. In one case you get:
Overriding user-defined bean definition for bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' with a framework-generated bean definition: ...
in the other:
Overriding bean definition for bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' with a different definition: ...

How to map multiple Controller in java Spring MVC in context.xml

I have 2 controllers(Home,Rest) in my application and I would like to add mapping for both of them in web.xml and applicationContext.xml.How would I do that ?
Please read and include this guide in your project it is helpfull for your.
Please define your class is controller using #Controller annotation
include this in web.xml file
<servlet>
<servlet-name>springweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springweb</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</servlet>
3.Write this in servlet-context.xml file
<context:component-scan base-package="mypack.controller" />
<context:annotation-config></context:annotation-config>
In base-package you can give your root package name.
When your web request is arrived in servlet-context.xml file, it is redirected to specific controller using base-package attribute you have defined.

Spring MVC DispatcherServlet mapping / vs /*

<servlet>
<servlet-name>springmvcdemo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvcdemo</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
vs
<servlet>
<servlet-name>springmvcdemo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvcdemo</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
I know there are duplicated questions but i'm still confused. My understanding is that when using /* , every request will go through this servlet (It means all .jsp, .html,etc will end up in this ). / will make this servlet the default servlet ( if there are exact URL installed..., return ) But it seem to me that when using / every request all still go through The DispatcherServlet no matter what. I can't open any .jsp file directly. Can someone explain to me more about this?
As per the Servlet specification, mapping for "/" means default servlet meaning if there is no explicit servlet matching the request, then this default servlet would be serving the request. For e.g., there is a servlet named "default" defined in Tomcat server common configuration web.xml which is inherited by all applications. This servlet serves the static contents like css,images etc which are typically not mapped in applications web.xml. Similarly there is a special Servlet which handles requests for jsp files ( all request ending with *.jsp as naturally these will be needed to be compiled to Servlets which would then process the request). So if you override the default servlet to be any other servlet in the application web.xml, then all requests not handled by any other servlet goes to this servlet and if this Servlet is not capable to serving request, it will not work.
If you declare Spring dispatcher servlet as the default Servlet, then you will not be able to serve static contents from container provided Servlet. Instead there is a special handler provided which can load static resources from configurable path pattern from directory / classpath. You need to use <mvc:resources/> tag for this feature. However if you still want to use container provided Servlet for serving resource you would need to use
<mvc:default-servlet-handler/> in the spring configuration. You can read more about this approach and its prons/cons here - section 15.12.4

How to get a trivial case of Spring MVC view (JSP) resolving to work?

My app uses Spring MVC (latest; 3.2.2) to create a RESTful API returning JSON, and so far I haven't needed a view layer at all. But now, besides the API, I need a simple utility page (plain dynamic HTML) and wanted to use JSP for that.
I want requests to http://localhost:8080/foo/<id> to go through a controller (Java) and end up in a JSP. Should be simple, right? But I'm getting 404; something is not right in resolving the view.
HTTP ERROR 404
Problem accessing /jsp/foo.jsp. Reason:
Not Found
Controller:
#RequestMapping(value = "/foo/{id}")
public String testing(#PathVariable String id, ModelMap model) {
model.addAttribute("id", id);
return "foo";
}
Defining controllers and mapping requests works; this method gets called just fine.
Spring config:
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/jsp/" p:suffix=".jsp"/>
The problem is probably here. I've experimented with slightly different prefixes and putting the JSPs under WEB-INF, as well as stuff like <mvc:view-controller path="/*" /> but no luck yet.
(Do I even need to specify InternalResourceViewResolver, or should default view resolvers take care of this?)
JSP files. Under src/main/webapp/jsp (the project uses Maven conventions) I obviously have the JSPs.
Is there something wrong with this location?
web.xml:
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
I have browsed through Spring MVC documentation, but my problem is probably too trivial and obvious to easily find help there. :-P
Can anyone enlighten me on what I'm doing wrong?
I think what you need to do is changing
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
to
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
/* won't match if there is another folder in the path, like /jsp/foo.jsp. On the other hand / will match everything.

Spring: How to redirect non-dispatcher requests to dispatcher?

I have standard servlet-mapping for Dispatcher Servlet - /app/*. I have controller that handle /notify requests. I need to expose this controller as servlet on http://[SERVER]/notify. How to simple redirect all requests from http://[SERVER]/notify to http://[SERVER]/app/notify (but without other tools, like urlrewrite) ? I know i can write simple servlet instead of, and set servlet-mapping in web.xml, but want to have controller, not servlet ;)
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
Controller:
#Controller
public class PaymentNotificationController {
#RequestMapping("/notify")
void notify() { ... }
}
You can put another Dispatcher Servlet in
<servlet-mapping>
<servlet-name>Notification Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/notify/*</url-pattern>
</servlet-mapping>
and configure it with the same XML file as your main dispatcher servlet.
Don't discount urlrewrite - it only takes nanoseconds to execute, and years have gone into making it as fast as possible.
If you do throw up another Spring MVC servlet, you'll wind up with a second application context, which may not be desirable. DispatcherServlet is a front controller, of which there is supposed to be one - so yes - you can put as many as you want in, but they're almost like little mini-apps inside your WAR.

Resources