1、go語言簡介
- 自然支持高併發
- 內存自動回收,不須要開發人員管理內存
- 支持管道,從而支持多個協程之間通訊
- 多返回值,一個函數能夠容許多個返回值
- 異常和錯誤的區分,異常是不能預料到的事情發生了,錯誤是預料到的事情發生了。
2、理解gopath
- gopath go項目的工做目錄,目錄下面包含三個目錄,src,pkg,bin
- goroot go源碼的安裝路徑
- gobin 存放go編譯後生成的可執行文件
- go get 執行go get會把源碼放在第一個gopath的src目錄下面
3、go基礎知識
3.1 基本數據結構和操做符
-
- 文件名&關鍵字&標識符(略)
-
- Go程序基本結構
-
- 任何一個代碼文件隸屬於一個包
-
- import 關鍵字,引用其餘包:
-
- golang可執行程序,package main,而且有且只有一個main入口函數
-
- 包中函數調用:a.同一個包中直接實用名稱調用;b。不一樣包中的函數經過包名+點+函數名稱進行調用
-
- 包訪問控制規則:大寫意味函數公有;小寫意味函數私有;
-
- 常量和變量
- 常量使用const 修飾,表明永遠是隻讀的,不能修改。
-
- const 只能修飾boolean,number(int相關類型、浮點類型、complex)和string。
-
- 數據類型和操做符
- 值類型:變量直接存儲值,內存一般在棧中分配。
- 值類型:基本數據類型int、float、bool、string以及數組和struct。
- 引用類型:變量存儲的是一個地址,這個地址存儲最終的值。內存一般在堆上分配。經過GC回收。
- 引用類型:指針、slice、map、chan等都是引用類型
- 在函數內部聲明的變量叫作局部變量,生命週期僅限於函數內部。
- 在函數外部聲明的變量叫作全局變量,生命週期做用於整個包,若是是大寫的,則做用於整個程序。
- bool類型,只能存true和false
- 相關操做符, !、&&、||
- 數字類型,主要有int、int八、int1六、int3二、int6四、uint八、uint1六、uint3二、uint6四、float3二、float64
- 類型轉換,type(variable),好比:var a int=8; var b int32=int32(a)
- 邏輯操做符:== 、!=、<、<=、>和 >=
- 數學操做符:+、-、*、/等等
-
- 字符串類型
- 字符類型:var a byte
- 字符串類型: var str string
- 字符串表示兩種方式: 1)雙引號2)``(不須要轉義)
3.2 字符串處理&時間和日期類型&指針類型&流程控制&函數詳解
- strings和strconv使用
- strings.HasPrefix(s string, prefix string) bool:判斷字符串s是否以prefix開頭。
-
- strings.HasSuffix(s string, suffix string) bool:判斷字符串s是否以suffix結尾。
-
- strings.Index(s string, str string) int:判斷str在s中首次出現的位置,若是沒有出現,則返回-1
-
- strings.LastIndex(s string, str string) int:判斷str在s中最後出現的位置,若是沒有出現,則返回-1
-
- strings.Replace(str string, old string, new string, n int):字符串替換
-
- strings.Count(str string, substr string)int:字符串計數
-
- strings.Repeat(str string, count int)string:重複count次str
-
- strings.ToLower(str string)string:轉爲小寫
-
- strings.ToUpper(str string)string:轉爲大寫
- strings.TrimSpace(str string):去掉字符串首尾空白字符
- strings.Trim(str string, cut string):去掉字符串首尾cut字符
- strings.TrimLeft(str string, cut string):去掉字符串首cut字符
- strings.TrimRight(str string, cut string):去掉字符串首cut字符
- strings.Field(str string):返回str空格分隔的全部子串的slice
- strings.Split(str string, split string):返回str split分隔的全部子串的slice
- strings.Join(s1 []string, sep string):用sep把s1中的全部元素連接起來
- strings.Itoa(i int):把一個整數i轉成字符串
- strings.Atoi(str string)(int, error):把一個字符串轉成整數
- Go中的時間和日期類型
-
- time包
-
- time.Time類型,用來表示時間
-
- 獲取當前時間, now := time.Now()
-
- time.Now().Day(),time.Now().Minute(),time.Now().Month(),time.Now().Year()
-
- 格式化,fmt.Printf("%02d/%02d%02d %02d:%02d:%02d", now.Year()…)
-
- time.Duration用來表示納秒
- 指針類型
-
- 普通類型,變量存的就是值,也叫值類型
-
- 獲取變量的地址,用&,好比: var a int, 獲取a的地址:&a
-
- 指針類型,變量存的是一個地址,這個地址存的纔是值
-
- 獲取指針類型所指向的值,使用:,好比:var p int, 使用*p獲取p指向的值
- 流程控制
-
- If / else分支判斷
-
- switch case語句,可使用fallthrough強制執行後面的case代碼
-
- for 語句
-
- for range 語句
-
- break continue語句
-
- goto 和 label 語句 (明天補充)
- 函數詳解
-
- 聲明語法:func 函數名 (參數列表) [(返回值列表)] {}
-
- golang函數特色:
- a. 不支持重載,一個包不能有兩個名字同樣的函數
- b. 函數是一等公民,函數也是一種類型,一個函數能夠賦值給變量
- c. 匿名函數
- d. 多返回值
-
- 函數參數傳遞方式:
- 1). 值傳遞
- 2). 引用傳遞
注意1:不管是值傳遞,仍是引用傳遞,傳遞給函數的都是變量的副本,不過,值傳遞是值的拷貝。引用傳遞是地址的拷貝,通常來講,地址拷貝更爲高效。而值拷貝取決於拷貝的對象大小,對象越大,則性能越低。
注意2:map、slice、chan、指針、interface默認以引用的方式傳遞
-
- _標識符,用來忽略返回值:
-
- 可變參數:func add(a int, b int, arg…int) int {} //兩個或多個參數
- 注意:其中arg是一個slice,咱們能夠經過arg[index]依次訪問全部參數,經過len(arg)來判斷傳遞參數的個數
-
- defer用途:
-
- 當函數返回時,執行defer語句。所以,能夠用來作資源清理
-
- 多個defer語句,按先進後出的方式執行
-
- defer語句中的變量,在defer聲明時就決定了。
-
- defer的用途:關閉文件句柄;釋放資源;關閉數據庫鏈接
3.3 內置函數、遞歸函數、閉包
內置函數
-
- close:主要用來關閉channel
-
- len:用來求長度,好比string、array、slice、map、channel
-
- new:用來分配內存,主要用來分配值類型,好比int、struct。返回的是指針
-
- make:用來分配內存,主要用來分配引用類型,好比chan、map、slice
-
- append:用來追加元素到數組、slice中
-
- panic和recover:用來作不可預料的異常捕獲
-
- new和make的區別
- New與make都是用來分配內存。不一樣點:new用來分配值類型,make用來分配引用類型;new返回的是一個指針,make返回的是值類型。
遞歸函數
-
- 一個函數調用本身,就叫作遞歸。
-
- 斐波那契數
-
- 遞歸的設計原則
- 1)一個大的問題可以分解成類似的小問題
- 2)定義好出口條件
閉包
- 一個函數和與其相關的引用環境而組合成的實體。
3.4 數組和切片、map數據結構
數組
- 數組:是同一種數據類型的固定長度的序列。
- 數組定義:var a [len]int,好比:var a[5]int
- 長度是數組類型的一部分,所以,var a[5] int和var a[10]int是不一樣的類型
- 數組能夠經過下標進行訪問,下標是從0開始,最後一個元素下標是:len-1
- 訪問越界,若是下標在數組合法範圍以外,則觸發訪問越界,會panic
- 數組是值類型,所以改變副本的值,不會改變自己的值
- 數組初始化
切片
- 切片:切片是數組的一個引用,所以切片是引用類型
- 切片的長度能夠改變,所以,切片是一個可變的數組
- 切片遍歷方式和數組同樣,能夠用len()求長度
- cap能夠求出slice最大的容量,0 <= len(slice) <= (array),其中array是slice引用的數組
- 切片的定義:var 變量名 []類型,好比 var str []string var arr []int
Slice的數據結構
Type slice struct {
ptr *[5]type
len int
cap int
}
Cap:不指定的話,默認爲2^0 2^1 2^2 2^n n爲元素的個數
- 經過make來建立切片(另外一種經過數組建立切片)
- var slice []type = make([]type, len)
- slice := make([]type, len)
- slice := make([]type, len, cap)
- 用append內置函數操做切片,當切片容量不夠,會自動擴容,建立一個新的地址。
- For range 遍歷切片
- 切片拷貝
- string與slice
- 排序和查找操做
- sort.Ints對整數進行排序, sort.Strings對字符串進行排序, sort.Float64s對浮點數排序
- sort.SearchInts(a []int, b int) 從數組a中查找b,前提是a必須有序
- sort.SearchFloats(a []float64, b float64) 從數組a中查找b,前提是a必須有序
- sort.SearchStrings(a []string, b string) 從數組a中查找b,前提是a必須有序
map數據結構
- key-value的數據結構,又叫字典或關聯數組
- 聲明 var a map[string]string
- 聲明是不會分配內存的,初始化須要make
- map相關操做
- var a = map[string]string{"key":"value"}
- a = make(map[string]string, 10)
- a[「hello」] = 「world」 //插入或者更新
- Val, ok := a[「hello」] // 查找
- for k, v := range a //遍歷
- delete(a, 「hello」) //刪除
- map是引用類型
- slice of map
- map排序
- a. 先獲取全部key,把key進行排序
- b. 按照排序好的key,進行遍歷
- Map反轉
- a. 初始化另一個map,把key、value互換便可
3.5 package介紹
- golang中的包
- a. golang目前有150個標準的包,覆蓋了幾乎全部的基礎庫
- b. golang.org有全部包的文檔,沒事都翻翻
- 線程同步
- a. import(「sync」)
- b. 互斥鎖, var mu sync.Mutex
- c. 讀寫鎖, var mu sync.RWMutex
- d. 爲何要有鎖:十字路口是公有的資源,紅綠燈就是鎖。爲了不小車爭搶資源就要上鎖。
- 課後做業
實現一個冒泡排序、選擇排序、插入排序、快速排序(參考書籍及搜索引擎) 3.6 結構體&方法&接口
結構體的特色
- 用來自定義複雜數據結構
- struct裏面能夠包含多個字段(屬性)
- struct類型能夠定義方法,注意和函數的區分
- struct類型是值類型
- struct類型能夠嵌套
- Go語言沒有class類型,只有struct類型
結構體的定義以及初始化
- struct 聲明:
- struct 中字段訪問:和其餘語言同樣,使用點
- struct定義的三種形式:
- var stu Student
- var stu *Student = new (Student)
- var stu *Student = &Student{}
- 其中2和3返回的都是指向結構體的指針,訪問形式:a. stu.Name、stu.Age和stu.Score或者 (stu).Name、(stu).Age等
- struct的內存佈局:struct中的全部字段在內存是連續的,佈局以下:
- 鏈表定義
- 雙鏈表定義
- 二叉樹定義
- 若是每一個節點有兩個指針分別用來指向左子樹和右子樹,咱們把這樣的結構叫作二叉樹
type Student struct {
Name string
left* Student
right* Student
}
- 結構體是用戶單獨定義的類型,不能和其餘類型進行強制轉換
- golang中的struct沒有構造函數,通常可使用工廠模式來解決這個問題
type student struct {
Name string
Age int
}
func NewStudent(name string, age int) *student {
return &student{
Name:name,
Age:age,
}
}
func main() {
s := new(student)
a := NewStudent("tony", 23)
s = a
fmt.Println(*s)
fmt.Println(*a)
}
- 再次強調:
- make 用來建立map、slice、channel、interface,new用來建立值類型
- 咱們能夠爲struct中的每一個字段,寫上一個tag。這個tag能夠經過反射的機制獲取到,最經常使用的場景就是json序列化和反序列化
type student struct {
Name stirng "this is name field"
Age int "this is age field"
}
- 結構體中字段能夠沒有名字,即匿名字段
type Train struct {
Car
Start time.Time
int
}
3.7 方法
- Golang中的方法是做用在特定類型的變量上,所以自定義類型,均可以有方法,而不只僅是struct
- 定義:func (recevier type) methodName(參數列表)(返回值列表){}
- 方法的調用
type A struct {
a int
}
func (this A) test() {
fmt.Println(this.a)
}
var t A
t.test()
- 方法和函數的區別
- 函數調用: function(variable, 參數列表)
- 方法調用:variable.function(參數列表)
- 指針receiver vs 值receiver
- 方法的訪問控制,經過大小寫控制
- 繼承
- 若是一個struct嵌套了另外一個匿名結構體,那麼這個結構能夠直接訪問匿名結構體的方法,從而實現了繼承。
- 組合和匿名字段
- 若是一個struct嵌套了另外一個有名結構體,那麼這個模式就叫組合。
- 多重繼承
- 若是一個struct嵌套了多個匿名結構體,那麼這個結構能夠直接訪問多個匿名結構體的方法,從而實現了多重繼承。
- 實現String()
- 若是一個變量實現了String()這個方法,那麼fmt.Println默認會調用這個變量的String()進行輸出。
3.8 接口
接口的定義
- interface類型能夠定義一組方法,可是這些方法不須要實現。而且interface不能包含任何變量。
- 定義接口
type example interface {
Method1(parse1) return1
method2(parse1) return1
}
- interface類型默認是一個指針
- 接口實現
- a. Golang中的接口,不須要顯示的實現。只要一個變量,含有接口類型中的全部方法,那麼這個變量就實現這個接口。所以,golang中沒有implement相似的關鍵字
- b. 若是一個變量含有了多個interface類型的方法,那麼這個變量就實現了多個接口。
- c. 若是一個變量只含有了1個interface的方部分方法,那麼這個變量沒有實現這個接口。
- 多態
一種事物的多種形態,均可以按照統一的接口進行操做
- 接口嵌套
一個接口能夠嵌套在另外的接口
package main
type ReadWrite interface {
Read()
Write()
}
type Lock interface {
Lock()
Unlock()
}
type File interface {
ReadWrite
Lock
Close()
}
8.類型斷言,因爲接口是通常類型,不知道具體類型,若是要轉成具體類型,能夠採用如下方法進行轉換:mysql
var t int
var x interface{}
x = t
y, ok := x.(int) //轉成int
if ok {...}
switch b := x.(type) {
case int:
fmt.Println("int:", b)
case string:
fmt.Println("string", b)
default:
fmt.Println("i don't know")
}
- 空接口 interface{},空接口沒有任何方案,全部類型都實現了空接口,可使用空接口
var a int
var b interface{}
b = a
-
判斷一個變量是否實現了指定接口nginx
- if sv, ok := v.(Stringer); ok {}
- 問題
-
- 指針類型和值類型的區別
-
- 實現一個通用的鏈表類
-
- 實現一個負載均衡調度算法,支持隨機、輪訓等算法
-
- 變量slice和接口slice之間賦值操做,for range???
3.9 反射
- 反射:能夠在運行時動態獲取變量的相關信息Import (「reflect」)
- a. reflect.TypeOf,獲取變量的類型,返回reflect.Type類型
- b. reflect.ValueOf,獲取變量的值,返回reflect.Value類型
- c. reflect.Value.Kind,獲取變量的類別,返回一個常量
- d. reflect.Value.Interface(),轉換成interface{}類型
- 變量 <--> interface <--> Reflect.Value
- 獲取變量的值
- reflect.ValueOf(x).Float()
- reflect.ValueOf(x).Int()
- reflect.ValueOf(x).String()
- reflect.ValueOf(x).Bool()
- 經過反射的來改變變量的值
- reflect.Value.SetXX相關方法,好比:
- reflect.Value.SetFloat(),設置浮點數
- reflect.Value.SetInt(),設置整數
- reflect.Value.SetString(),設置字符串
package main
import (
"fmt"
"reflect"
)
func main() {
var a float64
fv := reflect.ValueOf(&a)
fv.Elem().SetFloat(3.3)
fmt.Printf("%v\n", a)
}
fv.Elem()用來獲取指針指向的變量
- 用反射操做結構體 ???
- a. reflect.Value.NumField()獲取結構體中字段的個數
- b. reflect.Value.Method(n).Call來調用結構體中的方法
3.9 終端以及文件操做
- 終端讀寫
- os.Stdin:標準輸入
- os.Stdout:標準輸出
- os.Stderr:標準錯誤輸出
- 帶緩衝區的讀寫:
- var inputReader *bufio.Reader //聲明一個結構體類型的變量
- inputReader = bufio.NewReader(os.Stdin) //讀取標準輸入
- input, err = inputReader.ReadString('\n') // 讀取換行
- os.File封裝全部文件相關操做,以前講的 os.Stdin,os.Stdout, os.Stderr都是*os.File
- 打開一個文件進行讀操做: os.Open(name string) (*File, error)
- 關閉一個文件:File.Close()
- bufio.NewReader //構造一個reader結構體
- inputReader.ReadString //逐行讀取
- ioutil.ReadFile(inputFile) //讀取整個文件
- ioutil.WriteFile(outputFile,buf,0x644) //寫入整個文件
- gzip.NewReader(fi) //構造一個壓縮文件的reader結構體
- 文件寫入
- os.OpenFile(「output.dat」, os.O_WRONLY|os.O_CREATE, 0666)
- 第二個參數:文件打開模式:
- 第三個參數:權限控制:
- bufio.NewWriter(outputFile) //構建一個寫入文件的writer
- outputWriter.WriteString(outputString) //寫入文件
- outputWriter.Flush() 提交
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
inputFile, err := os.Open("file")
if err != nil {
fmt.Printf("open file err:%v\n", err)
return
}
defer inputFile.Close()
inputReader := bufio.NewReader(inputFile)
for {
inputString, readerError := inputReader.ReadString('\n')
if readerError == io.EOF {
return
}
fmt.Printf("the input was %s", inputString)
}
}
//讀取以及寫入整個文件
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
inputFile := "file"
outputFile := "output_file"
buf, err := ioutil.ReadFile(inputFile)
if err != nil {
fmt.Fprintf(os.Stderr, "File Error:%s\n", err)
return
}
fmt.Printf("%s\n",string(buf))
err = ioutil.WriteFile(outputFile,buf,0x644)
if err != nil {
panic(err.Error())
}
}
//讀取壓縮文件
gzip.NewReader(fi)
//寫入文件
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
outputFile, outputErr := os.OpenFile("new_file", os.O_WRONLY|os.O_CREATE,0666)
if outputErr != nil {
fmt.Printf("An error occurred with file creation\n")
return
}
defer outputFile.Close()
outputWriter := bufio.NewWriter(outputFile)
outputString := "hello world!\n"
for i:= 0;i < 10; i++ {
outputWriter.WriteString(outputString)
}
outputWriter.Flush()
}
- 拷貝文件 io.Copy(dst, src)
- os.Args是一個string的切片,用來存儲全部的命令行參數
- flag包的使用,用來解析命令行參數:
flag.BoolVar(&test, "b", false, "print on newline")
flag.StringVar(&str, "s", "", "print on newline")
flag.IntVar(&count, "c", 1001, "print on newline")
package main
import (
"flag"
"fmt"
)
func main() {
var test bool
var str string
var count int
flag.BoolVar(&test, "b", false, "print on newline")
flag.StringVar(&str, "s", "hello", "print on newline")
flag.IntVar(&count, "c", 1001, "print on newline")
flag.Parse()
fmt.Println(test,str,count)
}
package main
import (
"bufio"
"fmt"
"os"
)
//帶緩衝的區的終端讀寫
func main() {
fmt.Fprintf(os.Stdout,"%s\n","hello world!-unbuffered")
buf := bufio.NewWriter(os.Stdout)
fmt.Fprintf(buf, "%s\n", "hello world!-buffered" )
buf.Flush()
}
4、中階知識
4.1 json數據協議
- golang --> json字符串 --> 網絡傳輸 --> 程序 --> 其餘語言
- 導入包:Import 「encoding/json」
- 序列化: json.Marshal(data interface{}) //json化
- 反序列化: json.UnMarshal(data []byte, v interface{}) //解析json
- json序列化結構體
- json序列化map
4.2 錯誤處理
- 定義錯誤 var errNotFound error = errors.New("Not found error")
- 自定義錯誤
- 如何判斷自定義錯誤
- pannic 這一部分參考俯瞰四維
5、併發編程
6、高階編程
6.1 tcp編程
6.2 redis
redis是個開源的高性能的key-value的內存數據庫,能夠把它當成遠程的數據結構。支持的value類型很是多,好比string、list(鏈表)、set(集合)、hash表等等。redis性能很是高,單機可以達到15w qps,一般適合作緩存。使用第三方開源的redis庫: github.com/garyburd/redigo/redisgit
- 連接redis c, err := redis.Dial("tcp", "localhost:6379")
- Set 接口 _, err = c.Do("Set", "abc", 100) r, err := redis.Int(c.Do("Get", "abc"))
- Hash表 _, err = c.Do("HSet", "books", "abc", 100) r, err := redis.Int(c.Do("HGet", "books", "abc"))
- . 批量Set _, err = c.Do("MSet", "abc", 100, "efg", 300) r, err := redis.Ints(c.Do("MGet", "abc", "efg"))
- 過時時間 _, err = c.Do("expire", "abc", 10)
- 隊列操做 _, err = c.Do("lpush", "book_list", "abc", "ceg", 300) r, err := redis.String(c.Do("lpop", "book_list"))
6.3 http編程
特色
- a. Go原生支持http,import(「net/http」)
- b. Go的http服務性能和nginx比較接近
- c. 幾行代碼就能夠實現一個web服務
- 處理http請求
使用 net/http 包提供的 http.ListenAndServe() 方法,能夠在指定的地址進行監聽, 開啓一個HTTP,服務端該方法的原型:func ListenAndServe(addr string, handler Handler) error <br>
第二個參數表示服務端處理程序, 一般爲空,這意味着服務端調用 http.DefaultServeMux 進行處理,而服務端編寫的業務邏 輯處理程序 http.Handle() 或 http.HandleFunc() 默認注入 http.DefaultServeMux 中
- 處理https請求 func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error
- 路由 http.HandleFunc()方法接受兩個參數
- 第一個參數是HTTP請求的 目標路徑"/hello",該參數值能夠是字符串,也能夠是字符串形式的正則表達式
- 第二個參數指定具體的回調方法,好比helloHandler
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(「hello beifeng!」))
})
- post訪問
resp, err:=http.Get(「.....」)
defer resp.Body.Close()
body,err:=ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
- post訪問
resp, err:=http.Post(「.....」, 」application/x-www-form-urlencoded」, strings.NewReader(「..=...」))
defer resp.Body.Close()
body,err:=ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
6.4 正則表達式
Go語言標準庫內建提供了regexp包github
. 匹配除換行符之外的任意字符
\w 匹配字母或數字或下劃線或漢字
\s 匹配任意的空白符
\d 匹配數字
\b 匹配單詞的開始或結束
^ 匹配字符串的開始
$ 匹配字符串的結束
* 重複零次或更屢次
+ 重複一次或更屢次
? 重複零次或一次
{n} 重複n次
{n,} 重複n次或更屢次
{n,m} 重複n到m次
捕獲 (exp) 匹配exp,並捕獲文本到自動命名的組裏
(?<name>exp) 匹配exp,並捕獲文本到名稱爲name的組裏,也能夠寫成(?'name'exp)
(?:exp) 匹配exp,不捕獲匹配的文本,也不給此分組分配組號
func Match(pattern string, b []byte) (matched bool, err error)
func MatchString(pattern string, s string) (matched bool, err error)
func MustCompile(str string) *Regexp
func (re *Regexp) FindAllString(s string, n int) []string
https://my.oschina.net/kuerant/blog/199146
6.5 mysql編程
-
mysql驅動
https://github.com/Go-SQL-Driver/MySQL golang
- sqlx
- database, err := sqlx.Open("mysql", "root:@tcp(127.0.0.1:3306)/test")
- r, err := Db.Exec("insert into person(username, sex, email)values(?, ?, ?)", "stu001", "man", "stu01@qq.com")
- err := Db.Select(&person, "select user_id, username, sex, email from person where user_id=?", 1)
- _, err := Db.Exec("update person set username=? where user_id=?", "stu0001", 1)
- _, err := Db.Exec("delete from person where user_id=?", 1)
6.6 beego框架
beego.meweb
beego框架之請求數據處理
- ``符號,表示裏面的不用轉義
- 直接解析到struct
- 獲取request body裏的內容
-
- 在配置文件裏設置 copyrequestbody = true
-
- this.Ctx.Input.RequestBody