title: Serve static pages & REST services with Spring
body: <p>I have developed a Spring application that serves Spring MVC REST services. Now I want to implement Angularjs in to the webapp directory of my Spring application. The problem is that I don't know how to correctly configure this.</p>
  
  <p>What I want to achieve:</p>
  
  <ul>
  <li>/api/... = URL that serves REST services over Spring MVC such as localhost:8080/api/user/1</li>
  <li>/... = URL that contains static html pages such as localhost:8080/index.html</li>
  </ul>
  
  <p>Also I would like that index.html will be loaded by default. </p>
  
  <p>Currently I'm using following configuration which works but it doesn't load index.html by default. But I don't know if this is really the way I should configure static pages within the Spring Container:</p>
  
  <p><strong>web.xml</strong></p>
  
  <pre><code>&lt;listener&gt;
      &lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
  &lt;/listener&gt;
  
  &lt;servlet&gt;
      &lt;servlet-name&gt;spring&lt;/servlet-name&gt;
      &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;
      &lt;init-param&gt;
          &lt;param-name&gt;dispatchOptionsRequest&lt;/param-name&gt;
          &lt;param-value&gt;true&lt;/param-value&gt;
      &lt;/init-param&gt;
  &lt;/servlet&gt;
  
  &lt;servlet-mapping&gt;
      &lt;servlet-name&gt;spring&lt;/servlet-name&gt;
      &lt;url-pattern&gt;/&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;
  </code></pre>
  
  <p><strong>applicationContext.xml</strong></p>
  
  <pre><code>&lt;context:property-placeholder properties-ref="deployProperties" /&gt;
  
  &lt;!-- Activates various annotations to be detected in bean classes --&gt;
  &lt;context:annotation-config /&gt;
  
  &lt;!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
  For example @Controller and @Service. Make sure to set the correct base-package --&gt;
  &lt;context:component-scan base-package="com.eerra" /&gt;
  
  &lt;!-- Configures the annotation-driven Spring MVC Controller programming model.
  Note that, with Spring 3.0, this tag works in Servlet MVC only! --&gt;
  &lt;mvc:annotation-driven/&gt;
  
  &lt;!-- Tell Spring what to treat as resources --&gt;
  &lt;mvc:resources mapping="/" location="/resources/ang2/app/"/&gt;
  </code></pre>
  
  <p>With the current configuration everything is working but when I try to point my browser to localhost:8080/ then a 404 not found appears instead of loading the index.html file. When i try with localhost:8080/index.html then everything works.</p>
  
  <p>Can somebody point me in to the right direction how to configure such a setup?</p>
  
  <p>Below you can see my debug.log. Problem here seems to be following: </p>
  
  <p><strong>Did not find handler method for [/]</strong></p>
  
  <p>So I guess that the DispatcherServlet is trying to map the path to a controller. But I don't know how I can avoid this.</p>
  
  <pre><code>21:55:04.896 [qtp581501261-25] DEBUG org.eclipse.jetty.http.HttpParser - filled 314/314
  21:55:04.897 [qtp581501261-25 - /] DEBUG org.eclipse.jetty.server.Server - REQUEST / on AsyncHttpConnection@f908897,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-5,l=9,c=0},r=1
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.e.j.server.handler.ContextHandler - scope null||/ @ o.m.j.p.JettyWebAppContext{/,file:/C:/Users/charms/Documents/Intellij%20Projects/cardkeeper/src/main/webapp/},file:/C:/Users/charms/Documents/Intellij%20Projects/cardkeeper/src/main/webapp/
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.e.j.server.handler.ContextHandler - context=||/ @ o.m.j.p.JettyWebAppContext{/,file:/C:/Users/charms/Documents/Intellij%20Projects/cardkeeper/src/main/webapp/},file:/C:/Users/charms/Documents/Intellij%20Projects/cardkeeper/src/main/webapp/
  21:55:04.897 [qtp581501261-25 - /] DEBUG org.eclipse.jetty.server.session - sessionManager=org.eclipse.jetty.server.session.HashSessionManager@70799896
  21:55:04.897 [qtp581501261-25 - /] DEBUG org.eclipse.jetty.server.session - session=null
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.e.jetty.servlet.ServletHandler - servlet |/|null -&gt; spring
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.e.jetty.servlet.ServletHandler - chain=springSecurityFilterChain-&gt;spring
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.e.jetty.servlet.ServletHandler - call filter springSecurityFilterChain
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/'; against '/favicon.ico*'
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/'; against '/resources/**'
  21:55:04.897 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 1 of 9 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 2 of 9 in additional filter chain; firing Filter: 'LogoutFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 3 of 9 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 4 of 9 in additional filter chain; firing Filter: 'DefaultLoginPageGeneratingFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 5 of 9 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 6 of 9 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 7 of 9 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 8 of 9 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / at position 9 of 9 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
  21:55:04.898 [qtp581501261-25 - /] DEBUG o.s.s.web.util.AntPathRequestMatcher - Checking match of request : '/'; against '/api/**'
  21:55:04.899 [qtp581501261-25 - /] DEBUG o.s.s.w.a.i.FilterSecurityInterceptor - Public object - authentication not attempted
  21:55:04.899 [qtp581501261-25 - /] TRACE o.s.w.c.s.XmlWebApplicationContext - Publishing event in Root WebApplicationContext: org.springframework.security.access.event.PublicInvocationEvent[source=FilterInvocation: URL: /]
  21:55:04.899 [qtp581501261-25 - /] DEBUG o.s.security.web.FilterChainProxy - / reached end of additional filter chain; proceeding with original chain
  21:55:04.899 [qtp581501261-25 - /] DEBUG o.e.jetty.servlet.ServletHandler - call servlet spring
  21:55:04.899 [qtp581501261-25 - /] TRACE o.s.web.servlet.DispatcherServlet - Bound request context to thread: SecurityContextHolderAwareRequestWrapper[ FirewalledRequest[ (GET /)@604241542 org.eclipse.jetty.server.Request@2403fe86]]
  21:55:04.899 [qtp581501261-25 - /] DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'spring' processing GET request for [/]
  21:55:04.899 [qtp581501261-25 - /] TRACE o.s.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping@12440d38] in DispatcherServlet with name 'spring'
  21:55:04.899 [qtp581501261-25 - /] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /
  21:55:04.912 [qtp581501261-25 - /] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [/]
  21:55:04.912 [qtp581501261-25 - /] TRACE o.s.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@c518734] in DispatcherServlet with name 'spring'
  21:55:04.912 [qtp581501261-25 - /] TRACE o.s.w.s.h.BeanNameUrlHandlerMapping - No handler mapping found for [/]
  21:55:04.912 [qtp581501261-25 - /] TRACE o.s.web.servlet.DispatcherServlet - Testing handler map [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@3c836d3d] in DispatcherServlet with name 'spring'
  21:55:04.912 [qtp581501261-25 - /] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - Matching patterns for request [/] are [/**]
  21:55:04.912 [qtp581501261-25 - /] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - URI Template variables for request [/] are {}
  21:55:04.912 [qtp581501261-25 - /] DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - Mapping [/] to HandlerExecutionChain with handler [org.springframework.web.servlet.resource.ResourceHttpRequestHandler@6abe6713] and 1 interceptor
  21:55:04.912 [qtp581501261-25 - /] TRACE o.s.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@7a614724]
  21:55:04.912 [qtp581501261-25 - /] TRACE o.s.web.servlet.DispatcherServlet - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@885cb41]
  21:55:04.912 [qtp581501261-25 - /] DEBUG o.s.web.servlet.DispatcherServlet - Last-Modified value for [/] is: -1
  21:55:04.912 [qtp581501261-25 - /] DEBUG o.s.w.s.r.ResourceHttpRequestHandler - Ignoring invalid resource path []
  21:55:04.913 [qtp581501261-25 - /] DEBUG o.s.w.s.r.ResourceHttpRequestHandler - No matching resource found - returning 404
  21:55:04.913 [qtp581501261-25 - /] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'spring': assuming HandlerAdapter completed request handling
  21:55:04.913 [qtp581501261-25 - /] TRACE o.s.web.servlet.DispatcherServlet - Cleared thread-bound request context: SecurityContextHolderAwareRequestWrapper[ FirewalledRequest[ [GET /]@604241542 org.eclipse.jetty.server.Request@2403fe86]]
  21:55:04.914 [qtp581501261-25 - /] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request
  21:55:04.914 [qtp581501261-25 - /] TRACE o.s.w.c.s.XmlWebApplicationContext - Publishing event in WebApplicationContext for namespace 'spring-servlet': ServletRequestHandledEvent: url=[/]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[spring]; session=[null]; user=[null]; time=[15ms]; status=[OK]
  21:55:04.914 [qtp581501261-25 - /] TRACE o.s.w.c.s.XmlWebApplicationContext - Publishing event in Root WebApplicationContext: ServletRequestHandledEvent: url=[/]; client=[0:0:0:0:0:0:0:1]; method=[GET]; servlet=[spring]; session=[null]; user=[null]; time=[15ms]; status=[OK]
  21:55:04.914 [qtp581501261-25 - /] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Chain processed normally
  21:55:04.914 [qtp581501261-25 - /] DEBUG o.s.s.w.c.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
  21:55:04.914 [qtp581501261-25 - /] DEBUG org.eclipse.jetty.server.Server - RESPONSE /  404 handled=true
  21:55:04.915 [qtp581501261-25] DEBUG o.e.jetty.server.AsyncHttpConnection - Enabled read interest SCEP@7cee85c5{l(/0:0:0:0:0:0:0:1:51001)&lt;-&gt;r(/0:0:0:0:0:0:0:1:8080),d=true,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@f908897,g=HttpGenerator{s=4,h=0,b=0,c=-1},p=HttpParser{s=0,l=9,c=0},r=1}
  21:55:04.915 [qtp581501261-25] DEBUG org.eclipse.jetty.http.HttpParser - filled 0/0
  </code></pre>