package main import ( "fmt" "html/template" "log" "net/http" "strings" ) func sayhelloName(w http.ResponseWriter, r *http.Request) { r.ParseForm() //解析url传递的参数,对于POST则解析响应包的主体(request body) //注意:若是没有调用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") log.Println(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) } }
request.Form是一个url.Values类型,里面存储的是对应的相似key=value的信息,下面展现了能够对form数据进行的一些操做:javascript
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")) fmt.Println(v["friend"])
Request自己也提供了FormValue()函数来获取用户提交的参数。如r.Form["username"]也可写成r.FormValue("username")。调用r.FormValue时会自动调用r.ParseForm,因此没必要提早调用。r.FormValue只会返回同名参数中的第一个,若参数不存在则返回空字符串。html
这一部分讲了验证数字,中文,名字,号码等许多须要验证的东西,主要有:经过正则验证,经过逻辑结构验证并调用相关的包java
如今的网站包含大量的动态内容以提升用户体验,比过去要复杂得多。所谓动态内容,就是根据用户环境和须要,Web应用程序可以输出相应的内容。动态站点会受到一种名为“跨站脚本攻击”(Cross Site Scripting, 安全专家们一般将其缩写成 XSS)的威胁,而静态站点则彻底不受其影响。git
攻击者一般会在有漏洞的程序中插入JavaScript、VBScript、 ActiveX或Flash以欺骗用户。一旦得手,他们能够盗取用户账户信息,修改用户设置,盗取/污染cookie和植入恶意广告等。github
对XSS最佳的防御应该结合如下两种方法:一是验证全部输入数据,有效检测攻击(这个咱们前面小节已经有过介绍);另外一个是对全部输出数据进行适当的处理,以防止任何已成功注入的脚本在浏览器端运行。golang
主要经过转义来实现这个,用到text/template和html/templateweb
不知道你是否曾经看到过一个论坛或者博客,在一个帖子或者文章后面出现多条重复的记录,这些大多数是由于用户重复递交了留言的表单引发的。因为种种缘由,用户常常会重复递交表单。一般这只是鼠标的误操做,如双击了递交按钮,也多是为了编辑或者再次核对填写过的信息,点击了浏览器的后退按钮,而后又再次点击了递交按钮而不是浏览器的前进按钮。固然,也多是故意的——好比,在某项在线调查或者博彩活动中重复投票。那咱们如何有效的防止用户屡次递交相同的表单呢?浏览器
解决方案是在表单中添加一个带有惟一值的隐藏字段。在验证表单时,先检查带有该惟一值的表单是否已经递交过了。若是是,拒绝再次递交;若是不是,则处理表单进行逻辑处理。另外,若是是采用了Ajax模式递交表单的话,当表单递交后,经过javascript来禁用表单的递交按钮。安全
func login(w http.ResponseWriter, r *http.Request) { fmt.Println("method:", r.Method) //获取请求的方法 if r.Method == "GET" { crutime := time.Now().Unix() h := md5.New() io.WriteString(h, strconv.FormatInt(crutime, 10)) token := fmt.Sprintf("%x", h.Sum(nil)) t, _ := template.ParseFiles("login.gtpl") t.Execute(w, token) } else { //请求的是登录数据,那么执行登录的逻辑判断 r.ParseForm() token := r.Form.Get("token") if token != "" { //验证token的合法性 } else { //不存在token报错 } fmt.Println("username length:", len(r.Form["username"][0])) fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //输出到服务器端 fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) template.HTMLEscape(w, []byte(r.Form.Get("username"))) //输出到客户端 } }
利用惟一性来判断,但并不能防止恶意的攻击服务器
你想处理一个由用户上传的文件,好比你正在建设一个相似Instagram的网站,你须要存储用户拍摄的照片。这种需求该如何实现呢?
要使表单可以上传文件,首先第一步就是要添加form的enctype属性,enctype属性有以下三种状况:
上传文件主要三步处理:
表单中增长enctype="multipart/form-data" 服务端调用r.ParseMultipartForm,把上传的文件存储在内存和临时文件中 使用r.FormFile获取文件句柄,而后对文件进行存储等处理
Go支持模拟客户端表单功能支持文件上传,详细用法请看以下示例:
package main import ( "bytes" "fmt" "io" "io/ioutil" "mime/multipart" "net/http" "os" ) func postFile(filename string, targetUrl string) error { bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) //关键的一步操做 fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) if err != nil { fmt.Println("error writing to buffer") return err } //打开文件句柄操做 fh, err := os.Open(filename) if err != nil { fmt.Println("error opening file") return err } defer fh.Close() //iocopy _, err = io.Copy(fileWriter, fh) if err != nil { return err } contentType := bodyWriter.FormDataContentType() bodyWriter.Close() resp, err := http.Post(targetUrl, contentType, bodyBuf) if err != nil { return err } defer resp.Body.Close() resp_body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } fmt.Println(resp.Status) fmt.Println(string(resp_body)) return nil } // sample usage func main() { target_url := "http://localhost:9090/upload" filename := "./astaxie.pdf" postFile(filename, target_url) }
这一章里面咱们学习了Go如何处理表单信息,咱们经过用户登陆、上传文件的例子展现了Go处理form表单信息及上传文件的手段。可是在处理表单过程当中咱们须要验证用户输入的信息,考虑到网站安全的重要性,数据过滤就显得至关重要了,所以后面的章节中专门写了一个小节来说解了不一样方面的数据过滤,顺带讲一下Go对字符串的正则处理。
客户端和服务器端是如何进行数据上的交互,客户端将数据传递给服务器系统,服务器接受数据又把处理结果反馈给客户端。