import json
import requests
if __name__ == "__main__":
data = {"apitype": "getData", "params": {}}
res = requests.post("http://localhost:8080/test", data=json.dumps(data))
for text in json.loads(res.text):
print(text['Text'])
挾帶參數為apitype這參數是為了讓server端在解析JSON之後知道要調用哪個函式為調用者提供服務。則params為參數。這個格式就是依照server端去做的。一般來說只要不參數不合法server也不會給出正確的響應。因為是在本地端運行所以url參數就是local的默認8080。路由是server端定義的這裡我定義為/test。至於data的傳送形式也是看server的設定有些吃JSON有些吃別的...不一定之後沒響應或者報錯就知道傳錯了。當然這些功能也可以靠postman這應用程式來實現。會比直接寫python好用。
下圖為,golangApi的結構
可以知道有main也就是入口,route,Gin也是在這裡new的。執行了也就會開始監聽。則go.mod跟go.sum是執行了
go mod init projectName
的產物。它主要是包管理。如果不做這個的話。main將無法調用ctrl跟DB裡面的func。
接下來直接看main.go
package main
import (
"github.com/gin-gonic/gin"
"goTest/ctrl"
)
func main() {
mainRoute := gin.Default()
mainRoute.POST("/test", ctrl.ApiIndex)
mainRoute.Run()
}
原本想是建立一個資料夾管理route的gin也會在那裡new。main就只要import就會開始監聽了。但都先寫到main.go裡面,再來就是調用gin的post了,傳參是“路由”跟func。func就是調用ctrl包裡的ApiIndex所以接下來看ctrl.go。
package ctrl
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson"
MongoDB "goTest/DB/mongoDB"
)
//new一個字定義的 mongo實例
var mongo = MongoDB.New("")
//map映射
var apiIndexCallBackMap = map[string]func(*gin.Context, map[string]interface{}){
"getData": getData,
}
func ApiIndex(pCont *gin.Context) {
// 從 body 獲取資料
pBodyData, pErr := pCont.GetRawData()
// 如果出錯
if pErr != nil {
fmt.Println(pErr)
return
}
var pParseData map[string]interface{}
// json 結構解析
pErr = json.Unmarshal(pBodyData, &pParseData)
if pErr != nil {
fmt.Println(pErr)
return
}
resFun, _ := apiIndexCallBackMap[pParseData["apitype"].(string)]
resFun(pCont, pParseData["params"].(map[string]interface{}))
}
type LogInfo struct {
ID interface{} `bson:"_id,omitempty"`
Text string `bson:"text,omitempty"`
}
func getData(pCont *gin.Context, params map[string]interface{}) {
var pTmpData = new(LogInfo)
var dataSli = make([]LogInfo, 0)
operations := []bson.M{
//可以加些複合查詢條件
//{"$match":
// bson.M{"field": value}
//},
}
_ = mongo.GetMany("Data", operations, pTmpData, func() {
dataSli = append(dataSli, *pTmpData)
})
pCont.JSON(200, dataSli)
}
調用ApiIndex的時候就會解析post裡面的data了。透過GetRawData這方法來獲取。json.Unmarshal將傳過來的data用json來解析。將會得到事前定義好的apitype在透過map去做映射。簡單的說透過apitype他可以映射一個func回來在最後讓ApiIndex執行。本次apitype參數為getData映射到func getData。所以在把參數跟gin的Context傳給func。
在這裡mongo-driver撈取mongodb方式比較特別。也別於python跟php...它要求取出的資料型態要事先定義,並且new一個切片讓它可以存放它取出的資料。python就直接給他個『等號』在把變數印出來看看就行了。我也不知道為啥他的設計要那麼機車。如果資料型態沒有正確則無法存取。他會返回空。以本次為例我要取objID跟text就要事先定義結構體LogInfo(後面bson也要寫對)。pTmpData要丟進函數讓GetMany迭代的它吃的是位子。dataSli則是切片要拿來裝的用append這有點像python的list。
mongodb.go
package MongoDB
import (
"context"
"fmt"
"sync"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
//-----------------------------------------------------------------------------
// 資料結構
//-----------------------------------------------------------------------------
// MongoDBType : MongoDB 主結構
type MongoDBType struct {
sync.Mutex // 繼承
sURLBase string // 資料庫基本 sURLBase
sDBName string // 資料庫名稱
// 連線物件
pDBConn *mongo.Client
pDBConnDB *mongo.Database
// control flag
bIsLink bool
}
//-----------------------------------------------------------------------------
// 變數
//-----------------------------------------------------------------------------
var pMongoDBObjs []*MongoDBType
// ----------------------------------------------------------------------------
// 函式
// ----------------------------------------------------------------------------
func init() {
fmt.Println("init function was called.")
pMongoDBObjs = make([]*MongoDBType, 0)
}
func New(pName string) *MongoDBType {
var pErr error
pMongoDB := new(MongoDBType)
//設定本連接本地端
pMongoDB.sURLBase = "mongodb://127.0.0.1:27017/"
//DB資料庫名稱
pMongoDB.sDBName = "goGinDB"
pMongoDB.pDBConn, pErr = mongo.NewClient(options.Client().ApplyURI(pMongoDB.sURLBase))
if pErr != nil {
fmt.Println(pErr)
pMongoDB = nil
return pMongoDB
}
pMongoDBObjs = append(pMongoDBObjs, pMongoDB)
return pMongoDB
}
// IsAlive :
func (pObj *MongoDBType) IsAlive() bool {
var pErr error
var times int
pContext, pContextCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer pContextCancel()
//嘗試兩次連線
for times = 0; times < 2; times++ {
pObj.Lock()
if !pObj.bIsLink {
pErr = pObj.pDBConn.Connect(pContext)
if pErr != nil {
pObj.pDBConnDB = nil
pObj.Unlock()
return false
}
pObj.pDBConnDB = pObj.pDBConn.Database(pObj.sDBName)
pObj.bIsLink = true
}
// ping
pErr = pObj.pDBConn.Ping(pContext, nil)
if pErr != nil {
pObj.pDBConnDB = nil
pErr = pObj.pDBConn.Disconnect(pContext)
if pErr != nil {
fmt.Println(pErr)
}
pObj.Unlock()
pObj.bIsLink = false
continue
}
pObj.Unlock()
break
}
return pObj.bIsLink
}
// GetMany :
func (pObj *MongoDBType) GetMany(sTableName string, pFilter interface{}, pTmpData interface{}, pAppend func()) bool {
var pErr error
if !pObj.IsAlive() {
fmt.Println("Can't connect to MongoDB server.")
return false
}
pContext, pContextCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer pContextCancel()
fmt.Println("<<Get>> Table :", sTableName, "Filter : ", pFilter)
pObj.Lock()
pCollection := pObj.pDBConnDB.Collection(sTableName)
if pCollection == nil {
fmt.Println("Can't get the", sTableName)
pObj.Unlock()
return false
}
pCursor, pErr := pCollection.Aggregate(pContext, pFilter)
if pErr != nil {
fmt.Println(pErr)
pObj.Unlock()
return false
}
for pCursor.Next(pContext) {
// create a value into which the single document can be decoded
pErr = pCursor.Decode(pTmpData)
if pErr != nil {
fmt.Println(pErr)
pObj.Unlock()
return false
}
pAppend()
}
pErr = pCursor.Err()
if pErr != nil {
fmt.Println(pErr)
pObj.Unlock()
return false
}
// Close the cursor once finished
err := pCursor.Close(pContext)
if err != nil {
return false
}
pObj.Unlock()
return true
}
在GetMany方法中需要參數有左至右為表名,在ctrl.go寫的bson.M條件式,還有切片指針跟執行func。則傳入func最後會在pAppend那行執行。前面還很神奇的有指針參數pObj就是剛剛new出來的mongo是同個。mongo連接部分會在IsAlive。pContext那塊是網路上找到的寫法。時間到狀態就會更改。上面只是連接而已下面是要查詢所以時間上的設定也有所不同一個十秒一個三十秒。接下來就是用mongo包提供的函式處理資料了。最後把是否查詢成功回傳給ctrl.go。
最後再去mongoDB確認想撈出來的資料跟返回的資料有無一致。
恩。沒有問題。以上。很多地方是參考網路文章。拼揍而成的。一定有不足之處。還請大家多多指教囉。
代碼git:
git clone https://github.com/yusei27017/golangApiTest.git