在本教程中,我们将创建一个 Movie Catchphrase API,允许您创建、读取、更新和删除 Catchphrases,或者简而言之,执行 CRUD 操作。

我们将使用 Fiber(一个 Go Web 框架)和 go mongo 驱动程序来与 MongoDB 实例交互。

对于这个项目,我假设您已经将 go 安装到您的计算机上。您可以通过在终端中运行以下命令来验证安装:

go

进入全屏模式 退出全屏模式

如果还没有安装,可以到golang 官网下载必要的安装程序。

MongoDB 设置

对于这个项目,我假设您已经设置了一个 MongoDB 集群(或本地 MongoDB 安装)并拥有连接 URI。如果没有,您可以参考以下链接获取安装指南:MongoDB cluster或MongoDB local

项目设置

我们需要做的第一件事是通过为项目创建一个文件夹来设置项目。在该文件夹中,我们将使用 go mod 初始化项目并安装我们将要使用的包。运行以下命令来设置项目:

go mod init
go get -u github.com/gofiber/fiber/v2
go get go.mongodb.org/mongo-driver/mongo
go get github.com/joho/godotenv

进入全屏模式 退出全屏模式

godotenv将允许我们从.env文件中提取环境变量。在项目根目录下创建.env文件,添加以下内容:

MONGO_URI=Your_MongoDB_URI_comes_here
DB=Your_DB_name_comes_here
PORT=3000
APP_ENV=development

进入全屏模式 退出全屏模式

接下来,让我们在项目的根目录下创建一个.gitignore文件,并添加以下内容:

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

.env

进入全屏模式 退出全屏模式

开始构建API

让我们在项目的根目录下创建一个main.go文件。这将包含带有基本路由的基本服务器设置。将以下内容添加到文件中:

package main

import "github.com/gofiber/fiber/v2"

func main() {
    app := fiber.New()

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello, World 👋!")
    })

    app.Listen(":3000")
}

进入全屏模式 退出全屏模式

为了启动应用程序,运行以下命令:

go run main.go

进入全屏模式 退出全屏模式

在浏览器中导航到localhost:3000以查看应用程序。

配置并连接到数据库

始终将应用程序的所有配置保存在单独的文件夹中。让我们在应用程序的根文件夹中创建一个新文件夹config来保存所有配置。

在 config 文件夹中创建一个新文件db.go,其内容如下:

package config

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"

    "github.com/joho/godotenv"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/mongo/readpref"
)

type MongoInstance struct {
    Client *mongo.Client
    DB     *mongo.Database
}

var MI MongoInstance

func ConnectDB() {
    if os.Getenv("APP_ENV") != "production" {
        err := godotenv.Load()
        if err != nil {
            log.Fatal("Error loading .env file")
        }
    }

    client, err := mongo.NewClient(options.Client().ApplyURI(os.Getenv("MONGO_URI")))
    if err != nil {
        log.Fatal(err)
    }

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    err = client.Connect(ctx)
    if err != nil {
        log.Fatal(err)
    }

    err = client.Ping(ctx, readpref.Primary())
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Database connected!")

    MI = MongoInstance{
        Client: client,
        DB:     client.Database(os.Getenv("DB")),
    }
}

进入全屏模式 退出全屏模式

我们将使用 MI 对象来查询集合。

创建流行语模型

让我们在应用程序的根文件夹中创建一个新文件夹models来保存所有模型。

在模型文件夹中创建一个新文件Catchphrase.go,其内容如下:

package models

import (
    "go.mongodb.org/mongo-driver/bson/primitive"
)

type Catchphrase struct {
    ID           primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
    MovieName    string             `json:"movieName,omitempty" bson:"movieName,omitempty"`
    Catchphrase  string             `json:"catchphrase,omitempty" bson:"catchphrase,omitempty"`
    MovieContext string             `json:"movieContext,omitempty" bson:"movieContext,omitempty"`
}

进入全屏模式 退出全屏模式

创建流行语控制器

让我们在应用程序的根文件夹中创建一个新文件夹controllers来保存所有控制器。

