Golang 開發 REST 風格的 API 實戰
【導讀】本文用詳細例子介紹了 Rest API 的項目實現。
⒈ 從 0 開始
在實現動態 API 之前,先來實現一個簡單的靜態頁面渲染
package main
import (
"fmt"
"log"
"net/http"
)
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "HomePage")
fmt.Println("加載 HomePage 完成")
}
func handleRequests() {
http.HandleFunc("/", homePage)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func main() {
handleRequests()
}
其中,函數 homePage
負責渲染靜態頁面,而 handleRequests
負責將所有對 URL 根路徑的請求路由到 homePage
。
⒉ 路由
在實際應用中,同一服務中會包含多個 API,針對不同的功能進行響應,所以必須用到路由模塊。這裏我們使用第三方包 gorilla/mux
來實現路由功能。
func handleRequests() {
myRouter := mux.NewRouter()
myRouter.HandleFunc("/", homePage)
// 列表
myRouter.HandleFunc("/all", listAdx)
// 詳情
myRouter.HandleFunc("/adx/{id}", detailAdx).Methods("GET")
// 新建
myRouter.HandleFunc("/adx", createAdx).Methods("POST")
// 編輯
myRouter.HandleFunc("/adx/{id}", updateAdx).Methods("POST")
// 刪除
myRouter.HandleFunc("/adx/{id}", deleteAdx). Methods("DELETE")
log.Fatal(http.ListenAndServe(":8080", myRouter))
}
解析路由中的參數
通常在一些特定功能的 API 中,都會通過路由傳參(如上例中的詳情 API),此時我們首先需要從 API 中解析參數。gorilla/mux
中爲我們提供瞭解析路由方式傳參的方法
vars := mux.Vars(r)
id := vars["id"]
⒊ ORM
golang 與數據庫交互常用的第三方包爲 go-sql-driver
,但這裏我們使用另一個第三方包gorm
。gorm
的使用方法與 PHP 中的 ORM 的使用方法非常相似,既可以運行原生的 SQL 語句,還可以進行鏈式調用。
- 定義結構體
package main
type Adx struct {
Id int `json:"id,omitempty"`
Name string `json:"name,omitempty"`
}
- 初始化數據庫連接(數據庫連接信息配置在 .env 文件中,這裏使用 dotenv 來讀取配置信息,所以需要引入
autoload
)
package main
import (
"fmt"
_ "github.com/joho/godotenv/autoload"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
"os"
)
var (
host string
port string
dbName string
username string
password string
timeout string
DB *gorm.DB
)
func init(){
host := os.Getenv("HOST")
port := os.Getenv("PORT")
dbName := os.Getenv("DATABASE")
username := os.Getenv("USERNAME")
password := os.Getenv("PASSWORD")
timeout := os.Getenv("TIMEOUT")
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?timeout=%s&charset=utf8mb4&parseTime=True&loc=Local", username, password, host, port, dbName, timeout)
fmt.Println(dsn)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err.Error())
}
DB = db
}
- 執行數據的 CURD 操作
package main
import (
"encoding/json"
"fmt"
"github.com/gorilla/mux"
"io/ioutil"
"net/http"
"strconv"
)
func listAdx(w http.ResponseWriter, r *http.Request) {
var records []Adx
DB.Table("adx").
Scan(&records)
json.NewEncoder(w).Encode(records)
}
func detailAdx(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
var adx Adx
DB.Table("adx").
Where("id = ?", id).
First(&adx)
json.NewEncoder(w).Encode(adx)
}
func createAdx(w http.ResponseWriter, r *http.Request) {
var adxs []Adx
request, _ := ioutil.ReadAll(r.Body)
json.Unmarshal(request, &adxs)
fmt.Printf("%+v\n", adxs)
DB.Table("adx").
Create(&adxs)
json.NewEncoder(w).Encode(adxs)
}
func updateAdx(w http.ResponseWriter, r *http.Request) {
var adx Adx
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
fmt.Printf("id = %d\n", id)
request,_ := ioutil.ReadAll(r.Body)
json.Unmarshal(request, &adx)
fmt.Printf("%+v\n", adx)
DB.Table("adx").
Where("id = ?", id).
Updates(adx)
if DB.Error != nil {
json.NewEncoder(w).Encode(DB.Error)
} else {
adx.Id = id
json.NewEncoder(w).Encode(adx)
}
}
func deleteAdx(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
// 判斷記錄是否存在
var count int64
DB.Table("adx").
Where("id = ?", id).
Count(&count)
if count == 0 {
json.NewEncoder(w).Encode("記錄不存在")
return
}
DB.Table("adx").
Delete(&Adx{}, id)
if DB.Error != nil {
json.NewEncoder(w).Encode(DB.Error)
} else {
json.NewEncoder(w).Encode("操作成功")
}
}
上述代碼中,路由傳參通過 gorilla/mux
包中的方法解析,但 POST 傳參則需要通過 ioutil
和 json
來解析。
⒋ API 跨域
在目前 web 應用前後端分離的背景下,要求後端 API 支持跨域。在 go 語言開發的 API 中要實現跨域,仍然需要藉助第三方包,這裏使用 github.com/rs/cors
。
package main
import (
"github.com/gorilla/mux"
"github.com/rs/cors"
"log"
"net/http"
)
func handleRequests() {
myRouter := mux.NewRouter()
myRouter.HandleFunc("/", homePage)
// 列表
myRouter.HandleFunc("/all", listAdx)
// 詳情
myRouter.HandleFunc("/adx/{id}", detailAdx).Methods("GET")
// 新建
myRouter.HandleFunc("/adx", createAdx).Methods("POST")
// 編輯
myRouter.HandleFunc("/adx/{id}", updateAdx).Methods("POST")
// 刪除
myRouter.HandleFunc("/adx/{id}", deleteAdx). Methods("DELETE")
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
})
handler := c.Handler(myRouter)
log.Fatal(http.ListenAndServe(":8080", handler))
}
在實際應用中,爲了安全性考慮,AllowedOrigins
通常設置爲實際使用的域名,這裏只是作爲 DEMO 演示,所以設置成了 *
。
轉自:
juejin.cn/post/6945246548208910344
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/vzdeONeqKRO5dZ4yEbhxkA