在前一篇中,我们直接在 index 这个 handler func 中解析了模板,定义了数据,然后执行模板显示“拼合”了数据的网页。这是一个客户被动看的页面。实际的应用显然需要能够处理用户的请求。对于浏览器客户端的请求,我们先要来了解和请求有关的东西,如 URL、请求头部、请求主体、表单、文件上传、JSON主体、cookie等。
继续前面web学习随便记1中的代码:添加一个 handler func headers 如下
func headers(w http.ResponseWriter, r *http.Request) {
	url := r.URL
	// fmt.Fprintln(w, url.Scheme)
	// fmt.Fprintln(w, url.Opaque)
	// fmt.Fprintln(w, url.User.Username())
	// fmt.Fprintln(w, url.Host)
	fmt.Fprintln(w, url.Path)
	fmt.Fprintln(w, url.RawQuery)
	// fmt.Fprintln(w, url.Fragment)
	h := r.Header
	fmt.Fprintln(w, h)
	fmt.Fprintln(w, h["User-Agent"])
}主函数中添加路由 /headers 的处理
// ...............
	mux.HandleFunc("/", index)
	mux.HandleFunc("/headers", headers)
	server := &http.Server{
		Addr:    "0.0.0.0:8088",
		Handler: mux,
	}
// .................运行后,在浏览器地址栏输入 http://localhost:8088/headers?k1=v1&k2=bbb 结果类似如下:

看来,读取 URL 和 请求头部中的信息还是比较容易的。
类似地,我们添加 handler func body 如下:获取请求主体的长度,定义一个字节数组,将主体读入该数组,转成字符串显示
func body(w http.ResponseWriter, r *http.Request) {
	len := r.ContentLength
	body := make([]byte, len)
	r.Body.Read(body)
	fmt.Fprintln(w, string(body))
}路由添加 mux.HandleFunc("/body", body),运行。对于显示主体,因为我们没有表单,所以,用 curl 命令来查看结果
sjg@sjg-PC:~/go/src$ curl -id "family_name=Shen&name=Beta" 127.0.0.1:8088/body
HTTP/1.1 200 OK
Date: Fri, 05 May 2023 09:22:55 GMT
Content-Length: 27
Content-Type: text/plain; charset=utf-8
family_name=Shen&name=Beta
我们来进入表单的世界。在用来存放静态文件的目录 chitchat/public 下创建 client.html:
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Golang表单数据获取</title>
</head>
<body>
    <form action="/process?key1=张三&key2=123" method="post" enctype="application/x-www-form-urlencoded">
        <input type="text" name="key1" value="李四" />
        <input type="text" name="key3" value="456" />
        <input type="submit" />
    </form>
</body>
</html>表单最后会提交到 /process 这个路由路径上,所以,我们在主函数main中添加 mux.HandleFunc("/process", process),同时创建 handler func process 如下:http.Request对象的方法ParseForm()会对请求进行语法分析,而该对象Form字段可以获取表单字段信息
func process(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fmt.Fprintln(w, r.Form)
}现在打开浏览器,地址栏输入 http://localhost:8088/static/client.html,将显示如下表单

点击提交后,显示如下
 
上述显示结果表明,使用 r.Form字段获取的信息为一个map,对于表单和URL两者同名的键,对应的值都会保存在该字段。简单来说,r.Form字段信息中某个key对应的值为一个切片,当key在表单和URL中是唯一的时候,用r.Form获取该key对应值没有问题;而key不唯一时,将无法区分两个值(虽然一般表单值会排在前面,但通常不该依赖顺序)。那么,要只获取表单Post提交的数据怎么办呢?答案是使用 r.PostForm 字段。
修改 handler func process:
func process(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fmt.Fprintln(w, r.PostForm)
}重复前面的过程,显示如下:

上图只显示表单中的字段和值,这个道理和 PHP 中超级全局变量$_REQUEST可以同时获取GET和POST提交的信息,而超级全局变量$_POST只获取POST提交的信息是类似的。但下面的情况,两者就不类似了。
我们把表单enctype修改一下:
    <form action="/process?key1=张三&key2=123" method="post" enctype="multipart/form-data">重复前面的过程,显示将是

