学习记录
- [1 创建博文](#1 创建博文)
-
- [1.1 在main.go中添加路由](#1.1 在main.go中添加路由)
- [1.2 构建表单](#1.2 构建表单)
- [2 读取表单数据](#2 读取表单数据)
-
- [2.1 完善articlesStoreHandler() 函数](#2.1 完善articlesStoreHandler() 函数)
- [2.2 修改代码,查看区别](#2.2 修改代码,查看区别)
- [3 表单验证](#3 表单验证)
-
- [3.1 数据验证](#3.1 数据验证)
- [3.2 出错提示](#3.2 出错提示)
1 创建博文
1.1 在main.go中添加路由
访问http://localhost:3000/articles/create
1.2 构建表单
报错
router在main函数中定义,但其他函数无法直接识别,需将router定义在函数体外面当成全局变量(包级别的变量)。
包级别的变量不能使用:=
声明并赋值变量
应该使用
var router = mux.NewRouter( )
浏览器访问 localhost:3000/articles/create
浏览器中查看源码
2 读取表单数据
2.1 完善articlesStoreHandler() 函数
go
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil{
//解析错误,这里应该有错误处理
fmt.Fprintf(w,"请提供正确的数据!")
return
}
title := r.PostForm.Get("title")
fmt.Fprintf(w,"POST PostForm: %v <br>",r.PostForm)
fmt.Fprintf(w,"POST Form: %v <br>",r.Form)
fmt.Fprintf(w,"title 的值为: %v <br>", title)
}
访问 localhost:3000/articles/create ,在标题和内容里输入信息:
2.2 修改代码,查看区别
go
.
.
.
func articlesCreateHandler(w http.ResponseWriter, r *http.Request) {
html := `
<!DOCTYPE html>
<html lang="en">
<head>
<title>创建文章 ------ 我的技术博客</title>
</head>
<body>
<form action="%s?test=data" method="post">
<p><input type="text" name="title"></p>
<p><textarea name="body" cols="30" rows="10"></textarea></p>
<p><button type="submit">提交</button></p>
</form>
</body>
</html>
`
storeURL, _ := router.Get("articles.store").URL()
fmt.Fprintf(w, html, storeURL)
}
.
.
.
重新打开 localhost:3000/articles/create 并填写数据(注意如果你使用浏览器的回退功能的话,需要重新刷新页面),再次提交:
Form多了URL参数里的数据
如不想获取所有的请求内容,而是逐个获取的话,这也是比较常见的操作,无需使用 r.ParseForm() 可直接使用 r.FormValue() 和 r.PostFormValue() 方法:
go
.
.
.
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "r.Form 中 title 的值为: %v <br>", r.FormValue("title"))
fmt.Fprintf(w, "r.PostForm 中 title 的值为: %v <br>", r.PostFormValue("title"))
fmt.Fprintf(w, "r.Form 中 test 的值为: %v <br>", r.FormValue("test"))
fmt.Fprintf(w, "r.PostForm 中 test 的值为: %v <br>", r.PostFormValue("test"))
}
.
.
.
3 表单验证
3.1 数据验证
go
.
.
.
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {
title := r.PostFormValue("title")
body := r.PostFormValue("body")
errors := make(map[string]string)
// 验证标题
if title == "" {
errors["title"] = "标题不能为空"
} else if len(title) < 3 || len(title) > 40 {
errors["title"] = "标题长度需介于 3-40"
}
// 验证内容
if body == "" {
errors["body"] = "内容不能为空"
} else if len(body) < 10 {
errors["body"] = "内容长度需大于或等于 10 个字节"
}
// 检查是否有错误
if len(errors) == 0 {
fmt.Fprint(w, "验证通过!<br>")
fmt.Fprintf(w, "title 的值为: %v <br>", title)
fmt.Fprintf(w, "title 的长度为: %v <br>", len(title))
fmt.Fprintf(w, "body 的值为: %v <br>", body)
fmt.Fprintf(w, "body 的长度为: %v <br>", len(body))
} else {
fmt.Fprintf(w, "有错误发生,errors 的值为: %v <br>", errors)
}
}
.
.
.
errors := make(map[string]string)
这行代码的含义是创建了一个空的map,其中键的类型为string,值的类型也为string。这个map被命名为errors。
访问并输入数据
在Java中,一个汉字通常占用两个字节,因为Java中使用的是UTF-16编码,每个字符通常占用16位,即2个字节。这使得Java中处理Unicode字符和汉字比较方便。
在Go语言中,一个汉字通常占用3个字节,因为Go语言中的字符串是以UTF-8格式保存的,而UTF-8编码中一个汉字通常由3个字节表示。
若要按字符个数计算而非字节数,可以使用Go语言中的utf8
包中的RuneCountInString()
函数来计算。这个函数会返回字符串中Unicode字符的个数,而不是字节数。
修改代码
go
.
.
.
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {
title := r.PostFormValue("title")
body := r.PostFormValue("body")
errors := make(map[string]string)
// 验证标题
if title == "" {
errors["title"] = "标题不能为空"
} else if utf8.RuneCountInString(title) < 3 || utf8.RuneCountInString(title) > 40 {
errors["title"] = "标题长度需介于 3-40"
}
// 验证内容
if body == "" {
errors["body"] = "内容不能为空"
} else if utf8.RuneCountInString(body) < 10 {
errors["body"] = "内容长度需大于或等于 10 个字节"
}
// 检查是否有错误
if len(errors) == 0 {
fmt.Fprint(w, "验证通过!<br>")
fmt.Fprintf(w, "title 的值为: %v <br>", title)
fmt.Fprintf(w, "title 的长度为: %v <br>", utf8.RuneCountInString(title))
fmt.Fprintf(w, "body 的值为: %v <br>", body)
fmt.Fprintf(w, "body 的长度为: %v <br>", utf8.RuneCountInString(body))
} else {
fmt.Fprintf(w, "有错误发生,errors 的值为: %v <br>", errors)
}
}
.
.
.
网页验证
3.2 出错提示
数据验证的逻辑
- 正确则存入数据库
- 错误则重新显示表单,并显示错误提示
erros大于0时,我们需要将错误信息的参数渲染到HTML中,html/template
标准库专门设计解决这种场景。
go
.
.
.
// ArticlesFormData 创建博文表单数据
type ArticlesFormData struct {
Title, Body string
URL *url.URL
Errors map[string]string
}
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {
.
.
.
// 检查是否有错误
if len(errors) == 0 {
.
.
.
} else {
html := `
<!DOCTYPE html>
<html lang="en">
<head>
<title>创建文章 ------ 我的技术博客</title>
<style type="text/css">.error {color: red;}</style>
</head>
<body>
<form action="{{ .URL }}" method="post">
<p><input type="text" name="title" value="{{ .Title }}"></p>
{{ with .Errors.title }}
<p class="error">{{ . }}</p>
{{ end }}
<p><textarea name="body" cols="30" rows="10">{{ .Body }}</textarea></p>
{{ with .Errors.body }}
<p class="error">{{ . }}</p>
{{ end }}
<p><button type="submit">提交</button></p>
</form>
</body>
</html>
`
storeURL, _ := router.Get("articles.store").URL()
data := ArticlesFormData{
Title: title,
Body: body,
URL: storeURL,
Errors: errors,
}
tmpl, err := template.New("create-form").Parse(html)
if err != nil {
panic(err)
}
err = tmpl.Execute(w, data)
if err != nil {
panic(err)
}
}
}
.
.
.
首先我们定义一个 ArticlesFormData struct,用以给模板文件传输变量时使用。
接下来是构建 ArticlesFormData 里的数据,storeURL 是通过路由参数生成的 URL 路径。