# 表單支援

在 Web 開發中對於這樣的一個流程可能很眼熟：

* 開啟一個網頁顯示出表單。
* 使用者填寫並提交了表單。
* 如果使用者提交了一些無效的資訊，或者可能漏掉了一個必填項，表單將會連同使用者的資料和錯誤問題的描述資訊回傳。
* 使用者再次填寫，繼續上一步過程，直到提交了一個有效的表單。

在接收端，指令碼必須：

* 檢查使用者提交的表單資料。
* 驗證資料是否為正確的型別，合適的標準。例如，如果一個使用者名稱被提交，它必須被驗證是否只包含了允許的字元。它必須有一個最小長度，不能超過最大長度。使用者名稱不能與已存在的他人使用者名稱重複，甚至是一個保留字等。
* 過濾資料並清理不安全字元，保證邏輯處理中接收的資料是安全的。
* 如果需要，預格式化資料（資料需要清除空白或者經過 HTML 編碼等等。）
* 準備好資料，插入資料庫。

儘管上面的過程並不是很複雜，但是通常情況下需要編寫很多程式碼，而且為了顯示錯誤資訊，在網頁中經常要使用多種不同的控制結構。建立表單驗證雖簡單，實施起來實在枯燥無味。

## 表單和驗證

對於開發者來說，一般開發過程都是相當複雜，而且大多是在重複一樣的工作。假設一個場景專案中忽然需要增加一個表單資料，那麼區域性程式碼的整個流程都需要修改。我們知道 Go 裡面 struct 是常用的一個數據結構，因此 beego 的 form 採用了 struct 來處理表單資訊。

首先定義一個開發 Web 應用時相對應的 struct，一個欄位對應一個 form 元素，透過 struct 的 tag 來定義相應的元素資訊和驗證資訊，如下所示：

```go
type User struct{
    Username     string     `form:text,valid:required`
    Nickname     string     `form:text,valid:required`
    Age            int     `form:text,valid:required|numeric`
    Email         string     `form:text,valid:required|valid_email`
    Introduce     string     `form:textarea`
}
```

定義好 struct 之後接下來在 controller 中這樣操作

```go
func (this *AddController) Get() {
    this.Data["form"] = beego.Form(&User{})
    this.Layout = "admin/layout.html"
    this.TplNames = "admin/add.tpl"
}
```

在範本中這樣顯示錶單

```markup
<h1>New Blog Post</h1>
<form action="" method="post">
{{.form.render()}}
</form>
```

上面我們定義好了整個的第一步，從 struct 到顯示錶單的過程，接下來就是使用者填寫資訊，伺服器端接收資料然後驗證，最後插入資料庫。

```go
func (this *AddController) Post() {
    var user User
    form := this.GetInput(&user)
    if !form.Validates() {
        return
    }
    models.UserInsert(&user)
    this.Ctx.Redirect(302, "/admin/index")
}
```

## 表單型別

以下列表列出來了對應的 form 元素資訊：

| 名稱           | 參數 | 功能描述        |
| ------------ | -- | ----------- |
| **text**     | No | textbox 輸入框 |
| **button**   | No | 按鈕          |
| **checkbox** | No | 多選擇框        |
| **dropdown** | No | 下拉選擇框       |
| **file**     | No | 檔案上傳        |
| **hidden**   | No | 隱藏元素        |
| **password** | No | 密碼輸入框       |
| **radio**    | No | 單選框         |
| **textarea** | No | 文字輸入框       |

## 表單驗證

以下列表將列出可被使用的原生規則

| 規則                        | 參數  | 描述                                                                                                                                              | 舉例                       |
| ------------------------- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| **required**              | No  | 如果元素為空，則回傳 FALSE                                                                                                                                |                          |
| **matches**               | Yes | 如果表單元素的值與參數中對應的表單欄位的值不相等，則回傳 FALSE                                                                                                              | matches\[form\_item]     |
| **is\_unique**            | Yes | 如果表單元素的值與指定資料表欄位有重複，則回傳 False（譯者注：比如 is\_unique\[User.Email]，那麼驗證類別會去查詢 User 表中 Email 欄位有沒有與表單元素一樣的值，如存重複，則回傳 false，這樣開發者就不必另寫 Callback 驗證程式碼。） | is\_unique\[table.field] |
| **min\_length**           | Yes | 如果表單元素值的字元長度少於參數中定義的數字，則回傳 FALSE                                                                                                                | min\_length\[6]          |
| **max\_length**           | Yes | 如果表單元素值的字元長度大於參數中定義的數字，則回傳 FALSE                                                                                                                | max\_length\[12]         |
| **exact\_length**         | Yes | 如果表單元素值的字元長度與參數中定義的數字不符，則回傳 FALSE                                                                                                               | exact\_length\[8]        |
| **greater\_than**         | Yes | 如果表單元素值是非數字型別，或小於參數定義的值，則回傳 FALSE                                                                                                               | greater\_than\[8]        |
| **less\_than**            | Yes | 如果表單元素值是非數字型別，或大於參數定義的值，則回傳 FALSE                                                                                                               | less\_than\[8]           |
| **alpha**                 | No  | 如果表單元素值中包含除字母以外的其他字元，則回傳 FALSE                                                                                                                  |                          |
| **alpha\_numeric**        | No  | 如果表單元素值中包含除字母和數字以外的其他字元，則回傳 FALSE                                                                                                               |                          |
| **alpha\_dash**           | No  | 如果表單元素值中包含除字母/數字/下劃線/破折號以外的其他字元，則回傳 FALSE                                                                                                       |                          |
| **numeric**               | No  | 如果表單元素值中包含除數字以外的字元，則回傳 FALSE                                                                                                                    |                          |
| **integer**               | No  | 如果表單元素中包含除整數以外的字元，則回傳 FALSE                                                                                                                     |                          |
| **decimal**               | Yes | 如果表單元素中輸入（非小數）不完整的值，則回傳 FALSE                                                                                                                   |                          |
| **is\_natural**           | No  | 如果表單元素值中包含了非自然數的其他數值 （其他數值不包括零），則回傳 FALSE。自然數形如：0,1,2,3....等等。                                                                                  |                          |
| **is\_natural\_no\_zero** | No  | 如果表單元素值包含了非自然數的其他數值 （其他數值包括零），則回傳 FALSE。非零的自然數：1,2,3.....等等。                                                                                    |                          |
| **valid\_email**          | No  | 如果表單元素值包含不合法的 email 地址，則回傳 FALSE                                                                                                                |                          |
| **valid\_emails**         | No  | 如果表單元素值中任何一個值包含不合法的 email 地址（地址之間用英文逗號分割），則回傳 FALSE。                                                                                            |                          |
| **valid\_ip**             | No  | 如果表單元素的值不是一個合法的 IP 地址，則回傳 FALSE。                                                                                                                |                          |
| **valid\_base64**         | No  | 如果表單元素的值包含除了 base64 編碼字元之外的其他字元，則回傳 FALSE。                                                                                                      |                          |

## links

* [目錄](https://github.com/doggy8088/build-web-application-with-golang-zhtw/tree/4cbbaa31bdfc3678915eb91f23db2f1bad554a20/preface.md)
* 上一節: [Session 支援](https://willh.gitbook.io/build-web-application-with-golang-zhtw/14.0/14.2)
* 下一節: [使用者認證](https://willh.gitbook.io/build-web-application-with-golang-zhtw/14.0/14.4)
