SpringBoot3 代理静态资源失败

背景​

SpringBoot2升级到3时,原本配置的静态文件代理,在SpringBoot3 代理失败

SpringBoot2 配置

  1. 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 添加的是前端项目的静态文件

  1. 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 等其它异常解析器工作。

注意:

此代码仅供参考,生产环境还需多次验证

扩展

SpringBoot自定义错误页面与原理讲解

### 配置 Nginx 作为 Spring Boot 应用在云服务器上的反向代理 为了确保 Nginx 正确地充当 Spring Boot 应用程序的反向代理,需遵循特定配置原则。以下是详细的配置指南: #### 1. 基本配置示例 对于基本设置,在 `nginx.conf` 文件中的 HTTP 或者 server 块内定义如下内容[^1]: ```nginx server { listen 80; server_name yourdomain.com; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://localhost:8080/; } } ``` 此配置将所有请求转发给运行于本地端口 8080 上的 Spring Boot 应用。 #### 2. 处理上下文路径 (Context Path) 当应用程序部署在一个子路径下时,例如 `/admin`, 则应调整相应的location部分: ```nginx location /admin/ { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://localhost:8081/admin/; } ``` 这里的关键在于保持一致性的斜杠结尾,这有助于防止 URL 路径拼接错误引发的404问题[^3]. #### 3. 解决静态资源加载失败的问题 如果遇到 CSS, JS 等静态文件无法正常加载的情况,则可能需要额外指定这些资源的位置以及如何处理它们: ```nginx location ~* \.(css|js|jpg|jpeg|png|gif)$ { alias /path/to/static/files/; } location / { try_files $uri @backend; } location @backend { proxy_pass http://localhost:8080; } ``` 上述代码片段通过正则表达式匹配来定位并提供静态资产访问权限;同时利用try_files指令尝试直接服务文件或回退至后端应用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值