在 controllers 文件夹中创建一个新文件catchphraseController.go,其内容如下:

package controllers

import (
    "context"
    "log"
    "math"
    "strconv"
    "time"

    "github.com/gofiber/fiber/v2"
    "github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/config"
    "github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/models"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo/options"
)

func GetAllCatchphrases(c *fiber.Ctx) error {
    catchphraseCollection := config.MI.DB.Collection("catchphrases")
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)

    var catchphrases []models.Catchphrase

    filter := bson.M{}
    findOptions := options.Find()

    if s := c.Query("s"); s != "" {
        filter = bson.M{
            "$or": []bson.M{
                {
                    "movieName": bson.M{
                        "$regex": primitive.Regex{
                            Pattern: s,
                            Options: "i",
                        },
                    },
                },
                {
                    "catchphrase": bson.M{
                        "$regex": primitive.Regex{
                            Pattern: s,
                            Options: "i",
                        },
                    },
                },
            },
        }
    }

    page, _ := strconv.Atoi(c.Query("page", "1"))
    limitVal, _ := strconv.Atoi(c.Query("limit", "10"))
    var limit int64 = int64(limitVal)

    total, _ := catchphraseCollection.CountDocuments(ctx, filter)

    findOptions.SetSkip((int64(page) - 1) * limit)
    findOptions.SetLimit(limit)

    cursor, err := catchphraseCollection.Find(ctx, filter, findOptions)
    defer cursor.Close(ctx)

    if err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrases Not found",
            "error":   err,
        })
    }

    for cursor.Next(ctx) {
        var catchphrase models.Catchphrase
        cursor.Decode(&catchphrase)
        catchphrases = append(catchphrases, catchphrase)
    }

    last := math.Ceil(float64(total / limit))
    if last < 1 && total > 0 {
        last = 1
    }

    return c.Status(fiber.StatusOK).JSON(fiber.Map{
        "data":      catchphrases,
        "total":     total,
        "page":      page,
        "last_page": last,
        "limit":     limit,
    })
}

func GetCatchphrase(c *fiber.Ctx) error {
    catchphraseCollection := config.MI.DB.Collection("catchphrases")
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)

    var catchphrase models.Catchphrase
    objId, err := primitive.ObjectIDFromHex(c.Params("id"))
    findResult := catchphraseCollection.FindOne(ctx, bson.M{"_id": objId})
    if err := findResult.Err(); err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrase Not found",
            "error":   err,
        })
    }

    err = findResult.Decode(&catchphrase)
    if err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrase Not found",
            "error":   err,
        })
    }

    return c.Status(fiber.StatusOK).JSON(fiber.Map{
        "data":    catchphrase,
        "success": true,
    })
}

func AddCatchphrase(c *fiber.Ctx) error {
    catchphraseCollection := config.MI.DB.Collection("catchphrases")
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
    catchphrase := new(models.Catchphrase)

    if err := c.BodyParser(catchphrase); err != nil {
        log.Println(err)
        return c.Status(400).JSON(fiber.Map{
            "success": false,
            "message": "Failed to parse body",
            "error":   err,
        })
    }

    result, err := catchphraseCollection.InsertOne(ctx, catchphrase)
    if err != nil {
        return c.Status(500).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrase failed to insert",
            "error":   err,
        })
    }
    return c.Status(fiber.StatusCreated).JSON(fiber.Map{
        "data":    result,
        "success": true,
        "message": "Catchphrase inserted successfully",
    })

}

func UpdateCatchphrase(c *fiber.Ctx) error {
    catchphraseCollection := config.MI.DB.Collection("catchphrases")
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
    catchphrase := new(models.Catchphrase)

    if err := c.BodyParser(catchphrase); err != nil {
        log.Println(err)
        return c.Status(400).JSON(fiber.Map{
            "success": false,
            "message": "Failed to parse body",
            "error":   err,
        })
    }

    objId, err := primitive.ObjectIDFromHex(c.Params("id"))
    if err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrase not found",
            "error":   err,
        })
    }

    update := bson.M{
        "$set": catchphrase,
    }
    _, err = catchphraseCollection.UpdateOne(ctx, bson.M{"_id": objId}, update)
    if err != nil {
        return c.Status(500).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrase failed to update",
            "error":   err.Error(),
        })
    }
    return c.Status(fiber.StatusCreated).JSON(fiber.Map{
        "success": true,
        "message": "Catchphrase updated successfully",
    })
}

