goweb-表單

表單

簡單的處理一個登錄界面

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屬性有以下三種狀況:

  • application/x-www-form-urlencoded 表示在發送前編碼全部字符(默認)
  • multipart/form-data 不對字符編碼。在使用包含文件上傳控件的表單時,必須使用該值。
  • text/plain 空格轉換爲 "+" 加號,但不對特殊字符編碼。

上傳文件主要三步處理:

表單中增長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對字符串的正則處理。

客戶端和服務器端是如何進行數據上的交互,客戶端將數據傳遞給服務器系統,服務器接受數據又把處理結果反饋給客戶端。

連接

相關文章
相關標籤/搜索