gin的第三天,千杯不醉。
今天的目標是要使用gin來製作一個可以提款、存款以及查詢餘額功能的個人小銀行。
一步一步來看要怎麼實作:
先來實作查詢功能
package main
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
var balance = 1000
func main() {
router := gin.Default()
router.GET("/balance/", getBalance)
router.Run(":80")
}
func getBalance(context *gin.Context) {
var msg = "您的帳戶內有:" + strconv.Itoa(balance) + "元"
context.JSON(http.StatusOK, gin.H{
"amount": balance,
"status": "ok",
"message": msg,
})
}
緊接著來做儲值以及提款功能
func deposit(context *gin.Context) {
var status string
var msg string
input := context.Param("input")
amount, err := strconv.Atoi(input)
if err == nil {
if amount <= 0 {
amount = 0
status = "failed"
msg = "操作失敗,存款金額需大於0元!"
} else {
balance += amount
status = "ok"
msg = "已成功存款" + strconv.Itoa(amount) + "元"
}
} else {
amount = 0
status = "failed"
msg = "操作失敗,輸入有誤!"
}
context.JSON(http.StatusOK, gin.H{
"amount": amount,
"status": status,
"message": msg,
})
}
加值時需要判斷使用者填入的數字是不是正整數,而不是負數或其他亂填的阿雜符號。
func withdraw(context *gin.Context) {
var status string
var msg string
input := context.Param("input")
amount, err := strconv.Atoi(input)
if err == nil {
if amount <= 0 {
amount = 0
status = "failed"
msg = "操作失敗,提款金額需大於0元!"
} else {
if balance-amount < 0 {
amount = 0
status = "failed"
msg = "操作失敗,餘額不足!"
} else {
balance -= amount
status = "ok"
msg = "成功提款" + strconv.Itoa(amount) + "元"
}
}
} else {
amount = 0
status = "failed"
msg = "操作失敗,輸入有誤!"
}
context.JSON(http.StatusOK, gin.H{
"amount": amount,
"status": status,
"message": msg,
})
}
不過這個第一版的個人小銀行,還不夠格成為一個夠成熟的API接口,還有些地方得改善,
像是在 操作成功時 通常不會出現message
敘述,
只有在操作失敗時才會出現訊息,以提示使用者操作為何失敗。
裡面的邏輯及變數也有些冗餘,我們接著修改第二版。
與此同時,根據以下發送的參數,在操作成功後,
http://127.0.0.1/deposit/100
我們就可以從中取得到兩個資訊,
儲值
操作此筆儲值金額為100
相同地,提款也是如此
http://127.0.0.1/withdraw/10
考量到API的設計理念,
盡可能讓每個回傳參數攜帶最多的資訊、發揮最大的意義及功效。
我們可以這樣子改動 API返回的金額:
儲值多少錢
=> 儲值後用戶餘額有多少
提款多少錢
=> 提款後用戶餘額剩多少
同時也引入struct
作為 gin Context回傳的json結構。
type Result struct {
Amount int `json:"amount"`
Status string `json:"status"`
Message string `json:"message"`
}
var result = Result{}
在設計較大型的專案API時,為了讓每個回傳的json格式、型別都一致,
(如果在接同個專案的不同接口時,API接口格式給的不一致,那麼處理上會Hen麻煩)
此時可另外設計一個wrapResponse function
,
不論程式有沒有出現錯誤,都可以將gin.Context
作為參數傳遞給wrapResponse
,
把所要回傳的值、型別、甚至err
都集結起來,統一一個介面
來作回傳。
如此一來也能更精簡化程式碼。
func wrapResponse(context *gin.Context, amount int, err error) {
var r = struct {
Amount int `json:"amount"`
Status string `json:"status"`
Message string `json:"message"`
}{
Amount: amount,
Status: "ok", // 預設狀態為ok
Message: "",
}
if err != nil {
r.Amount = 0
r.Status = "failed" // 若出現任何err,狀態改為failed
r.Message = err.Error() // Message回傳錯誤訊息
}
context.JSON(http.StatusOK, r)
}
Restful(Representational State Transfer) API是一種設計的概念,
其中理念是以資源對應的方式為主,讓每個資源對應Server上的一個URI(以路徑識別資源位置)。
不是硬性的特定規範,也沒有明確定義要如何實作,如何達成Restful全賴個人的設計。
設計時的大方向:
GET is used to request data from a specified resource.
PUT/POST is used to send data to a server to create/update a resource.
The DELETE method deletes the specified resource.
以訊息為例,原先查詢訊息、發布訊息、刪除訊息 3支動作的API,
可如下修改,將其命名為相同的func
:
getMsg => msg (GET方法)
createMsg => msg (POST方法)
deleteMsg => msg (DELETE方法)