func DeleteCatchphrase(c *fiber.Ctx) error {
    catchphraseCollection := config.MI.DB.Collection("catchphrases")
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)

    objId, err := primitive.ObjectIDFromHex(c.Params("id"))
    if err != nil {
        return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrase not found",
            "error":   err,
        })
    }
    _, err = catchphraseCollection.DeleteOne(ctx, bson.M{"_id": objId})
    if err != nil {
        return c.Status(500).JSON(fiber.Map{
            "success": false,
            "message": "Catchphrase failed to delete",
            "error":   err,
        })
    }
    return c.Status(fiber.StatusCreated).JSON(fiber.Map{
        "success": true,
        "message": "Catchphrase deleted successfully",
    })
}

进入全屏模式 退出全屏模式

控制器文件将包含用于查询我们的数据库的逻辑。

创建流行语路线

让我们在应用程序的根文件夹中创建一个新文件夹routes来保存所有路由。

在 routes 文件夹中创建一个新文件catchphrases.go,其内容如下:

package routes

import (
    "github.com/gofiber/fiber/v2"
    "github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/controllers" // replace
)

func CatchphrasesRoute(route fiber.Router) {
    route.Get("/", controllers.GetAllCatchphrases)
    route.Get("/:id", controllers.GetCatchphrase)
    route.Post("/", controllers.AddCatchphrase)
    route.Put("/:id", controllers.UpdateCatchphrase)
    route.Delete("/:id", controllers.DeleteCatchphrase)
}

进入全屏模式 退出全屏模式

放在一起

修改main.go文件如下:

package main

import (
    "log"
    "os"

    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
    "github.com/gofiber/fiber/v2/middleware/logger"
    "github.com/joho/godotenv"
    "github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/config"
    "github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/routes"
)

func setupRoutes(app *fiber.App) {
    app.Get("/", func(c *fiber.Ctx) error {
        return c.Status(fiber.StatusOK).JSON(fiber.Map{
            "success":     true,
            "message":     "You are at the root endpoint 😉",
            "github_repo": "https://github.com/MikeFMeyer/catchphrase-go-mongodb-rest-api",
        })
    })

    api := app.Group("/api")

    routes.CatchphrasesRoute(api.Group("/catchphrases"))
}

func main() {
    if os.Getenv("APP_ENV") != "production" {
        err := godotenv.Load()
        if err != nil {
            log.Fatal("Error loading .env file")
        }
    }

    app := fiber.New()

    app.Use(cors.New())
    app.Use(logger.New())

    config.ConnectDB()

    setupRoutes(app)

    port := os.Getenv("PORT")
    err := app.Listen(":" + port)

    if err != nil {
        log.Fatal("Error app failed to start")
        panic(err)
    }
}

进入全屏模式 退出全屏模式

运行应用程序后,您应该能够导航到以下路线localhost:3000/api/catchphrases以查看数据库中的流行语。

在 Heroku 上托管

Heroku 允许您免费托管您的应用程序,但资源有限。要设置项目,请使用Heroku 官方文档中的以下网页。

注意:您可能需要添加以下配置变量才能运行应用程序:

MONGO_URI = <Your mongo uri>

DB = <Your database name>

PORT = 8080

APP_ENV = production

进入全屏模式 退出全屏模式

额外

这是我用于此 API 的数据集。

电影流行语数据集

感谢阅读

这是使用 Fiber (Go) 和 MongoDB 构建的 REST API 的一个非常基本的示例。代码可以从github下载。

Logo

MongoDB社区为您提供最前沿的新闻资讯和知识内容

更多推荐