表單
簡單的處理一個登陸界面
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資料進行的一些操作:
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只會回傳同名引數中的第一個,若引數不存在則回傳空字串,
服務端表單驗證
這一部分講了驗證數字,中文,名字,號碼等許多需要驗證的東西,主要有:通過正則驗證,通過邏輯結構驗證并呼叫相關的包
預防跨站腳本
現在的網站包含大量的動態內容以提高用戶體驗,比過去要復雜得多,所謂動態內容,就是根據用戶環境和需要,Web應用程式能夠輸出相應的內容,動態站點會受到一種名為“跨站腳本攻擊”(Cross Site Scripting, 安全專家們通常將其縮寫成 XSS)的威脅,而靜態站點則完全不受其影響,
攻擊者通常會在有漏洞的程式中插入JavaScript、VBScript、 ActiveX或Flash以欺騙用戶,一旦得手,他們可以盜取用戶帳戶資訊,修改用戶設定,盜取/污染cookie和植入惡意廣告等,
對XSS最佳的防護應該結合以下兩種方法:一是驗證所有輸入資料,有效檢測攻擊(這個我們前面小節已經有過介紹);另一個是對所有輸出資料進行適當的處理,以防止任何已成功注入的腳本在瀏覽器端運行,
主要通過轉義來實作這個,用到text/template和html/template
防止多次遞交表單
不知道你是否曾經看到過一個論壇或者博客,在一個帖子或者文章后面出現多條重復的記錄,這些大多數是因為用戶重復遞交了留言的表單引起的,由于種種原因,用戶經常會重復遞交表單,通常這只是滑鼠的誤操作,如雙擊了遞交按鈕,也可能是為了編輯或者再次核對填寫過的資訊,點擊了瀏覽器的后退按鈕,然后又再次點擊了遞交按鈕而不是瀏覽器的前進按鈕,當然,也可能是故意的——比如,在某項在線調查或者博彩活動中重復投票,那我們如何有效的防止用戶多次遞交相同的表單呢?
解決方案是在表單中添加一個帶有唯一值的隱藏欄位,在驗證表單時,先檢查帶有該唯一值的表單是否已經遞交過了,如果是,拒絕再次遞交;如果不是,則處理表單進行邏輯處理,另外,如果是采用了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對字串的正則處理,
客戶端和服務器端是如何進行資料上的互動,客戶端將資料傳遞給服務器系統,服務器接受資料又把處理結果反饋給客戶端,
鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/49402.html
標籤:Go
上一篇:goweb-goweb基礎
下一篇:goweb-訪問資料庫
