golang簡易API(使用Gin)

Avatar of 林藤恩.
Avatar of 林藤恩.

golang簡易API(使用Gin)

後端工程師
Taoyuan City, Taiwan

golang簡易API(使用Gin)

golangApi。主要是接收post請求然後透過control調用db板塊在db中查詢所要的資料。

圖為main.go在運行中。我們可以看到log表示響應代碼為200,方法為Post,以及正在監聽localhost:8080端口

還有我自己用fmt.print出的log其中有apitype跟params是決定要調用的api以及他挾帶的傳參。這兩個直都可以自訂義。還有路由設定為/test。


先從簡單的client端開始看起

以下為python的 requests包中的post方法發起請求

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
使用gin框架監聽,使用http協議,提供接口給client端調用。
Avatar of the user.
Please login to comment.

Published: Jun 10th 2022
95
8
0

Tools

mongodb
MongoDB
npm
npm
nginx
nginx
go
Go

npm
nginx
mongodb
golang-Gin
Gin
GO

Share