go 用 net/http 可构建轻量投票系统:用 sync.map 或 mutex 保障计数并发安全,通过一次性 Token 防重复投票,jsON 接口返回实时结果并前端轮询,Nginx 反向代理部署、静态文件安全暴露、panic 恢复中间件保障稳定性。

Go 本身不带 Web 框架,但 net/http 足够支撑一个轻量、可控的投票系统——关键不在“用什么框架”,而在如何组织状态、防止重复投票、避免竞态。
如何用 net/http 处理投票提交与计数
别急着上 gorilla/mux 或 gin。原生 http.ServeMux + 闭包或结构体方法就能清晰分离逻辑:
-
http.HandleFunc("/vote", handleVote):接收 POST,解析id=option1表单 - 计数器必须是并发安全的:
sync.Map存选项 → 计数值,或用sync.Mutex包裹普通map[String]int - 不要在 handler 里直接操作全局 map——把计数逻辑抽成方法,比如
voter.Increment("option1") - 返回 json(
Content-Type: application/json)比跳转更利于前端控制反馈
如何防止用户重复投票(无登录场景)
没有账号体系时,“防重”本质是降低作弊成本,而非绝对阻止:
- 服务端用
net/http.Request.RemoteAddr+User-Agent做粗粒度标记(仅限演示,不可用于生产) - 更可行的是:写入临时 cookie(
http.Setcookie(rw, &http.Cookie{Name: "voted", Value: "true", MaxAge: 3600})),前端禁止再次点击按钮 - 真正有效的方案是加简单 Token:
GET /vote/token返回一次性token=abc123,提交时带上,服务端用sync.Map标记已使用并立即删除 - 注意:别用时间戳或自增 ID 当 token,容易被枚举
如何让选项列表和结果实时一致
页面每次刷新都走一次 GET /results 是最简单的同步方式,无需 websocket:
立即学习“go语言免费学习笔记(深入)”;
- 接口返回 JSON:
{"option1": 42, "option2": 19, "total": 61} - 前端用
fetch()定期轮询(如每 5 秒),更新 dom 中的数字 —— 对投票系统足够及时 - 如果用模板渲染(
html/template),务必在Execute前从共享计数器读取最新值,别缓存 template 输出 - 避免在 HTML 中硬编码初始票数;所有数据应由后端注入或通过 API 获取
部署时最容易忽略的三个点
本地跑通 ≠ 可上线:
-
http.ListenAndServe(":8080", nil)在服务器上要绑定:80?别直接提权,用反向代理(nginx)转发,Go 进程仍跑:8080 - 静态文件(css/JS)别用
http.FileServer直接暴露整个目录,易泄露源码;明确指定子路径:http.Handle("/Static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static")))) - 没做 panic 恢复?加一层 middleware:
defer func() { if r := recover(); r != nil { log.printf("panic: %v", r) } }(),否则一次空指针就 kill 整个 server
真正的复杂点不在“怎么投”,而在于“谁投过、什么时候失效、数据怎么落地”。内存计数重启即丢,真要持久化,就得对接 sqlite 或 redis —— 那已经是下一个迭代的事了。