簡體   English   中英

Spring Boot 從 2.3.x 升級 -> 2.6.x 中斷 mvc 前進:

[英]Spring Boot Upgrade from 2.3.x -> 2.6.x Breaks mvc forward:

從 Spring Boot 2.3.x 升級到 2.6.x 后,轉發 MVC REST 調用不起作用。

我們有一個/health控制器,它返回forward:/actuator/health/ forward:/actuator/health/在控制器中失敗,但是localhost:8080/actuator/health/直接點擊時在我的瀏覽器中工作得很好。

我們有以下控制器:

@Controller
public class HealthRestController implements HealthAPI
{
   @Value("forward:/actuator/health/")
   String forwardString;

   @CrossOrigin
   @GetMapping(value = {"/health"}, produces = "application/json")
   public String getHealth() { return forwardString; }

   @GetMapping(value = {"/health_secure"}, produces = "application/json")
   public String getHealthSecure() { return forwardString; }

}

/health不需要身份驗證。

      @Override
      public void configure(WebSecurity web) throws Exception
      {  //other urls removed, for simplicity
         web.ignoring()
                 .antMatchers("/health",
                              "/actuator/health",
                              "/actuator/prometheus",
                              "/cloudfoundryapplication",
                              "/actuator/cloudfoundryapplication",
                              "/cloudfoundryapplication/**");
      }

但是當我在我的網絡瀏覽器中點擊/health時,我收到以下錯誤:

May 10, 2022 10:44:28 AM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-05-10 10:44:28 INFO  DispatcherServlet:525 -   - Initializing Servlet 'dispatcherServlet'
2022-05-10 10:44:28 INFO  DispatcherServlet:547 -   - Completed initialization in 2 ms
May 10, 2022 10:44:28 AM org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet [dispatcherServlet] threw exception
javax.servlet.ServletException: Could not resolve view with name 'forward:/actuator/health/' in servlet with name 'dispatcherServlet'
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1380)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1145)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:711)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:385)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:313)
    at com.allstate.d3.sh.commons.config.CloudMetricsConfig$2.service(CloudMetricsConfig.java:76)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1723)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

May 10, 2022 10:44:28 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [health] in context with path [/health] threw exception [Could not resolve view with name 'forward:/actuator/health/' in servlet with name 'dispatcherServlet'] with root cause
javax.servlet.ServletException: Could not resolve view with name 'forward:/actuator/health/' in servlet with name 'dispatcherServlet'
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1380)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1145)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:711)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:385)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:313)
    at com.allstate.d3.sh.commons.config.CloudMetricsConfig$2.service(CloudMetricsConfig.java:76)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1723)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

那么我們缺少什么? 為什么不forward:工作?

更新

來自 build.gradle 的依賴片段與 Spring 依賴項


    buildscript
    {
       ext { springBootVersion = '2.6.7' }
       //other stuff here

       //skip repositories

       dependencies
       {
          classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
          //other non-spring dependencies here
       }       
    }


    //dependencies

    implementation('org.springframework.boot:spring-boot-starter-web')

    implementation('org.springframework:spring-context')
    implementation('org.springframework:spring-expression')
    implementation('org.springframework:spring-beans')
    implementation('org.springframework:spring-core')

    implementation('org.springframework.boot:spring-boot-starter-webflux')

    implementation("org.springframework.cloud:spring-cloud-starter-vault-config:3.1.0")

    implementation('org.springframework.security:spring-security-web')
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-actuator")

    implementation("org.springframework.boot:spring-boot-starter-aop")

    implementation("org.springframework.kafka:spring-kafka")
    
    implementation("org.springframework.security.oauth:spring-security-oauth2:${springSecurityOauth2Version}")

    implementation "org.springframework.cloud:spring-cloud-spring-service-connector:${springCloudConnectorVersion}"
    implementation "org.springframework.cloud:spring-cloud-cloudfoundry-connector:${springCloudConnectorVersion}"

    //websocket
    implementation("org.springframework.boot:spring-boot-starter-websocket")

    // cache
    implementation 'org.springframework:spring-context'
    implementation 'org.springframework:spring-context-support'

    implementation 'org.springdoc:springdoc-openapi-ui:1.6.1'

    implementation("org.springframework.security:spring-security-jwt:${springSecurityJwtVersion}")           

應用程序上下文路徑在application.yml中設置

server:
  servlet:
    contextPath: /exe/v2

路徑暴露在上下文路徑之外,如下所示:

@Configuration
public class CloudMetricsConfig
{
    @Bean
    public TomcatServletWebServerFactory servletWebServerFactory()
    {
        return new TomcatServletWebServerFactory()
        {
            @Override
            protected void prepareContext(Host host,
                                          ServletContextInitializer[] initializers)
            {
               super.prepareContext(host, initializers);

               addContext(host, "/cloudfoundryapplication", getContextPath(),
                          "cloudfoundry");
               addContext(host, "/actuator/prometheus", getContextPath(),
                          "prometheus");
               addContext(host, "/actuator/health", getContextPath(),
                          "actuatorHealth");
               addContext(host, "/health", getContextPath(),
                          "health");
            }

        };
    }

    private void addContext(Host host, String path, String contextPath,
                            String servletName)
    {
        StandardContext child = new StandardContext();
        child.addLifecycleListener(new Tomcat.FixContextListener());
        child.setPath(path);
        ServletContainerInitializer initializer =
               getServletContextInitializer(contextPath, servletName, path);
        child.addServletContainerInitializer(initializer, Collections.emptySet());
        child.setCrossContext(true);
        host.addChild(child);
    }

    private ServletContainerInitializer getServletContextInitializer(String contextPath,
                                                                     String servletName,
                                                                     String path)
    {
       return (c, context) ->
       {
          Servlet servlet = new GenericServlet()
          {
             @Override
             public void service(ServletRequest req, ServletResponse res)
                    throws ServletException, IOException
             {
                ServletContext context = req.getServletContext().getContext(contextPath);
                context.getRequestDispatcher(path).forward(req, res);
             }
          };
          context.addServlet(servletName, servlet)//.addMapping(path);
                 .addMapping("/*");
       };
    }

剛剛檢查了 Spring Boot 2.6.7 - 按預期工作。

org.springframework.web.servlet.view.UrlBasedViewResolver#FORWARD_URL_PREFIX (即字符串"forward:" )是spring-webmvc模塊的一部分。 檢查您的項目的依賴項中是否有這個,其版本與您使用的 Spring/Boot 版本相對應。

根據@dekkard 的回答: https ://stackoverflow.com/a/72192299/659354
我從 Spring Boot 2.6.2 -> 2.6.7 升級以最小化版本覆蓋以獲得更安全的版本。 並驗證了spring-mvc的存在

最后一塊拼圖來自@RAHULBHOITE 答案中的鏈接
Spring Boot 單頁應用程序 - 將每個請求轉發到 index.html

我從我的一個配置類中刪除@EnableWebMvc
現在/health工作。 我的所有集成測試仍然通過,並且 REST 端點的手動郵遞員測試仍然有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM