iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 22
2
Software Development

Go繁不及備載系列 第 22

# Day22 Golang 網頁框架 gin 實作小專案 (RESTful API)

Day22 Golang 網頁框架 gin 實作小專案 (RESTful API)

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敘述,
只有在操作失敗時才會出現訊息,以提示使用者操作為何失敗。
裡面的邏輯及變數也有些冗餘,我們接著修改第二版。

賦予API返回值的意義

與此同時,根據以下發送的參數,在操作成功後,
http://127.0.0.1/deposit/100
我們就可以從中取得到兩個資訊,

  1. 這是儲值 操作
  2. 並且此筆儲值金額為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{}

改動後的程式碼

導入回傳值的樣板 wrapResponse

在設計較大型的專案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 API

Restful(Representational State Transfer) API是一種設計的概念,
其中理念是以資源對應的方式為主,讓每個資源對應Server上的一個URI(以路徑識別資源位置)。
不是硬性的特定規範,也沒有明確定義要如何實作,如何達成Restful全賴個人的設計。

設計時的大方向:

  • 必要的參數以路徑參數為主,選填的參數以查詢參數為主
  • 靈活運用Method,減少動詞的使用

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方法)

上一篇
# Day21 Golang 網頁框架 gin 使用教學(GET、POST、ANY)
下一篇
# Day23 Golang 爬蟲框架 colly 摳哩
系列文
Go繁不及備載35

尚未有邦友留言

立即登入留言