<commit_before>#4.1处理表单的输入

我们先来看一个表单递交的例子，我们有如下的表单内容，命名成文件login.gtpl

	<html>
	<head>
	<title></title>
	</head>
	<body>
	<form action="http://127.0.0.1:9090/login" method="post">
	    用户名:<input type="text" name="username">
	    密码:<input type="password" name="password">
	    <input type="submit" value="登陆">
	</form>
	</body>
	</html>

上面递交表单到/login里面，当用户输入信息之后点击登陆之后，跳转到我们的login里面，我们首先要判断这个是什么方式过来，post还是get呢？

http包里面有一个很简单的方式就可以获取，我们在前面web的例子基础之上来看看怎么处理login，怎么读取form数据


	package main

	import (
		"fmt"
		"html/template"
		"log"
		"net/http"
		"strings"
	)

	func sayhelloName(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()       //解析参数，默认是不会解析的
		fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
		fmt.Println("path", r.URL.Path)
		fmt.Println("scheme", r.URL.Scheme)
		fmt.Println(r.Form["url_long"])
		for k, v := range r.Form {
			fmt.Println("key:", k)
			fmt.Println("val:", strings.Join(v, ""))
		}
		fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
	}

	func login(w http.ResponseWriter, r *http.Request) {
		fmt.Println("method:", r.Method) //获取请求的方法
		if r.Method == "GET" {
			t, _ := template.ParseFiles("login.gtpl")
			t.Execute(w, nil)
		} else {
			//请求的是登陆数据，那么执行登陆的逻辑判断
			fmt.Println("username:", r.Form["username"])
			fmt.Println("password:", r.Form["password"])
		}
	}

	func main() {
		http.HandleFunc("/", sayhelloName)       //设置访问的路由
		http.HandleFunc("/login", login)         //设置访问的路由
		err := http.ListenAndServe(":9090", nil) //设置监听的端口
		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}
	}

	
通过上面的代码我们可以看出获取请求方法是通过`r.Method`来完成的，这是个字符串类型的变量，返回GET, POST, PUT等method信息。

login函数中我们根据`r.Method`来判断是显示登录界面还是处理登录逻辑。当GET方式请求时显示登录界面，其他方式处理登录(查询数据库之类的)。

当我们在浏览器里面输入`http://127.0.0.1:9090/login`的时候，出现如下界面

![](images/4.1.login.png?raw=true)

我们输入用户名和密码之后发现在服务器端是不会打印出来任何东西的，为什么呢？默认情况下，Handler里面是不会自动解析form的，必须显示的调用`r.ParseForm()`后，你才能对这个参数进行操作。我们修改一下代码，在`fmt.Println("username:", r.Form["username"])`之前加一行`r.ParseForm()`,重新编译，再次测试输入递交，现在是不是在服务器端有输出你的输入的用户名和密码了。

`r.Form`里面包含了所有请求的URL中query-string、POST的数据、PUT的数据，所有当你在URL的query-string字段和POST冲突时，会保存成一个slice，里面存储了多个值，Go官方文档中说在接下来的版本里面将会把POST、GET这些数据分离开来。	

现在我们修改一下login.gtpl里面form的action值`http://127.0.0.1:9090/login`修改为`http://127.0.0.1:9090/login?username=astaxie`，再次测试，服务器的输出username是不是一个slice。s服务器端的输出如下：

![](images/4.1.slice.png?raw=true)

`r.Form`是一个url.Values类型，里面存储的是对应的key=value信息，下面展示了可以对form进行的一些操作:

	v := url.Values{}
	v.Set("name", "Ava")
	v.Add("friend", "Jess")
	v.Add("friend", "Sarah")
	v.Add("friend", "Zoe")
	// v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
	fmt.Println(v.Get("name"))
	fmt.Println(v.Get("friend"))<commit_msg>fix typo<commit_after>#4.1处理表单的输入

