核心难点是路由设计、表单处理与数据库交互的衔接;c.PostForm在GET中静默返回空字符串因无请求体;模板路径匹配失败主因是**不支持递归匹配、大小写敏感、模板名非文件路径;DELETE应改用RESTful路由;数据库错误必须显式响应而非仅打印日志。

c.PostForm 在 GET 请求里会静默返回空字符串,这是最常踩的坑。
为什么 c.PostForm 在 GET 路由里永远拿不到值
GET 请求没有请求体(body),c.PostForm 只从 application/x-www-form-urlencoded 或 multipart/form-data 的 body 中解析字段。你在 r.GET("/book/new", newBookhandle) 里调用 c.PostForm("title"),结果一定是空字符串,不会报错,但后续 strconv.ParseFloat 会 panic 或插入零值。
- 新增页面(GET)只负责渲染
new_book.html,不读表单 - 提交动作必须走
r.POST("/book/new", createBookHandle),且前端 form 的method必须为post - 如果误把 POST 处理逻辑写在 GET handler 里,数据就丢了,还难调试
r.LoadHTMLGlob("template/**/*") 路径匹配失败的三个常见原因
模板加载失败不会直接报错,而是运行时 c.HTML 抛出 template: "xxx" is undefined,本质是路径没对上或文件没被识别。
- 路径中使用双星号
**时,Gin 实际依赖filepath.Glob,它在 Windows 和某些 Docker 镜像里不支持递归匹配;建议改用r.LoadHTMLFiles("template/book/book_list.html", "template/book/new_book.html") - 模板文件名大小写敏感:Linux/macOS 下
Book_list.html≠book_list.html,但开发机是 Windows 就可能“碰巧”跑通 -
c.HTML第二个参数是模板名,不是文件路径;比如book_list.html必须和LoadHTMLFiles加载时的 basename 一致,不能写成"book/book_list.html"(除非你加载时显式带了子目录前缀)
删除和更新路由用 GET 带 ID 是危险的
当前代码里 r.GET("/book/delete", deleteHandle) 依赖 URL 查询参数(如 /book/delete?id=123)做删除,这违反 REST 原则,也埋下安全漏洞。
- 浏览器刷新或代理重放会导致重复删除
- 爬虫或恶意链接可批量触发删除(CSRF 风险)
- 正确做法:改用
r.DELETE("/book/:id", deleteHandle),并在 handler 中用c.Param("id")获取;前端用fetch("/book/123", { method: "DELETE" }) - 如果必须兼容旧表单,至少加一层确认逻辑:
if c.Query("confirm") != "yes" { c.Redirect(http.StatusFound, "/book/list"); return }
数据库操作未统一错误处理,导致 500 页面空白
示例代码中 queryAlllBook() 和 insertAlllBook() 返回 error,但部分 handler 只打印日志或直接 return,没调用 c.AbortWithStatusJSON 或 c.String,结果 HTTP 状态码是 200,但响应体为空,前端卡死。
- 所有数据库操作后必须检查 error,并显式返回客户端可感知的结果
- 不要用
fmt.Println替代错误响应;例如if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db query failed"}) } - 更稳妥的做法是封装一个
handleError(c *gin.Context, err error, msg string)统一处理


