这表明,r.PostForm 字段无法获取 multipart/form-data 格式编码的表单信息。而如果使用r.Form字段,显示将变成

只能获取URL中的字段信息,还是没有表单信息。此时,我们需要通过 r.MultipartForm 字段来获取 multipart/form-data 格式编码的表单数据(注意:multipart/form-data格式和url编码格式不同,语法解析的方法也不同,要用 r.ParseMultipartForm(指定长度),整个请求体解析后,指定长度的数据会放入内存,其余部分放在临时文件中)。
func process(w http.ResponseWriter, r *http.Request) {
	r.ParseMultipartForm(1024)
	fmt.Fprintln(w, r.MultipartForm)
}显示结果为

我们可以发现,r.MultipartForm字段的值不是单个map,而是两个map组成的结构体。上图中第一个map包含了表单的值,第二个map是空的,因为它是用于记录用户上传的文件的(第二个map相当于PHP中的超级全局变量$_FILES,而第一个对应$_POST)。
有时候,我们只需要一两个表单字段的值,获取包含全体字段的map,再根据键取值有点麻烦。此时,我们可以使用 r.FormValue(键名) 和 r.PostFormValue(键名) 这两个方法(使用这两个方法时,不需要在其前面使用ParseForm或者ParseMultipartForm方法)
修改 handler func process 如下,表单使用url编码:
func process(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, r.FormValue("key1"))
	fmt.Fprintln(w, r.FormValue("key2"))
	fmt.Fprintln(w, r.FormValue("key3"))
}显示结果为:

注意:key1对应的值,只显示表单中的值,没有显示URL中的值。查询 r.FormValue 文档可以知道,它返回查询到的第一个值,而 POST和PUT传递的值会优先于URL查询串中的值。
将上述代码中的 FormValue 全部替换为 PostFormValue,显示结果如下:

此时 key2因为没有Post方法对应值,所以没有输出。也许是因为我的Golang版本1.17,书上说的(Golang1.4)表单编码使用multipart/form-data编码时,FormValue或PostFormValue无法获取值的情况不存在。
事实上,不是为了文件上传,我们不会使用multipart/form-data来编码表单,因为url编码方式更高效。下面来看看文件上传问题。我们给表单添加一个file类型的字段:
................
        <input type="text" name="key1" value="李四" />
        <input type="text" name="key3" value="456" />
        <input type="file" name="upload" />
        <input type="submit" />
................修改 handler func process 如下 (解析表单,获得上传的第1个文件的文件头,打开该文件头对应的文件对象,读取文件数据并在浏览器输出)
func process(w http.ResponseWriter, r *http.Request) {
	r.ParseMultipartForm(1024)
	fileHeader := r.MultipartForm.File["upload"][0]
	file, err := fileHeader.Open()
	if err == nil {
		data, err := ioutil.ReadAll(file)
		if err == nil {
			fmt.Fprintln(w, string(data))
		}
	}
}试验时,注意选择文本文件,并且最好只有英文字符(因为中文涉及编码,有可能看到的是“乱码”)。我们要直接获取上传的文件的信息也是可能的,从而不用先parse,再获取头部,最后打开这样的复制步骤。答案是使用 r.FormFile()方法返回3值,parse、获取头部,打开一气呵成。
func process(w http.ResponseWriter, r *http.Request) {
	// r.ParseMultipartForm(1024)
	// fileHeader := r.MultipartForm.File["upload"][0]
	// file, err := fileHeader.Open()
	file, _, err := r.FormFile("upload")
	if err == nil {








![【LeetCode】数据结构题解(6)[回文链表]](https://img-blog.csdnimg.cn/52ade4d5b8c6462388f2aba13525c948.png#pic_center)









![[MySQL / Mariadb] 数据库学习-Linux中安装MySQL,YUM方式](https://img-blog.csdnimg.cn/9ff8e655589e4209986b04ed6a24cf10.png)