我们先来看一个表单递交的例子，我们有如下的表单内容，命名成文件login.gtpl

	<html>
	<head>
	<title></title>
	</head>
	<body>
	<form action="http://127.0.0.1:9090/login" method="post">
	    用户名:<input type="text" name="username">
	    密码:<input type="password" name="password">
	    <input type="submit" value="登陆">
	</form>
	</body>
	</html>

上面递交表单到/login里面，当用户输入信息之后点击登陆之后，跳转到我们的login里面，我们首先要判断这个是什么方式过来，post还是get呢？

http包里面有一个很简单的方式就可以获取，我们在前面web的例子基础之上来看看怎么处理login，怎么读取form数据


	package main

	import (
		"fmt"
		"html/template"
		"log"
		"net/http"
		"strings"
	)

	func sayhelloName(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()       //解析参数，默认是不会解析的
		fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
		fmt.Println("path", r.URL.Path)
		fmt.Println("scheme", r.URL.Scheme)
		fmt.Println(r.Form["url_long"])
		for k, v := range r.Form {
			fmt.Println("key:", k)
			fmt.Println("val:", strings.Join(v, ""))
		}
		fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
	}

	func login(w http.ResponseWriter, r *http.Request) {
		fmt.Println("method:", r.Method) //获取请求的方法
		if r.Method == "GET" {
			t, _ := template.ParseFiles("login.gtpl")
			t.Execute(w, nil)
		} else {
			//请求的是登陆数据，那么执行登陆的逻辑判断
			fmt.Println("username:", r.Form["username"])
			fmt.Println("password:", r.Form["password"])
		}
	}

	func main() {
		http.HandleFunc("/", sayhelloName)       //设置访问的路由
		http.HandleFunc("/login", login)         //设置访问的路由
		err := http.ListenAndServe(":9090", nil) //设置监听的端口
		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}
	}

	
通过上面的代码我们可以看出获取请求方法是通过`r.Method`来完成的，这是个字符串类型的变量，返回GET, POST, PUT等method信息。

login函数中我们根据`r.Method`来判断是显示登录界面还是处理登录逻辑。当GET方式请求时显示登录界面，其他方式处理登录(查询数据库之类的)。

当我们在浏览器里面输入`http://127.0.0.1:9090/login`的时候，出现如下界面

![](images/4.1.login.png?raw=true)

我们输入用户名和密码之后发现在服务器端是不会打印出来任何东西的，为什么呢？默认情况下，Handler里面是不会自动解析form的，必须显示的调用`r.ParseForm()`后，你才能对这个参数进行操作。我们修改一下代码，在`fmt.Println("username:", r.Form["username"])`之前加一行`r.ParseForm()`,重新编译，再次测试输入递交，现在是不是在服务器端有输出你的输入的用户名和密码了。

`r.Form`里面包含了所有请求的URL中query-string、POST的数据、PUT的数据，所有当你在URL的query-string字段和POST冲突时，会保存成一个slice，里面存储了多个值，Go官方文档中说在接下来的版本里面将会把POST、GET这些数据分离开来。	

现在我们修改一下login.gtpl里面form的action值`http://127.0.0.1:9090/login`修改为`http://127.0.0.1:9090/login?username=astaxie`，再次测试，服务器的输出username是不是一个slice。服务器端的输出如下：

![](images/4.1.slice.png?raw=true)

`r.Form`是一个url.Values类型，里面存储的是对应的key=value信息，下面展示了可以对form进行的一些操作:

	v := url.Values{}
	v.Set("name", "Ava")
	v.Add("friend", "Jess")
	v.Add("friend", "Sarah")
	v.Add("friend", "Zoe")
	// v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
	fmt.Println(v.Get("name"))
	fmt.Println(v.Get("friend"))
{'fraction_non_alphanumeric': 0.18305084745762712, 'fraction_numerical': 0.018832391713747645, 'mean_word_length': 5.322619047619048, 'pattern_counts': {'":': 2, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 8, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6727093', 'n_tokens_mistral': 3062, 'n_tokens_neox': 3139, 'n_words': 246}