214 lines
5.2 KiB
Markdown
214 lines
5.2 KiB
Markdown
# MeshRay 路由注册顺序检查清单
|
||
|
||
**创建时间**: 2026-03-24
|
||
**目的**: **防止再次犯同样的路由顺序错误!**
|
||
|
||
---
|
||
|
||
## ⚠️ **血的教训**
|
||
|
||
这个问题已经犯了**至少两次**:
|
||
1. ❌ 在 Go Embed 最佳实践文档中,示例代码写错了顺序
|
||
2. ❌ 在实际修复 MIME 类型问题时,又犯了同样的错误
|
||
|
||
**根本原因**: 没有形成肌肉记忆,没有使用检查清单
|
||
|
||
---
|
||
|
||
## ✅ **路由注册顺序(必须遵守)**
|
||
|
||
### 正确的顺序(从高优先级到低优先级)
|
||
|
||
```go
|
||
// 1️⃣ 中间件(最优先)
|
||
s.engine.Use(middleware.RequestLogger())
|
||
s.engine.Use(middleware.CORS())
|
||
|
||
// 2️⃣ 精确路由(具体路径)
|
||
s.engine.GET("/health", healthHandler)
|
||
s.engine.POST("/api/v1/login", loginHandler)
|
||
|
||
// 3️⃣ ⭐ 静态文件目录(高优先级,必须在 NoRoute 之前)
|
||
s.engine.StaticFS("/assets", httpFS)
|
||
s.engine.StaticFS("/static", httpFS)
|
||
|
||
// 4️⃣ 通配符路由(参数路由)
|
||
s.engine.GET("/files/:filepath", fileHandler)
|
||
|
||
// 5️⃣ ⭐ NoRoute 兜底路由(最低优先级,必须最后)
|
||
s.engine.NoRoute(func(c *gin.Context) {
|
||
// SPA 路由支持
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 **Checklist - 每次修改路由时都要检查**
|
||
|
||
### 开发阶段
|
||
|
||
- [ ] **确认路由注册顺序正确**
|
||
- [ ] StaticFS 在 NoRoute 之前
|
||
- [ ] 精确路由在 StaticFS 之前
|
||
- [ ] 中间件在最前面
|
||
|
||
- [ ] **添加注释标记**
|
||
```go
|
||
// ⭐ 重要:先注册静态文件目录(优先级高)
|
||
s.engine.StaticFS("/assets", httpFS)
|
||
|
||
// 再注册 NoRoute 处理 SPA 路由(优先级低)
|
||
s.engine.NoRoute(...)
|
||
```
|
||
|
||
---
|
||
|
||
### Code Review 阶段
|
||
|
||
- [ ] **重点检查路由顺序**
|
||
- [ ] StaticFS 是否在 NoRoute 之前?
|
||
- [ ] 是否有其他路由可能拦截静态资源?
|
||
- [ ] 注释是否清晰标明顺序要求?
|
||
|
||
- [ ] **功能测试**
|
||
- [ ] JS 文件返回 `application/javascript`
|
||
- [ ] CSS 文件返回 `text/css`
|
||
- [ ] HTML 文件返回 `text/html`
|
||
- [ ] 浏览器无 MIME 类型错误
|
||
|
||
---
|
||
|
||
### 测试验证
|
||
|
||
**快速测试命令**:
|
||
```bash
|
||
# 测试 JS 文件 MIME 类型
|
||
curl -I http://localhost:9531/assets/index.js | grep Content-Type
|
||
# 应该返回:application/javascript
|
||
|
||
# 测试 CSS 文件 MIME 类型
|
||
curl -I http://localhost:9531/assets/style.css | grep Content-Type
|
||
# 应该返回:text/css
|
||
|
||
# 测试 HTML 页面
|
||
curl http://localhost:9531 | grep "<!DOCTYPE html>"
|
||
# 应该返回:<!DOCTYPE html>
|
||
```
|
||
|
||
---
|
||
|
||
## 🔴 **错误示例(千万不要这样写)**
|
||
|
||
```go
|
||
// ❌ 错误示范 - 这个顺序会导致静态文件全部返回 HTML!
|
||
if staticFS != nil {
|
||
httpFS := http.FS(staticFS)
|
||
|
||
// ❌ 先注册 NoRoute(错误!)
|
||
s.engine.NoRoute(func(c *gin.Context) {
|
||
// 所有请求都被这里拦截
|
||
})
|
||
|
||
// ❌ 后注册 StaticFS(永远不会被执行)
|
||
s.engine.StaticFS("/assets", httpFS)
|
||
}
|
||
```
|
||
|
||
**后果**:
|
||
- ❌ 所有 `/assets/*.js` 返回 HTML
|
||
- ❌ 所有 `/assets/*.css` 返回 HTML
|
||
- ❌ 浏览器控制台大量 MIME 类型错误
|
||
- ❌ 前端完全无法使用
|
||
|
||
---
|
||
|
||
## 🟢 **正确示例(必须这样写)**
|
||
|
||
```go
|
||
// ✅ 正确示范 - 严格按照优先级顺序
|
||
if staticFS != nil {
|
||
httpFS := http.FS(staticFS)
|
||
|
||
// ✅ 先注册静态文件目录(高优先级)
|
||
s.engine.StaticFS("/assets", httpFS)
|
||
s.engine.StaticFS("/static", httpFS)
|
||
|
||
// ✅ 再注册 NoRoute(低优先级)
|
||
s.engine.NoRoute(func(c *gin.Context) {
|
||
// SPA 路由支持
|
||
})
|
||
}
|
||
```
|
||
|
||
**效果**:
|
||
- ✅ JS 文件正确返回 `application/javascript`
|
||
- ✅ CSS 文件正确返回 `text/css`
|
||
- ✅ HTML 文件正确返回 `text/html`
|
||
- ✅ 前端正常工作
|
||
|
||
---
|
||
|
||
## 📊 **记忆口诀**
|
||
|
||
> **中间件最先注册,精确路由紧随其后;**
|
||
> **静态文件要提前,NoRoute 最后面;**
|
||
> **顺序千万别搞反,否则资源全完蛋!**
|
||
|
||
---
|
||
|
||
## 🎯 **如何形成肌肉记忆**
|
||
|
||
### 1. 每次写路由时都大声念出口诀
|
||
```
|
||
"静态文件要提前,NoRoute 最后面"
|
||
```
|
||
|
||
### 2. 在代码模板中固化顺序
|
||
|
||
创建代码片段(VSCode Snippet):
|
||
```go
|
||
// Gin 路由模板
|
||
if staticFS != nil {
|
||
httpFS := http.FS(staticFS)
|
||
|
||
// ⭐ 重要:先注册静态文件目录(优先级高)
|
||
${1:s.engine.StaticFS("/assets", httpFS)}
|
||
|
||
// 再注册 NoRoute 处理 SPA 路由(优先级低)
|
||
${2:s.engine.NoRoute(...)}
|
||
}
|
||
```
|
||
|
||
### 3. 在 IDE 中设置警告
|
||
|
||
如果使用 VSCode,可以创建自定义规则检测路由顺序。
|
||
|
||
---
|
||
|
||
## 📈 **历史错误记录**
|
||
|
||
| 时间 | 错误 | 影响 | 改进措施 |
|
||
|------|------|------|----------|
|
||
| 2026-03-24 (Go Embed 文档) | 示例代码顺序错误 | 文档误导他人 | 已修正并添加警告 |
|
||
| 2026-03-24 (MIME 修复) | 实际修复时顺序错误 | 前端无法访问 | 已修正并创建本清单 |
|
||
|
||
---
|
||
|
||
## ✅ **承诺**
|
||
|
||
从今天起,每次修改路由注册时都要:
|
||
|
||
1. 📝 **先看这个 Checklist**
|
||
2. 🔍 **检查顺序是否正确**
|
||
3. 🧪 **运行测试命令验证**
|
||
4. 📋 **Code Review 时重点检查**
|
||
|
||
**不再犯同样的错误!**
|
||
|
||
---
|
||
|
||
**状态**: ✅ **Checklist 已创建**
|
||
**执行**: 每次修改路由时必须使用此清单
|
||
|
||
*MeshRay - 从错误中学习,让教训成为财富!* ✨📋
|