学习日报 20260423|[特殊字符] 深度解析:Vue 3 SPA 部署到 Spring Boot 的 404/500 错误排查与完美解决方案-2

在现代前后端分离的开发模式中,将 Vue 单页应用(SPA)打包并嵌入 Spring Boot 后端进行统一部署是一种非常常见的架构。然而,这种部署方式经常会遇到 404 Not Found500 Internal Server Error 的坑。

本文将结合实战案例,从路由模式判断打包配置后端过滤器,全方位剖析这个问题。


🧐 第一步:确认前提------你的 Vue 是 History 模式吗?

在解决问题之前,必须先确认你的前端路由模式。这是导致问题的根源。

1. 代码层面的"铁证"

打开你的 Vue 项目中的路由配置文件(通常是 src/router/index.jssrc/router/index.ts)。

  • 如果是 History 模式(本文讨论的场景)
    代码中会使用 createWebHistory()

    java 复制代码
    import { createRouter, createWebHistory } from 'vue-router'
    
    const router = createRouter({
      history: createWebHistory(), // 👈 关键代码:这就是 History 模式
      routes
    })
  • 如果是 Hash 模式
    代码中会使用 createWebHashHistory()

    复制代码
    // history: createWebHashHistory(), // 这种模式 URL 会带 # 号

2. 浏览器地址栏的直观表现

  • History 模式 :URL 干净清爽,没有特殊符号。
    • 例如:http://localhost:8080/user
  • Hash 模式 :URL 中带有 # 号。
    • 例如:http://localhost:8080/#/user

结论 :如果你的代码是 createWebHistory() 且 URL 没有 #,那么你遇到的 404 错误就是因为服务器无法识别前端路由路径。


⚙️ 第二步:打包配置------base 必须设置为 /

在将 Vue 项目打包放入 Spring Boot 之前,必须确保 vite.config.ts(或 vue.config.js)中的基础路径配置正确。

为什么要设置 base: '/'

Spring Boot 默认将 src/main/resources/static 目录作为 Web 应用的根路径(/)。为了让打包后的 index.html 能正确引用 assets 目录下的 JS 和 CSS 文件,我们需要告诉 Vite:所有资源都基于根路径查找

正确配置示例

TypeScript 复制代码
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  base: '/', // 👈 关键配置:设置为根路径
  build: {
    outDir: '../src/main/resources/static', // 输出到 Spring Boot 的 static 目录
    emptyOutDir: true,
  }
})

如果 base 设置错误(例如设置为 ./ 或其他路径),即使后端配置正确,页面也会因为加载不到 JS/CSS 而显示空白。


📖 第三步:错误案例剖析(404 与 500)

案例 1:500 错误 - Whitelabel Error Page

很多开发者在遇到 404 时,会尝试使用 WebMvcConfigurer 来解决,结果导致了 500 错误。

错误代码(避坑指南)
java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // ❌ 错误做法:会导致循环转发
        registry.addViewController("/*")
                .setViewName("forward:/index.html");
    }
}
原因分析
  1. 用户访问 /,被转发到 /index.html
  2. Spring Boot 的静态资源处理器试图处理 /index.html
  3. 由于配置冲突,请求在 DispatcherServlet 和静态资源处理器之间死循环
  4. 最终导致 java.lang.StackOverflowError500 错误

案例 2:404 错误 - 跳转子页面或刷新时

场景重现
  • 首页 localhost:8080/ 正常显示。
  • 点击跳转 /window 正常显示(前端路由拦截)。
  • 刷新 /window 页面直接输入 URL404 Not Found
原因分析

当浏览器请求 /window 时,请求发送到了服务器。Spring Boot 在 static 目录下找不到名为 window 的文件或文件夹,也没有对应的 @Controller 映射,因此返回 404。


✅ 第四步:正确解决方案------SpaForwardFilter

解决 SPA 路由问题的最佳实践是使用 Filter(过滤器)。Filter 的执行时机早于 Spring MVC,可以避免与静态资源处理器的冲突。

完整代码

java 复制代码
package com.example.liverank.config;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class SpaForwardFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String path = httpRequest.getRequestURI();

        // 1. 放行 API 请求
        if (path.startsWith("/api/")) {
            chain.doFilter(request, response);
            return;
        }

        // 2. 放行静态资源(包含 . 的路径,如 .js, .css, .png)
        if (path.contains(".")) {
            chain.doFilter(request, response);
            return;
        }

        // 3. 放行根路径
        if (path.equals("/")) {
            chain.doFilter(request, response);
            return;
        }

        // 4. 核心逻辑:将所有其他请求转发到 index.html
        // 让 Vue Router 去处理这些路径
        request.getRequestDispatcher("/index.html").forward(request, response);
    }
}

