[英]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.