
掌握API建模:基本概念和實踐
項目名稱你可以取一個你喜歡的名字,比如 golang-api,然后 Next:
接著直接點擊 Create Project 即可:
然后,點擊 Build a Database:
選擇免費的 Shared:
點擊 Create Cluster 創建集群,需要等一些時間。(其他用默認即可)
接著需要創建能夠訪問該數據庫的用戶。點擊 Create User,如果需要保證安全性,可以限制 IP。然后點擊 Finish and Close 即可。
你也可以不限制 IP,畢竟我們只是練習、測試項目。(如果你下面運行程序,發現連不上 MongoDB,就考慮把 IP 限制直接去掉)
保存后,可以看到 Database Deployments 界面:
在 GitHub 創建項目:fibermongo,clone 到本地,然后初始化:
$ go mod init github.com/programmerug/fibermongo
go: creating new go.mod: module github.com/programmerug/fibermongo
安裝以下兩個依賴:
$ go get -v github.com/gofiber/fiber/v2
$ go get -v go.mongodb.org/mongo-driver/mongo
接著在目錄下創建 main.go,輸入如下內容:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(&fiber.Map{"welcome": "Hello from Fiber + MongoDB"})
})
app.Listen(":2022")
}
啟動 go run main.go,然后通過 curlie[1] 測試(這是 Go 實現的類似 curl 工具,易用性類似 httpie):
$ curlie localhost:2022
HTTP/1.1 200 OK
Date: Mon, 07 Feb 2022 09:05:48 GMT
Content-Type: application/json
Content-Length: 40
{
"welcome": "Hello from Fiber + MongoDB"
}
創建如下目錄,注意其中的注釋:
.
├── config 用于項目配置文件
├── controller 用于應用程序邏輯
├── go.mod
├── go.sum
├── main.go
├── model 用于數據和數據庫邏輯
├── response 用于描述我們希望 API 給出的響應的文件
└── route 用于 URL 模式和處理程序信息
為了讓我們的程序連接到數據庫 MongoDB,跟 MySQL 等類似,需要連接字符串。
在上面 MongoDB 的 Database Deployments 頁面,點擊 Connect 按鈕:
接著點擊 Connect your application,然后驅動選擇 Go,及其版本。
然后復制:
在項目根目錄創建 .env
文件,增加 MONGOURI:
MONGOURI=mongodb+srv://<user>:<password>@cluster0.k0oen.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
注意替換其中的 user 和 password 為你開頭設置的。
接著需要寫代碼讀取環境變量。因為環境變量通過 key=value 的簡單形式保存在文件,因此我們使用一個對應的第三方庫:github.com/joho/godotenv。
在 config 目錄中增加 env.go 文件,輸入如下內容:
package config
import (
"log"
"os"
"github.com/joho/godotenv"
)
func EnvMongoURI() string {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
return os.Getenv("MONGOURI")
}
然后執行 go mod tidy,更新依賴。(這個命令可以多次執行)
接著在 config 目錄下創建 setup.go 文件,增加如下內容:
package config
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func ConnectDB() *mongo.Client {
client, err := mongo.NewClient(options.Client().ApplyURI(EnvMongoURI()))
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
err = client.Connect(ctx)
if err != nil {
log.Fatal(err)
}
// ping the database
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB")
return client
}
// MongoDB 客戶端實例
var DB *mongo.Client = ConnectDB()
// GetCollection 獲得數據庫集合
func GetCollection(client *mongo.Client, collectionName string) *mongo.Collection {
collection := client.Database("golangAPI").Collection(collectionName)
return collection
}
以上代碼簡單解釋下:
ConnectDB
函數,首先將客戶端配置為使用正確的 URI 并檢查錯誤。其次,我們定義了嘗試連接時要使用的 10 秒超時。第三,檢查連接數據庫時是否出錯,如果連接時間超過10秒,則取消連接。最后,我們 ping 數據庫以測試我們的連接并返回client
實例。ConnectDB
創建一個DB
變量實例。這在創建集合時會派上用場。GetCollection
函數獲取 collections
。以上的設計并不是最好的,主要讓大家熟悉操作 MongoDB 的 API。
Route Handler
完成后,我們需要在 route 文件夾中創建一個 user_route.go 文件,管理我們應用程序中所有與用戶相關的路由,如下所示:
package routes
import "github.com/gofiber/fiber/v2"
func UserRoute(app *fiber.App) {
// All routes related to users comes here
}
接下來,修改 main.go,路由交給上面的函數處理:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/programmerug/fibermongo/route"
)
func main() {
app := fiber.New()
// routes
route.UserRoute(app)
app.Listen(":2022")
}
響應類型
接下來,我們需要創建一個可重用的 struct
的來描述我們 API 的響應。為此,在 response
文件夾創建一個user_response.go
文件并添加以下內容:
package response
import "github.com/gofiber/fiber/v2"
type UserResponse struct {
Status int json:"status"
Message string json:"message"
Data interface{} json:"data"
}
上面的代碼片段創建了一個通用的響應類型:UserResponse
。
接下來,我們需要一個 model 來表示我們的應用程序數據。
為此,我們在 model 中創建 user_model.go 文件并輸入如下內容:
package model
import "go.mongodb.org/mongo-driver/bson/primitive"
type User struct {
ID primitive.ObjectID json:"id,omitempty"
Name string json:"name,omitempty" validate:"required"
Location string json:"location,omitempty" validate:"required"
Title string json:"title,omitempty" validate:"required"
}
要解釋的是上面的 tag:validate,這是 github.com/go-playground/validator/v10 庫。
有了 model,我們現在可以創建一個函數來創建用戶。
在 controller
文件夾下創建文件 user_controller.go
并添加以下內容:
package controller
import (
"context"
"net/http"
"time"
"github.com/programmerug/fibermongo/config"
"github.com/programmerug/fibermongo/model"
"github.com/programmerug/fibermongo/response"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var userCollection *mongo.Collection = config.GetCollection(config.DB, "user")
var validate = validator.New()
func CreateUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var user model.User
defer cancel()
// validate the request body
if err := c.BodyParser(&user); err != nil {
return c.Status(http.StatusBadRequest).JSON(response.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
// use the validator library to validate required fields
if validationErr := validate.Struct(&user); validationErr != nil {
return c.Status(http.StatusBadRequest).JSON(response.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": validationErr.Error()}})
}
newUser := model.User{
ID: primitive.NewObjectID(),
Name: user.Name,
Location: user.Location,
Title: user.Title,
}
result, err := userCollection.InsertOne(ctx, newUser)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(response.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: err.Error()})
}
return c.Status(http.StatusCreated).JSON(response.UserResponse{Status: http.StatusCreated, Message: "success", Data: result})
}
這段代碼比較多,簡單解釋下:
UserResponse
返回了適當的消息和狀態代碼。其次,我們創建了一個newUser
變量,使用userCollection.InsertOne
函數將其插入并檢查是否有錯誤。最后,如果插入成功,我們會返回正確的響應。接著在 user_route.go 文件中綁定 API URL 和對應的 handler:
package route
import (
"github.com/gofiber/fiber/v2"
"github.com/programmerug/fibermongo/controller"
)
func UserRoute(app *fiber.App) {
app.Post("/user", controller.CreateUser)
}
go run main.go 啟動程序,不出意外會看到如下輸出:
$ go run main.go
Connected to MongoDB
┌───────────────────────────────────────────────────┐
│ Fiber v2.26.0 │
│ http://127.0.0.1:2022 │
│ (bound on host 0.0.0.0 and port 2022) │
│ │
│ Handlers ............. 1 Processes ........... 1 │
│ Prefork ....... Disabled PID .............. 7158 │
└───────────────────────────────────────────────────┘
然后通過 curlie 測試創建用戶:
$ curlie POST -d '{
"name": "幽鬼",
"location": "北京",
"title": "Software Engineer"
}' http://localhost:2022/user
會看到如下輸出:
HTTP/1.1 201 Created
Date: Tue, 08 Feb 2022 03:08:17 GMT
Content-Type: application/json
Content-Length: 92
{
"status": 201,
"message": "success",
"data": {
"InsertedID": "6201de338ecf6d62fb799d62"
}
}
然后你可以到 MongoDB 確認,數據有沒有寫入:
接下來就是實現查找、更新和刪除。這塊內容不一一細講,只列出關鍵代碼。
先看完整的路由:
package route
import (
"github.com/gofiber/fiber/v2"
"github.com/programmerug/fibermongo/controller"
)
func UserRoute(app *fiber.App) {
app.Post("/user", controller.CreateUser)
app.Get("/user/:userId", controller.GetAUser)
app.Put("/user/:userId", controller.EditAUser)
app.Delete("/user/:userId", controller.DeleteAUser)
app.Get("/users", controller.GetAllUsers)
}
通過 userId 獲取用戶信息中,因為 userId 是字符串,需要轉為 BSON 的 objectID。MongoDB 庫有一個專門的函數:
objId, _ := primitive.ObjectIDFromHex(userId)
具體的操作 MongoDB 的 API 可以查閱文檔:https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo。
本文主要在于帶著大家完成一個基本的 CRUD 工作,讓大家熟悉 Fiber + MongoDB 構建 RESTful API,熟悉工作流程。同時了解相關庫的使用。
本文的完整代碼見:https://github.com/programmerug/fibermongo。
參考文章:https://dev.to/hackmamba/build-a-rest-api-with-golang-and-mongodb-fiber-version-4la0
此外,測試 API 時,大家可以使用自己喜歡的工具,比如 postman。
[1]curlie:?https://curlie.io/
文章轉自微信公眾號@幽鬼