背景
SpringBoot2升级到3时,原本配置的静态文件代理,在SpringBoot3 代理失败
SpringBoot2 配置
- mvc配置
@Configuration
public class EnjoyWebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/enjoy/**").addResourceLocations("file:///home/enjoy/software/static/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
其中第1、2个addResourceHandler 添加的是前端项目的静态文件
- tomcat 配置 404 页面
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return factory -> {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
factory.addErrorPages(error404Page);
};
}
在原来的SpringBoot2中,上面的配置是生效的。当页面访问静态资源时,没找到资源文件,会返回index.html给前端。前端根据index.html中的路由信息,跳转到对应的页面。
升级SpringBoot3后,访问不到静态资源,且无法跳转页面。
源码排查
SpringBoot2 源码
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// For very general mappings (e.g. "/") we need to check 404 first
Resource resource = getResource(request);
if (resource == null) {
logger.debug("Resource not found");
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
ResourceHttpRequestHandler 根据资源路径找不到资源时,调用 response.sendError(HttpServletResponse.SC_NOT_FOUND); 标记http状态码为404,资源找不到
执行到最后时,会走到tomcat处理状态码的逻辑中,根据http状态码404,返回对应的错误页
SpringBoot3 源码
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// For very general mappings (e.g. "/") we need to check 404 first
Resource resource = getResource(request);
if (resource == null) {
logger.debug("Resource not found");
throw new NoResourceFoundException(HttpMethod.valueOf(request.getMethod()), getPath(request));
}
ResourceHttpRequestHandler 根据资源路径找不到资源时,抛出异常 throw new NoResourceFoundException,直接结束掉此次请求。所以也不会走到tomcat处理状态码的逻辑中,返回对应的错误页
SpringBoot3 自定义异常解析
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver, Ordered {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public ModelAndView resolveException(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, Object handler, @NotNull Exception ex) {
if (handler instanceof ResourceHttpRequestHandler && ex instanceof NoResourceFoundException) {
ModelAndView modelAndView = new ModelAndView("/index.html");
response.setStatus(HttpStatus.OK.value());
return modelAndView;
}
return null;
}
}
自定义异常解析,当handler是ResourceHttpRequestHandler,且抛出NoResourceFoundException 异常时,将页面重定向到index.html页面。
其它的异常不做处理,直接返回null。避免影响后面HandlerExceptionResolverComposite 等其它异常解析器工作。
注意:
此代码仅供参考,生产环境还需多次验证