# Baa Context
Context是HTTP上下文的意思,封裝了`輸入`、`輸出`,提供請求處理,結果響應,模板渲染等相關操作。
## Request
`c.Req`
Request 中包含了所有的請求數據,是標準包中 `http.Request` 的封裝,可以通過 `c.Req` 訪問原生的請求結構。
### URL參數
`func (c *Context) Posts() map[string]interface{}`
獲取所有的POST 和 GET 數據,返回一個字典,字典的索引為表單字段的名稱,值為字段的值,值如果只有一個則為 `string` 類型,值有多個則為 `[]string` 類型。
`func (c *Context) Querys() map[string]interface{}`
獲取所有的 GET 數據,返回一個字典,字典的索引為表單字段的名稱,值為字段的值,值如果只有一個則為 `string` 類型,值有多個則為 `[]string` 類型。
`func (c *Context) Query(name string) string`
根據 `name` 獲取一個字段的值,返回 `string` 類型,包含 POST 和 GET 數據。
`func (c *Context) QueryStrings(name string) []string`
根據 `name` 獲取一個字段的多個值,返回 `[]string` 類型。
`func (c *Context) QueryEscape(name string) string`
根據 `name` 獲取一個字段的值,escapre編碼后返回 `string` 類型。
`func (c *Context) QueryTrim(name string) string`
根據 `name` 獲取一個字段的值,去除兩端空白后返回 `string` 類型。
`func (c *Context) QueryBool(name string) bool`
根據 `name` 獲取一個字段的值,并強制轉化為 `bool` 類型 返回。
`func (c *Context) QueryFloat(name string) float64`
根據 `name` 獲取一個字段的值,并強制轉化為 `float64` 類型 返回。
`func (c *Context) QueryInt(name string) int`
根據 `name` 獲取一個字段的值,并強制轉化為 `int` 類型 返回。
`func (c *Context) QueryInt32(name string) int32`
根據 `name` 獲取一個字段的值,并強制轉化為 `int32` 類型 返回。
`func (c *Context) QueryInt64(name string) int64`
根據 `name` 獲取一個字段的值,并強制轉化為 `int64` 類型 返回。
### 路由參數
`func (c *Context) Param(name string) string`
根據 `name` 獲取一個路由參數的值,返回 `string` 類型。
`func (c *Context) Params() map[string]string`
返回所有的路由參數組成的字典,字典的索引是參數名稱。
`func (c *Context) ParamBool(name string) bool`
根據 `name` 獲取一個路由參數的值,并強制轉化為 `bool` 類型 返回。
`func (c *Context) ParamFloat(name string) float64`
根據 `name` 獲取一個路由參數的值,并強制轉化為 `float64` 類型 返回。
`func (c *Context) ParamInt(name string) int`
根據 `name` 獲取一個路由參數的值,并強制轉化為 `int` 類型 返回。
`func (c *Context) ParamInt32(name string) int32`
根據 `name` 獲取一個路由參數的值,并強制轉化為 `int32` 類型 返回。
`func (c *Context) ParamInt64(name string) int64`
根據 `name` 獲取一個路由參數的值,并強制轉化為 `int64` 類型 返回。
### Cookie
`func (c *Context) GetCookie(name string) string`
根據 `name` 獲取一個Cookie的值,返回 `string` 類型。
`func (c *Context) GetCookieBool(name string) bool`
根據 `name` 獲取一個Cookie的值,并強制轉化為 `bool` 類型 返回。
`func (c *Context) GetCookieFloat64(name string) float64`
根據 `name` 獲取一個Cookie的值,并強制轉化為 `float64` 類型 返回。
`func (c *Context) GetCookieInt(name string) int`
根據 `name` 獲取一個Cookie的值,并強制轉化為 `int` 類型 返回。
`func (c *Context) GetCookieInt32(name string) int32`
根據 `name` 獲取一個Cookie的值,并強制轉化為 `int32` 類型 返回。
`func (c *Context) GetCookieInt64(name string) int64`
根據 `name` 獲取一個Cookie的值,并強制轉化為 `int64` 類型 返回。
`func (c *Context) SetCookie(name string, value string, others ...interface{})`
設置 名稱為 `name` 值為 `value` 的Cookie。
`setCookie` 還可以指定更多Cookie參數,通過可變長度的 `others` 參數可以依次指定:
```
SetCookie(<name>, <value>, <max age>, <path>, <domain>, <secure>, <http only>)
```
使用姿勢:
```
c.SetCookie("mykey", "myvalue")
c.SetCookie("mykey", "myvalue", 3600)
c.SetCookie("mykey", "myvalue", 3600, "/")
c.SetCookie("mykey", "myvalue", 3600, "/", ".vodjk.com")
c.SetCookie("mykey", "myvalue", 3600, "/", ".vodjk.com", true)
c.SetCookie("mykey", "myvalue", 3600, "/", ".vodjk.com", true, true)
```
> 可變參數需依次指定,不能跳過中間的參數
### 文件上傳
`func (c *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, error)`
通過 `multipart/form-data` 表單上傳的文件,可以通過 `c.GetFile` 指定文件字段獲得到文件結構
[multipart.File](https://godoc.org/mime/multipart#File) 和 [multipart.FileHeader](https://godoc.org/mime/multipart#FileHeader),
然后可以利用這些結構進行文件大小檢測,文件類型檢測,文件存儲等。
舉個栗子:
```
func Upload(c *baa.Context) {
file, header, err := c.GetFile("file1")
if err != nil {
c.Error(errors.New("沒有文件被上傳"))
return
}
defer file.Close()
savedTo := "savedFile.jpg"
newFile, err := os.Create(savedTo)
if err != nil {
c.Error(errors.New("文件創建失敗"))
return
}
defer newFile.Close()
size, err := io.Copy(newFile, file)
msg := fmt.Sprintf("fileName: %s, savedTo: %s, size: %d, err: %v", header.Filename, savedTo, size, err)
fmt.Println(msg)
c.String(200, msg)
}
```
`func (c *Context) SaveToFile(name, savePath string) error`
快速保存指定的文件字段 `name` 到指定的路徑 `savePath` 并返回 `nil` 或者 `error`
還是上面的例子,改一下:
```
func Upload2(c *baa.Context) {
savedTo := "savedFile2.jpg"
err := c.SaveToFile("file1", savedTo)
if err != nil {
c.Error(err)
return
}
c.String(200, savedTo)
}
```
## Response
`c.Resp`
Response 中用于處理結果輸出,是標準包中 `http.ResponseWriter` 的封裝,可以通過 `c.Resp` 訪問原生的接口。
### 數據存儲
`Context` 提供了臨時存儲可用于整個請求的生命周期中。
`func (c *Context) Set(key string, v interface{})`
根據 `key` 設置一個值 `v` 到 `Context` 存儲中。
`func (c *Context) Get(key string) interface{}`
根據 `key` 從`Context`存儲中獲取一個值并返回。
`func (c *Context) Gets() map[string]interface{}`
獲取`Context`中存儲的所有值,返回由這些值組成的字典。
舉個例子:
```
package main
import (
"gopkg.in/baa.v1"
)
func main() {
app := baa.New()
app.Get("/", func(c *baa.Context) {
c.Set("mykey", "myvalue")
}, func(c *baa.Context) {
val := c.Get("mykey")
fmt.Println(val) // myvalue
c.String(200, val.(string))
})
app.Run(":1323")
}
```
### 內容輸出
baa 提供了多種形式的內容輸出。
`func (c *Context) String(code int, s string)`
設定輸出的 http code 為 `code`,并輸出一個字符串 `s`。
`func (c *Context) Text(code int, s []byte)`
設定輸出的 http code 為 `code`,并輸出一個字節切片 `s`。
`func (c *Context) JSON(code int, v interface{})`
設定輸出的 http code 為 `code`,設定內容類型為 `application/json`, 把 結構 `v` 使用JSON編碼后輸出。
`func (c *Context) JSONP(code int, callback string, v interface{})`
設定輸出的 http code 為 `code`,設定內容類型為 `application/json`, 把 結構 `v` 使用JSON編碼,并結合 `callback`參數輸出。
`func (c *Context) JSONString(v interface{}) (string, error)`
把 結構 `v` 使用JSON編碼后返回。
`func (c *Context) XML(code int, v interface{})`
設定輸出的 http code 為 `code`,設定內容類型為 `application/json`, 把 結構 `v` 使用XML編碼后輸出。
## 有用的函數
`func (c *Context) Baa() *Baa`
`func (c *Context) Body() *RequestBody`
`func (c *Context) Break()`
`func (c *Context) Error(err error)`
`func (c *Context) Next()`
`func (c *Context) NotFound()`
`func (c *Context) IsAJAX() bool`
`func (c *Context) IsMobile() bool`
`func (c *Context) Redirect(code int, url string) error`
`func (c *Context) Referer() string`
`func (c *Context) RemoteAddr() string`
`func (c *Context) URL(hasQuery bool) string`
`func (c *Context) UserAgent() string`
## 模板渲染
baa 集成一個簡單的模板渲染,使用Go標準庫的 [template語法](https://godoc.org/html/template)。
baa 的模板渲染使用 `Context`存儲 中的數據作為模板變量。
`func (c *Context) Fetch(tpl string) ([]byte, error)`
根據 `tpl` 模板文件路徑,使用 `Context`存儲中的數據,渲染模板并返回渲染后的內容。
`func (c *Context) Render(code int, tpl string)`
設定輸出的 http code 為 `code`,設定內容類型為 `text/html`, 渲染模板 `tpl` 并直接輸出。
> 內部就是調用的 `c.Fetch` 然后 把內容輸出
`func (c *Context) HTML(code int, tpl string)`
`c.Render`的一個別名,用起來更順手。
舉個例子:
```
package main
import baa "gopkg.in/baa.v1"
func main() {
app := baa.New()
app.Get("/", func(c *baa.Context) {
c.Set("title", "this is title")
c.Set("content", "this is content")
c.HTML(200, "template/index.html")
})
app.Run(":1323")
}
```
```
<!-- template/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ .title }}</title>
</head>
<body>
{{ .content}}
</body>
</html>
```
### 模板語法
以下僅做簡單介紹,完整文檔請見官方 [html/template](https://godoc.org/html/template)。
* 模板語法以一對 `雙大括號` 包裹
* 變量都是以 `.` 開始,`.` 代表所有數據組成的結構
* 結構體中的字段用 `.` 表示子集
#### 輸出變量
```
{{ .var }}
{{ .user.id }}
```
#### 條件語句
```
{{ if .show }}
i want show!
{{ else }}
i was hidden
{{ end }}
```
#### 循環語句
```
{{ range .list }}
{{ .id }}
{{ .name }}
{{ end }}
```
如果循環體不是結構體,比如就是一個字符串數組,直接用 `.` 即可輸出:
```
{{ range .strs }}
{{ . }}
{{ end }}
```
### 模板接口
baa 中運行通過 `DI` 替換模板引擎,只要實現 `baa.Renderer` 接口即可。
Renderer
```
type Renderer interface {
Render(w io.Writer, tpl string, data interface{}) error
}
```
渲染接口只有一個方法 `Render`,該方法 接收三個參數:
#### w `io.Writer`
一個可寫入的類型,用于寫入渲染后的數據,這里其實就是 `c.Resp` 。
#### tpl `string`
模板文件路徑
#### data `interface{}`
向模板傳遞的數據(模板變量),這里其實傳遞過來的就是 `c.Gets` 的結果,是一個 `map[string]interface{}` 類型。