【问题标题】:Serve static pages & REST services with Spring使用 Spring 提供静态页面和 REST 服务
【发布时间】:2015-03-25 07:05:40
【问题描述】:

我开发了一个提供 Spring MVC REST 服务的 Spring 应用程序。现在我想在我的 Spring 应用程序的 webapp 目录中实现 Angularjs。问题是我不知道如何正确配置它。

我想要达到的目标:

  • /api/... = 通过 Spring MVC 提供 REST 服务的 URL,例如 localhost:8080/api/user/1
  • /... = 包含静态 html 页面的 URL,例如 localhost:8080/index.html

我还希望默认加载 index.html。

目前我正在使用以下配置,但默认情况下它不会加载 index.html。但我不知道这是否真的是我应该在 Spring Container 中配置静态页面的方式:

web.xml

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>dispatchOptionsRequest</param-name>
        <param-value>true</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

applicationContext.xml

<context:property-placeholder properties-ref="deployProperties" />

<!-- Activates various annotations to be detected in bean classes -->
<context:annotation-config />

<!-- 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 -->
<context:component-scan base-package="com.eerra" />

<!-- Configures the annotation-driven Spring MVC Controller programming model.
Note that, with Spring 3.0, this tag works in Servlet MVC only! -->
<mvc:annotation-driven/>

<!-- Tell Spring what to treat as resources -->
<mvc:resources mapping="/" location="/resources/ang2/app/"/>

使用当前配置,一切正常,但是当我尝试将浏览器指向 localhost:8080/ 时,会出现 404 not found 而不是加载 index.html 文件。当我尝试使用 localhost:8080/index.html 时,一切正常。

谁能指出正确的方向如何配置这样的设置?

您可以在下面看到我的 debug.log。这里的问题似乎如下:

没有找到 [/] 的处理方法

所以我猜 DispatcherServlet 正在尝试将路径映射到控制器。但我不知道如何避免这种情况。

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 -> spring
21:55:04.897 [qtp581501261-25 - /] DEBUG o.e.jetty.servlet.ServletHandler - chain=springSecurityFilterChain->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)<->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

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    您是否在 web.xml 中定义了欢迎文件?

    例如

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    

    其中 index.html 是项目根文件夹中的一个文件。

    【讨论】:

    • 是的,但这并没有成功。完全一样的事情发生了。
    【解决方案2】:

    我遇到了同样的问题,我按照您的建议解决了,在 Spring 管理的 rest api 模式前面加上前缀。

    唯一的区别是我没有使用 url 重写器,因为我所有的 angular 文件都部署在 web 应用程序的根目录中,因此可以直接访问。

    我的 Angular 项目实际上位于不同的文件夹中,作为不同的模块,并在 war build 期间使用此配置添加:

    <plugin>
         <artifactId>maven-war-plugin</artifactId>
         <version>2.3</version>
         <configuration>
             <webResources>
                 <resource>
                     <directory>${project.parent.basedir}/ANGULAR-PROJECT-NAME
                 </resource>
             </webResources>
         </configuration>
    </plugin>
    

    就像here 解释的那样。希望这会有所帮助,问候。

    【讨论】:

      【解决方案3】:

      主要问题是注册的ResourceHttpRequestHandler在请求foo/时没有解析为foo/index.html

      启用此行为的参数会很好。

      【讨论】:

        【解决方案4】:

        我已经通过以下方式解决了我的问题。

        • 首先,我将路径从 servlet-mapping 更改为 /api/*。我不得不将 MVC 控制器中的路径从 @RequestMapping("/api/role") 调整为 @RequestMapping("/role")。否则 URL 会响应 /api/api/role。
        • 然后我集成了urlrewritehttp://tuckey.org/urlrewrite/。我正在使用 Maven,所以我将它添加到 pom.xml 中。我这样做是因为 mvc:resource 映射指令似乎只在使用 DispatcherServlet 时才起作用。这就是我想为我的静态 html 页面禁用的功能。
        • 然后我将过滤器添加到 web.xml 文件中,并在 WEB-INF 目录中创建了一个 urlrewrite.xml 文件,如下所示。
        • 使用 urlrewrite 我可以设置一个条件,排除 /api/**(我的基于 Spring MVC 的 REST 控制器的目录),但将 /** 中的任何其他内容重写到 /resources/ang2/app 目录(我的 angularjs 目录)。
        • 我还从 applicationContext.xml 中删除了 mvc:resource 指令。

        web.xml

        <!-- Setup urlrewrite filter -->
        <filter>
            <filter-name>UrlRewriteFilter</filter-name>
            <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
            <init-param>
                <param-name>logLevel</param-name>
                <param-value>WARN</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>UrlRewriteFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
        
        <!-- Spring related section -->
        <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        
        <servlet>
            <servlet-name>spring</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
            <param-name>dispatchOptionsRequest</param-name>
            <param-value>true</param-value>
            </init-param>
        </servlet>
        
        <servlet-mapping>
            <servlet-name>spring</servlet-name>
            <url-pattern>/api/*</url-pattern>
        </servlet-mapping>
        

        urlrewrite.xml

        <!-- Use urlrewritefilter to redirect / requests to angularjs directory -->
        <urlrewrite default-match-type="wildcard">
        <rule>
            <!-- exclude everything in /api/** directory -->
            <condition type="request-uri" operator="notequal">/api/**</condition>
            <!-- everything else from / is redirected to the angularjs directory -->
            <from>/**</from>
            <to>/resources/ang2/app/$1</to>
        </rule>
        </urlrewrite>
        

        【讨论】:

          猜你喜欢
          • 2017-03-05
          • 1970-01-01
          • 2023-03-13
          • 2013-02-20
          • 1970-01-01
          • 2012-09-06
          • 1970-01-01
          • 2020-10-25
          • 1970-01-01
          相关资源
          最近更新 更多