当前位置: 首页 > news >正文

上集:一个前端的血泪复仇记 —— 静态部署的胜利

在这里插入图片描述

“我只是想部署一个小项目,结果干翻了Spring Security、Next.js 和 AI —— 三个加起来至少值我两天命。”

话说有一天,我朋友搭了一个小系统,前后端分离,说大不大,说小不小。后端Spring Boot 3.4.5,前端Next.js 15。开发时他们自我感觉良好,分层清晰,接口明了,Node起服务,Spring管API,一切皆优雅。

然而,当要部署上线时,他们懵了。

Azure上已经通过GitHub Actions成功部署好了后端jar包,但前端还在本地嗷嗷叫。老板催着要结果,可老板哪会折腾前端部署?你还得教他怎么写Actions脚本、怎么设置Node环境、怎么指定输出目录……天知道你一说完他会不会当场升天。

这时候我一拍大腿:

“何必呢?这项目没几个页面,又不需要SSR,咱就静态导出,然后把前端生成的HTML+JS直接丢进Spring Boot的static目录不就完了?jar包一启动,前后端全搞定,老板连指都不用动!”

第一难关:Spring Security 发疯了!

你以为这样就结束了?不,地狱的大门这才刚刚打开。

我照着Next.js的文档老老实实 npx next export,拷进resources/static/目录,打jar,跑起来,打开浏览器:403 Forbidden。

不是404,不是500,是403。意思是你这请求我知道,但我就是不让你进。

我立刻意识到:是Spring Security出手了。

点开日志,一行熟悉的输出迎面而来:

Securing GET /
Http403ForbiddenEntryPoint: Pre-authenticated entry point called. Rejecting access

这熟悉的中世纪防火墙味道……果然是你,Security!

于是我翻出SecurityConfig,一看默认策略是 .anyRequest().authenticated(),只放行了/api/**/public/**。而我那可怜的index.html,连个像样的api都不是,当然被一棍子拍死在门外。

我没有头铁上去一个个添加 /index.html/*.js/*.css……那样太低效。正确方式其实就两个字:放行。

于是我重新配置了SecurityConfig.java,使用精简清晰的策略:只对 /api/** 做权限控制,其余一律放行。

.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/**").authenticated()
.anyRequest().permitAll()

配合上另一个核心配置——WebConfig.java中精细处理静态资源和前端路径:

if (resourcePath.contains(".")) return null; // 静态资源请求
if (resourcePath.startsWith("api/")) return null; // 后端接口
return new ClassPathResource("/static/index.html"); // 其他全部交给前端路由

部署、启动、访问:Boom,终于进来了。

第二难关:前端路由全军覆没

我刚准备喝口水庆祝一下,结果点击跳转到 /admin/login 页面时,界面啪地一下变白了。

刷新页面,仍然白屏。

看着那沉默如谜的浏览器窗口,我仿佛听见了它在说:

“你静态部署的SPA里根本没这个路径。”

于是我开始搜索、试验,AI这时候又开始滔滔不绝:

“写个FrontendController吧,用@Forward转发所有路径到index.html!”

我信了。然后一秒钟内,部署的服务陷入了前端路由的死循环:什么都跳转,什么都不对。

点击任何路由按钮,都不是你想去的页面,而是index本尊笑着再次出现。

于是我彻底扔掉了FrontendController那一套。

你用静态导出的Next.js,其实每一个页面路径,都已经被导出成了真正的HTML文件!比如 /admin/login 对应的就是 admin/login/index.html,这些文件真实地存在于 out/ 目录中。只要你把它们原封不动地复制进Spring Boot的 static/ 目录中,路径匹配得上,页面自然就出来了,根本不需要靠 index.html 来做路由跳转!

AI死活跟我说,“你这个前端还是SPA,要靠 index.html 来初始化页面、再用JS切换路由”,我反复回怼:“你是不是没搞清楚静态导出是什么意思?”

最后我干脆截图,把 Next.js 导出的每个页面的 index.html 全部拍给它看,我说你看,是不是每个页面都有?每个URL其实就是对应一个静态路径,压根不需要搞什么index跳转那一套。

它这才哑口无言,终于认同我的方案,把 WebConfig 改成现在这个样子 —— 静态资源自己找,找不到再跳 index,而不是一上来就全部丢给 index 去兜底。

所以问题的根源是:不该试图用Controller去理解前端的路由,也不能拿SPA框架的假设来套用静态导出逻辑

正确姿势是:

  • 让 WebConfig 的 ResourceHandler 处理静态文件匹配
  • 只放过那些真的访问不到的页面给 index.html
  • 更重要的是:你要相信文件系统,而不是JS路由器!

配置定了,页面能跳,静态能拿,接口能调,世界终于恢复了秩序。

小结:写给还在挣扎的小项目开发者

  • 前后端分离是一种美梦,但对小项目而言,它可能是场噩梦
  • 能静态部署的,就别搭两个服务了,咱不卷
  • AI是个很棒的助手,但它不懂项目上下文,更不会给你抹眼泪
  • 不要让AI拉着你进坑,它会笑着劝你用Controller转发一切,然后带你走向死循环
  • 最靠谱的办法,永远是你亲眼看清 log,亲手写对 config,亲自试出通路

下集我会继续讲完整的构建流程、打包命令、静态导出配置、最终Security+WebConfig的全代码。并带你避开更多AI建议里那些"听起来合理,做出来崩盘"的经典误区。

别急,下一章我们见真章。

相关文章:

  • 调用DeepSeek系列模型问答时,输出只有</think>标签,而没有<think>标签
  • CANoe CAPL TCP DoIP通信问题
  • 【springboot+vue3的前后端分离项目实现支付宝的沙箱支付】
  • 使用instance着色
  • 边缘计算:物联网的“加速器”与“守护者”
  • Postman遇到脚本不支持replaceIn函数
  • 3D个人简历网站 4.小岛
  • BootCDN介绍(Bootstrap主导的前端开源项目免费CDN加速服务)
  • 【技巧】GoogleChrome浏览器开发者模式查看dify接口
  • DeepSeek赋能电商,智能客服机器人破解大型活动人力困境
  • AtomicInteger
  • 信息收集+初步漏洞打点
  • springboot配置tomcat端口的方法
  • 单细胞转录组(3)
  • DAY28-类的定义和方法
  • 深入解析Spring Boot与Spring Security的集成实践
  • 无线数传模块支持园区多节点电力参数同步监测配置指南
  • Java 注解篇:@RequestMapping
  • 大模型技术演进与应用场景深度解析
  • LVGL- Calendar 日历控件
  • 多图|多款先进预警机亮相雷达展,专家:中国预警机已达世界先进水平
  • 白玉兰奖征片综述丨海外剧创作趋势观察:跨界·融变·共生
  • 普京调整俄陆军高层人事任命
  • 国税总局上海市税务局回应刘晓庆被举报涉嫌偷漏税:正依法依规办理
  • 腾讯一季度净利增14%:AI直接拉动广告收入增长,王者荣耀流水创新高
  • “三个集中”之后:图说浦东新区28次撤乡并镇