在 Web 应用开发中,留言板是非常经典的入门案例。它涵盖了用户提交数据、后台存储和页面渲染等核心功能,能够帮助我们熟悉 HTTP 请求处理、模板渲染以及数据持久化等知识点。
本文将以 Go 语言为例,演示如何用最简单的文件存储方式,实现一个基本的留言板系统,满足用户留言的新增和查看功能。
一、项目需求分析
- • 用户可以通过网页填写姓名和留言内容,提交后留言保存到服务器。
- • 服务器端将留言存储到本地 JSON 文件,实现数据的持久化。
- • 首页展示所有留言,支持留言列表的浏览。
- • 使用 Go 标准库,尽量减少外部依赖。
- • 保证并发写文件的安全性。
二、技术点梳理
- • HTTP 请求处理 (
net/http
) - • 表单数据接收与解析
- • 文件读写(JSON 格式)
- • 模板渲染 (
html/template
) - • 并发同步 (
sync.Mutex
)
三、核心代码解析
1. 留言结构体定义
go
type Message struct {
Name string
Content string
Time string
}
每条留言包含用户名、留言内容和留言时间。
2. 读取和保存留言
go
func loadMessages() ([]Message, error) {
file, err := os.Open(dataFile)
if os.IsNotExist(err) {
return []Message{}, nil
}
if err != nil {
return nil, err
}
defer file.Close()
var msgs []Message
err = json.NewDecoder(file).Decode(&msgs)
if err != nil {
return []Message{}, nil
}
return msgs, nil
}
func saveMessages(msgs []Message) error {
file, err := os.Create(dataFile)
if err != nil {
return err
}
defer file.Close()
return json.NewEncoder(file).Encode(msgs)
}
这两个函数负责从文件加载留言列表以及将留言列表写回文件。
3. 并发写保护
使用 sync.Mutex
避免多请求同时写文件导致的数据混乱:
dart
var mutex sync.Mutex
所有写操作用 mutex.Lock()
和 mutex.Unlock()
包裹。
4. 首页展示留言和留言表单
go
func indexHandler(w http.ResponseWriter, r *http.Request) {
msgs, _ := loadMessages()
// 使用 html/template 渲染留言页面
tmpl := ` ... `
t := template.Must(template.New("index").Parse(tmpl))
t.Execute(w, msgs)
}
5. 处理留言提交
scss
func addHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
name := r.FormValue("name")
content := r.FormValue("content")
if name == "" || content == "" {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
mutex.Lock()
defer mutex.Unlock()
msgs, _ := loadMessages()
msgs = append(msgs, Message{
Name: name,
Content: content,
Time: time.Now().Format("2006-01-02 15:04:05"),
})
saveMessages(msgs)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
四、完整代码
go
package main
import (
"encoding/json"
"html/template"
"net/http"
"os"
"sync"
"time"
)
type Message struct {
Name string
Content string
Time string
}
var (
dataFile = "messages.json"
mutex sync.Mutex
)
func loadMessages() ([]Message, error) {
file, err := os.Open(dataFile)
if os.IsNotExist(err) {
return []Message{}, nil
}
if err != nil {
return nil, err
}
defer file.Close()
var msgs []Message
err = json.NewDecoder(file).Decode(&msgs)
if err != nil {
return []Message{}, nil
}
return msgs, nil
}
func saveMessages(msgs []Message) error {
file, err := os.Create(dataFile)
if err != nil {
return err
}
defer file.Close()
return json.NewEncoder(file).Encode(msgs)
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
msgs, _ := loadMessages()
tmpl := `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>简易留言板</title>
</head>
<body>
<h1>简易留言板</h1>
<form method="POST" action="/add">
姓名: <input name="name" required><br>
留言: <textarea name="content" required></textarea><br>
<button type="submit">提交</button>
</form>
<hr>
<h2>留言列表</h2>
{{range .}}
<p><b>{{.Name}}</b> ({{.Time}}):<br>{{.Content}}</p>
<hr>
{{else}}
<p>暂无留言</p>
{{end}}
</body>
</html>
`
t := template.Must(template.New("index").Parse(tmpl))
t.Execute(w, msgs)
}
func addHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
name := r.FormValue("name")
content := r.FormValue("content")
if name == "" || content == "" {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
mutex.Lock()
defer mutex.Unlock()
msgs, _ := loadMessages()
msgs = append(msgs, Message{
Name: name,
Content: content,
Time: time.Now().Format("2006-01-02 15:04:05"),
})
saveMessages(msgs)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
func main() {
http.HandleFunc("/", indexHandler)
http.HandleFunc("/add", addHandler)
println("服务器启动:http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
五、运行测试
-
- 在项目目录下执行:
go
go run main.go
-
- 打开浏览器访问:
arduino
http://localhost:8080
-
- 输入姓名和留言内容,点击提交,留言即被保存并显示。
-
- 服务器目录下生成
messages.json
文件,实现留言持久化。
- 服务器目录下生成
六、注意事项与扩展建议
- • 数据存储:文件存储适合小型简单应用,生产环境建议使用数据库(如 MySQL、SQLite、MongoDB)存储留言。
- • 并发安全 :此示例使用
sync.Mutex
简单保证写文件安全,实际复杂系统建议设计更严谨的并发策略。 - • 输入校验:本示例未对输入内容做更多安全处理,生产环境需防止 XSS 注入、恶意内容。
- • 分页显示:当留言数量多时,建议分页加载,提升页面性能。
- • 样式美化:可增加 CSS 使界面更加美观友好。
七、总结
本文示范了如何用 Go 语言快速搭建一个简易留言板,涵盖 HTTP 请求处理、表单解析、模板渲染和文件存储等基础知识,帮助初学者掌握实用的 Web 开发技能。