【go项目01_学习记录07】

学习记录

  • [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 出错提示

数据验证的逻辑

  1. 正确则存入数据库
  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 路径。


相关推荐
酷讯网络_2408701601 分钟前
【全开源】Java多语言tiktok跨境商城TikTok内嵌商城送搭建教程
java·开发语言·开源
PP东25 分钟前
ES学习class类用法(十一)
javascript·学习
海威的技术博客29 分钟前
JS中的原型与原型链
开发语言·javascript·原型模式
WPG大大通37 分钟前
基于DIODES AP43781+PI3USB31531+PI3DPX1207C的USB-C PD& Video 之全功能显示器连接端口方案
c语言·开发语言·计算机外设·开发板·电源·大大通
从以前1 小时前
【算法题解】Bindian 山丘信号问题(E. Bindian Signaling)
开发语言·python·算法
high20111 小时前
【Java 基础】-- ArrayList 和 Linkedlist
java·开发语言
1nullptr1 小时前
lua和C API库一些记录
开发语言·lua
Jerry Nan1 小时前
Lua元表
开发语言·lua