全局变量universitySelected读取HTML表单值后被意外覆盖问题
问题描述
我在代码顶部定义了全局变量universitySelected和domain,但执行email := c.FormValue("email")语句后,universitySelected的值被意外覆盖。比如原本值是"Harvard University",执行后变成了"ex@harvard.edusity"。
相关代码如下:
全局变量定义及被覆盖的代码位置
var universitySelected string var domain string func HandleRegistration(c *fiber.Ctx) error { // 获取表单值 email := c.FormValue("email") password := c.FormValue("password") if !strings.Contains(email, ".edu") { return c.Render("signup", fiber.Map{ "UniversityName": universitySelected, "UniversityDomain": domain, "ErrorMessage": "Email Domain Not Supported", }) } // 后续逻辑... }
universitySelected的赋值逻辑
func HandleUniversitySelection(c *fiber.Ctx) error { universitySelected = c.FormValue("university") err := db.QueryRow("SELECT domain FROM universities WHERE name = $1", universitySelected).Scan(&domain) if err != nil { return err } return c.Render("signup", fiber.Map{ "UniversityName": universitySelected, "UniversityDomain": domain, }) }
问题原因与解决办法
根本原因
全局变量在Go的HTTP服务中是所有请求共享的,当多个用户同时发起请求时,不同请求的handler会交替修改全局变量的值,导致数据混乱。你遇到的"ex@harvard.edusity"就是典型的并发覆盖问题——不同请求的内存数据发生了意外干扰。
解决方案
方案1:用请求上下文(fiber.Ctx.Locals)传递数据
每个fiber请求的c.Locals是当前请求独有的存储区域,不会被其他请求干扰,适合同一次请求内的handler间传值:
修改HandleUniversitySelection:
func HandleUniversitySelection(c *fiber.Ctx) error { university := c.FormValue("university") var domain string err := db.QueryRow("SELECT domain FROM universities WHERE name = $1", university).Scan(&domain) if err != nil { return err } // 将值存入当前请求的上下文 c.Locals("UniversityName", university) c.Locals("UniversityDomain", domain) return c.Render("signup", fiber.Map{ "UniversityName": university, "UniversityDomain": domain, }) }
修改HandleRegistration:
func HandleRegistration(c *fiber.Ctx) error { email := c.FormValue("email") password := c.FormValue("password") // 从上下文取出值,注意类型断言 universitySelected, ok := c.Locals("UniversityName").(string) if !ok { // 处理未找到的情况,比如重定向到选大学页面 return c.Redirect("/select-university") } domain, ok := c.Locals("UniversityDomain").(string) if !ok { return c.Redirect("/select-university") } if !strings.Contains(email, ".edu") { return c.Render("signup", fiber.Map{ "UniversityName": universitySelected, "UniversityDomain": domain, "ErrorMessage": "Email Domain Not Supported", }) } // 后续逻辑... }
方案2:用表单隐藏域传递(跨页面场景)
如果用户选完大学后会跳转到注册页面(跨请求),上下文Locals会失效,此时可以将大学信息作为隐藏字段嵌入注册表单:
在signup模板中添加隐藏域:
<form action="/register" method="POST"> <!-- 其他表单字段 --> <input type="hidden" name="university" value="{{.UniversityName}}"> <input type="hidden" name="domain" value="{{.UniversityDomain}}"> <button type="submit">注册</button> </form>
然后在HandleRegistration中直接获取:
func HandleRegistration(c *fiber.Ctx) error { email := c.FormValue("email") password := c.FormValue("password") // 直接从表单获取隐藏字段的值 universitySelected := c.FormValue("university") domain := c.FormValue("domain") if !strings.Contains(email, ".edu") { return c.Render("signup", fiber.Map{ "UniversityName": universitySelected, "UniversityDomain": domain, "ErrorMessage": "Email Domain Not Supported", }) } // 后续逻辑... }
方案3:用Session存储(用户会话场景)
如果需要在用户的多个请求间持久化数据(比如用户关闭页面后再次打开仍保留选择),可以使用fiber的session中间件:
- 先初始化session中间件:
app := fiber.New() store := session.New(session.Config{ CookieHTTPOnly: true, Expiration: 24 * time.Hour, }) app.Use(store)
- 修改
HandleUniversitySelection存入session:
func HandleUniversitySelection(c *fiber.Ctx) error { university := c.FormValue("university") var domain string err := db.QueryRow("SELECT domain FROM universities WHERE name = $1", university).Scan(&domain) if err != nil { return err } // 获取当前session sess, err := store.Get(c) if err != nil { return err } // 存入session sess.Set("UniversityName", university) sess.Set("UniversityDomain", domain) // 保存session if err := sess.Save(); err != nil { return err } return c.Render("signup", fiber.Map{ "UniversityName": university, "UniversityDomain": domain, }) }
- 修改
HandleRegistration从session读取:
func HandleRegistration(c *fiber.Ctx) error { email := c.FormValue("email") password := c.FormValue("password") sess, err := store.Get(c) if err != nil { return err } // 从session取值 universitySelected, ok := sess.Get("UniversityName").(string) if !ok { return c.Redirect("/select-university") } domain, ok := sess.Get("UniversityDomain").(string) if !ok { return c.Redirect("/select-university") } if !strings.Contains(email, ".edu") { return c.Render("signup", fiber.Map{ "UniversityName": universitySelected, "UniversityDomain": domain, "ErrorMessage": "Email Domain Not Supported", }) } // 后续逻辑... }
内容的提问来源于stack exchange,提问作者cyw
相关产品推荐
相关产品推荐

