You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

全局变量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中间件:

  1. 先初始化session中间件:
app := fiber.New()
store := session.New(session.Config{
    CookieHTTPOnly: true,
    Expiration:     24 * time.Hour,
})
app.Use(store)
  1. 修改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,
    })
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 19:35:24