Go TODO应用用net/http实现轻量Web服务,以切片+sync.RWMutex管理并发安全的TODO数据,通过http.HandleFunc路由、template渲染HTML、r.ParseForm解析表单,并注意重定向后return避免重复响应。

Go 语言写一个 TODO 应用,不需要框架也能快速跑起来——核心是用 net/http 搭个轻量 Web 服务,配合内存存储(如 map 或切片)完成增删查改。真正卡住新手的不是语法,而是路由设计、请求解析、状态保持这几个环节。
用 http.HandleFunc 实现基础路由和 HTML 渲染
别急着上 gorilla/mux 或 gin,先用标准库把流程走通。每个路由对应一个函数,用 http.ServeFile 静态服务 HTML,用 template 渲染动态内容。
关键点:
-
http.HandleFunc的第二个参数必须是func(http.ResponseWriter, *http.Request)类型 - HTML 表单的
method要和 Go 中判断的r.Method一致(比如POST提交就要检查r.Method == "POST") - 渲染模板前务必调用
template.ParseFiles(),且路径要相对于执行时的当前目录(不是源码目录)
用 struct 和切片管理 TODO 数据,避免全局 map 并发问题
初学者常直接用 var todos = make(map[int]*Todo),但 HTTP 处理函数是并发调用的,map 非线程安全。更稳妥的做法是用切片 + 互斥锁,或干脆用 sync.Map(适合读多写少)。
立即学习“go语言免费学习笔记(深入)”;
推荐结构:
type Todo struct {
ID int `json:"id"`
Text string `json:"text"`
Done bool `json:"done"`
}
var (
todos = []Todo{}
mu sync.RWMutex
)
所有读写操作前后加 mu.Lock()/mu.Unlock() 或 mu.RLock()/mu.RUnlock()。
处理表单提交:用 r.ParseForm() 和 r.FormValue() 取值
Go 不会自动解析 POST 表单,必须显式调用 r.ParseForm(),否则 r.FormValue("text") 返回空字符串。常见错误包括:
- 忘记调用
r.ParseForm(),尤其在if r.Method == "POST"分支里 - HTML 表单中
缺少name="text"属性,导致r.FormValue("text")找不到键 - 重定向后没加
return,导致后续代码继续执行并重复写响应(HTTP 500 或 “http: multiple response.WriteHeader calls” 错误)
典型处理逻辑:
if r.Method == "POST" {
r.ParseForm()
text := r.FormValue("text")
if text != "" {
mu.Lock()
todos = append(todos, Todo{
ID: len(todos) + 1,
Text: text,
Done: false,
})
mu.Unlock()
}
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
用 html/template 安全渲染列表,防止 XSS
直接拼接 HTML 字符串(如 w.Write([]byte(")有 XSS 风险。必须用 html/template,它会自动转义变量内容。
模板示例(保存为 index.html):
{{range .Todos}}
渲染时传入数据结构:
data := struct {
Todos []Todo
}{
Todos: todos,
}
tmpl.Execute(w, data)
注意:模板中字段名首字母必须大写(Text 可导出,text 不可),否则无法访问。