代码逐行解析

  1. @Component:将过滤器注册为 Spring Bean,使其生效。
  2. path.startsWith("/api/"):如果是后端接口请求,直接放行,交给 Controller 处理。
  3. path.contains(".") :如果是静态资源请求(如 /assets/index.js),直接放行,交给默认的资源处理器。
  4. forward("/index.html") :对于 /window/admin 等未知路径,服务器内部转发到 index.html。浏览器 URL 保持不变,Vue 加载后根据 URL 渲染对应组件。

🔍 第五步:原理深度解析

请求处理链的优先级

使用 Filter 能够解决问题的核心在于它的执行时机:

复制代码
浏览器请求
  ↓
┌─────────────────────────────────────┐
│  Filter (SpaForwardFilter)          │ <--- 最早拦截
│  ├─ 是 API? → 放行                  │
│  ├─ 是资源? → 放行                  │
│  └─ 是路由? → 转发到 index.html     │
└─────────────────────────────────────┘
  ↓
┌─────────────────────────────────────┐
│  DispatcherServlet (Spring MVC)     │
│  (此时请求已经是 /index.html)       │
└─────────────────────────────────────┘

Filter vs WebMvcConfigurer

特性 Filter (推荐) WebMvcConfigurer (不推荐)
执行时机 Servlet 容器层面(最早) DispatcherServlet 层面
静态资源冲突 不会冲突 会冲突 (导致 500)
适用范围 所有请求 仅 Spring MVC 管理的请求

🗣️ 第六步:面试回答模板

如果面试官问:"Vue 部署到 Spring Boot 遇到 404/500 怎么解决?" 你可以这样回答:

"这个问题通常出现在 Vue 使用 History 模式路由时。

404 的原因 是:刷新页面时,浏览器向服务器请求了 /sub-page 这样的路径,但 Spring Boot 找不到对应的 Controller 或静态资源,所以返回 404。

500 的原因 是:如果尝试用 WebMvcConfigurer 进行转发,容易和 Spring Boot 的静态资源处理器产生循环转发,导致栈溢出。

我的解决方案 是使用一个 Filter(过滤器)

在 Filter 中,我通过判断请求路径:

  1. 如果是 /api/ 开头的接口请求,或者包含 . 的静态资源请求,直接放行。
  2. 其他所有路径,统一使用 request.getRequestDispatcher("/index.html").forward() 转发到首页。

这样既避免了冲突,又能让 Vue Router 正确接管路由。另外,在打包时,我也会确保 Vite 的 base 配置为 '/'。"


📌 第七步:总结与扩展

核心知识点总结

问题 原因 解决方案
404 Not Found 服务器不认识前端路由路径 使用 Filter 转发所有非 API/非资源请求到 index.html
500 Error WebMvcConfigurer 导致循环转发 改用 Filter,它在更早的阶段拦截请求
页面空白 资源路径引用错误 检查 vite.config.ts 中的 base: '/'

扩展知识:Forward vs Redirect

  • Forward (转发) :服务器内部行为,URL 地址栏不变 ,请求次数为 1 次。(SPA 必须用这个)
  • Redirect (重定向) :浏览器行为,URL 地址栏改变,请求次数为 2 次。

生产环境最佳实践

在实际生产环境中,通常不使用 Spring Boot 托管静态文件,而是使用 Nginx 反向代理:

复制代码
location / {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html; # Nginx 的 SPA 配置
}

但掌握 Spring Boot 内部的 Filter 解决方案,对于理解 Java Web 请求生命周期非常有帮助!

相关推荐
青槿吖3 小时前
第二篇:从复制粘贴到自定义规则!Spring Cloud Gateway 断言 + 过滤全玩法,拿捏微服务流量管控
java·spring boot·后端·spring cloud·微服务·云原生·架构
学习论之费曼学习法3 小时前
AI 入门 30 天挑战 - Day 18 费曼学习法版 - 图像分割基础
人工智能·学习
It's Q3 小时前
hive学习分区&&函数
hive·hadoop·学习
red_redemption3 小时前
自由学习记录(173)
学习·clock ticking·lighting rod·zeus
千寻girling3 小时前
机器学习 | 逻辑回归 | 尚硅谷学习
java·人工智能·python·学习·算法·机器学习·逻辑回归
Tutankaaa3 小时前
防震减灾知识竞赛题库:地震常识、应急避险与自救互救指南
经验分享·笔记·学习
wljt3 小时前
SpringBoot学习笔记五:Spring Boot的web开发
spring boot·笔记·学习
Shadow(⊙o⊙)3 小时前
C++常见错误解析2.0
开发语言·数据结构·c++·后端·学习·算法
anzhxu4 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端