发布于 2026-01-06 6 阅读
0

使用 Golang 和 MongoDB 构建 REST API - Gin-gonic 版本 先决条件 开始编码 结论

使用 Golang 和 MongoDB 构建 REST API - Gin-gonic 版本

先决条件

让我们开始编程吧

结论

表述性状态转移(REST)是一种架构模式,它指导应用程序编程接口(API)的设计和开发。REST API 已成为产品服务器端与其客户端之间通信的标准,以提高性能、可扩展性、简洁性、可修改性、可见性、可移植性和可靠性。

本文将探讨如何使用 Golang 、Gin-gonic框架和MongoDB构建用户管理应用程序。在本教程的最后,我们将学习如何构建 Gin-gonic 应用程序、创建 REST API 以及使用 MongoDB 持久化数据。

Gin-gonic,通常简称为Gin,是一个用 Golang 编写的 HTTP Web 框架,专注于提升性能和生产力。Gin 使用定制版的HttpRouter,这是一个轻量级、高性能的 HTTP 请求路由器,其 API 路由的路由速度比大多数框架都要快。

MongoDB 是一款基于文档的数据库管理程序,可作为关系型数据库的替代方案。MongoDB 支持处理大型分布式数据集,并提供无缝存储和检索信息的选项。

您可以在此存储库中找到完整的源代码

先决条件

本文后续步骤需要具备 Golang 编程经验。虽然不要求具备 MongoDB 经验,但有相关经验会更好。

我们还需要以下物品:

让我们开始编程吧

入门

首先,我们需要导航到目标目录,然后在终端中运行以下命令。

mkdir gin-mongo-api && cd gin-mongo-api
Enter fullscreen mode Exit fullscreen mode

该命令会创建一个gin-mongo-api文件夹并导航到项目目录。

接下来,我们需要初始化一个 Go 模块来管理项目依赖项,运行以下命令:

go mod init gin-mongo-api
Enter fullscreen mode Exit fullscreen mode

此命令将创建一个go.mod用于跟踪项目依赖关系的文件。

接下来,我们使用以下命令安装所需的依赖项:

go get -u github.com/gin-gonic/gin go.mongodb.org/mongo-driver/mongo github.com/joho/godotenv github.com/go-playground/validator/v10
Enter fullscreen mode Exit fullscreen mode

github.com/gin-gonic/gin是一个用于构建 Web 应用程序的框架。

go.mongodb.org/mongo-driver/mongo是用于连接 MongoDB 的驱动程序。

github.com/joho/godotenv是一个用于管理环境变量的库。

github.com/go-playground/validator/v10是一个用于验证结构体和字段的库。

应用程序入口点

项目依赖项安装完毕后,我们需要main.go在根目录下创建一个文件,并添加以下代码片段:



package main

import "github.com/gin-gonic/gin"

func main() {
        router := gin.Default()

        router.GET("/", func(c *gin.Context) {
                c.JSON(200, gin.H{
                        "data": "Hello from Gin-gonic & mongoDB",
                })
        })

        router.Run("localhost:6000") 
}


Enter fullscreen mode Exit fullscreen mode

上面的代码片段执行以下操作:

  • 导入所需的依赖项。
  • 使用配置初始化 Gin 路由器Default。该Default函数会使用默认中间件(日志记录器和恢复器)配置 Gin 路由器。
  • 使用该Get函数路由到/路径和返回 JSON 的处理函数Hello from Gin-gonic & mongoDB
  • 使用该Run函数将服务器连接routerhttp.Server,并开始监听和处理 HTTP 请求localhost:6000

接下来,我们可以通过在终端中运行以下命令来启动开发服务器,从而测试我们的应用程序。

go run main.go
Enter fullscreen mode Exit fullscreen mode

测试应用

Golang 中的模块化

良好的项目文件夹结构至关重要。合理的项目结构可以简化我们管理应用程序依赖项的方式,并使我们自己和其他人更容易阅读代码库。
为此,我们需要在项目目录中创建 `<module> configs`、 `<module> ` controllers、 `<module> ` 和`<module>` 文件夹。modelsresponsesroutes

更新了项目文件夹结构

PS go.sum 文件包含所有依赖项校验和,并由 go 工具自动管理,我们无需担心。

configs用于模块化项目配置文件

controllers是用于模块化应用程序逻辑。

models是用于数据和数据库逻辑的模块化。

responses是用来模块化描述我们希望 API 返回的响应的文件。这一点稍后会更清楚。

routes用于模块化 URL 模式和处理程序信息。

设置 MongoDB

完成上述步骤后,我们需要登录或注册MongoDB帐户。​​点击项目下拉菜单,然后点击“新建项目”按钮。

新项目

输入golang-api项目名称,点击“下一步”,然后点击“创建项目”。

输入项目名称
创建项目

点击“构建数据库”

选择“共享”作为数据库类型。

共享内容以红色高亮显示

点击“创建”按钮设置集群。设置过程可能需要一些时间。

创建集群

接下来,我们需要创建一个用户,以便从外部访问数据库。输入用户名密码,然后点击“创建用户”。我们还需要添加自己的 IP 地址,以便安全地连接到数据库。点击“添加我的当前 IP 地址”按钮即可。然后点击“完成并关闭”保存更改。

创建用户
添加 IP

保存更改后,应该会看到如下所示的“数据库部署”屏幕:

数据库屏幕

将我们的应用程序连接到 MongoDB

配置完成后,我们需要将应用程序连接到已创建的数据库。为此,请单击“连接”按钮。

连接到数据库

点击“连接您的应用程序”,将驱动程序Go版本更改为如下所示的内容。然后点击复制图标复制连接字符串。

连接应用程序
复制连接字符串

设置环境变量
接下来,我们需要修改复制的连接字符串,使其包含我们之前创建的用户密码,并更改数据库名称。为此,首先需要.env在根目录下创建一个文件,并在该文件中添加以下代码片段:

MONGOURI=mongodb+srv://<YOUR USERNAME HERE>:<YOUR PASSWORD HERE>@cluster0.e5akf.mongodb.net/myFirstDatabese?retryWrites=true&w=majority
Enter fullscreen mode Exit fullscreen mode

以下是一个正确填写的连接字符串示例:

MONGOURI=mongodb+srv://malomz:malomzPassword@cluster0.e5akf.mongodb.net/golangDB?retryWrites=true&w=majority
Enter fullscreen mode Exit fullscreen mode

更新了包含 .env 文件的文件夹结构

加载环境变量
完成上述步骤后,我们需要创建一个辅助函数,使用github.com/joho/godotenv之前安装的库来加载环境变量。为此,我们需要导航到该configs文件夹​​,并在该文件夹中创建一个env.go文件,然后添加以下代码片段:



package configs

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")
}


Enter fullscreen mode Exit fullscreen mode

上面的代码片段执行以下操作:

  • 导入所需的依赖项。
  • 创建一个EnvMongoURI函数,检查环境变量是否已正确加载,并返回该环境变量。

连接到 MongoDB
要从我们的应用程序连接到 MongoDB 数据库,首先需要导航到该configs文件夹​​,并在该文件夹中创建一个setup.go文件,然后添加以下代码片段:



package configs

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
}

//Client instance
var DB *mongo.Client = ConnectDB()

//getting database collections
func GetCollection(client *mongo.Client, collectionName string) *mongo.Collection {
    collection := client.Database("golangAPI").Collection(collectionName)
    return collection
}


Enter fullscreen mode Exit fullscreen mode

上面的代码片段执行以下操作:

  • 导入所需的依赖项。
  • 创建一个ConnectDB函数,首先配置客户端使用正确的 URI 并检查错误。其次,我们定义了一个 10 秒的连接超时时间。第三,检查连接数据库时是否存在错误,如果连接时间超过 10 秒则取消连接。最后,我们 ping 了数据库以测试连接,并返回了实例client
  • 创建一个DB变量实例ConnectDB。这在创建集合时会非常有用。
  • 创建一个函数,用于在数据库中GetCollection检索和创建数据。collections

接下来,我们需要在应用程序启动时连接到数据库。为此,我们需要main.go按如下所示进行修改:



package main

import (
    "gin-mongo-api/configs" //add this
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    //run database
    configs.ConnectDB()

    router.Run("localhost:6000")
}


Enter fullscreen mode Exit fullscreen mode

设置 API 路由处理程序和响应类型

路由处理程序
完成上述步骤后,我们需要在文件夹user_route.go内创建一个文件routes来管理应用程序中所有与用户相关的路由,如下所示:



package routes

import "github.com/gin-gonic/gin"

func UserRoute(router *gin.Engine)  {
    //All routes related to users comes here
}


Enter fullscreen mode Exit fullscreen mode

接下来,我们需要将新创建的路由附加到http.Server,具体main.go操作如下所示:



package main

import (
    "gin-mongo-api/configs"
    "gin-mongo-api/routes" //add this
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    //run database
    configs.ConnectDB()

    //routes
    routes.UserRoute(router) //add this

    router.Run("localhost:6000")
}


Enter fullscreen mode Exit fullscreen mode


接下来,我们需要创建一个可重用的响应类型描述struct符来描述 API 的响应。为此,请导航到该responses文件夹​​,并在其中创建一个user_response.go文件,然后添加以下代码片段:



package responses

type UserResponse struct {
    Status  int                    `json:"status"`
    Message string                 `json:"message"`
    Data    map[string]interface{} `json:"data"`
}


Enter fullscreen mode Exit fullscreen mode

上面的代码片段创建了一个UserResponse包含`<struct>` StatusMessage`<property>` 和Data`<property>` 属性的结构体,用于表示 API 响应类型。

PSjson:"status"`< struct 标签>` json:"message"、`<struct 标签>` 和 ` json:"data" <struct 标签>` 被称为结构体标签。结构体标签允许我们将元信息附加到相应的结构体属性上。换句话说,我们使用它们来重新格式化 API 返回的 JSON 响应。

最后,创建 REST API

接下来,我们需要一个模型来表示我们的应用程序数据。为此,我们需要导航到该models文件夹​​,并在该文件夹中创建一个user_model.go文件,然后添加以下代码片段:



package models

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"`
}


Enter fullscreen mode Exit fullscreen mode

上面的代码片段执行以下操作:

  • 导入所需的依赖项。
  • 创建一个User包含必需属性的结构体。我们在结构体标签中添加了 `--no-text`omitemptyvalidate:"required"`--required`,分别告诉 Gin-gonic 忽略空字段并将空字段设为必需字段。

创建用户端点
模型设置完成后,我们现在可以创建一个创建用户的函数。为此,我们需要导航到该controllers文件夹​​,并在该文件夹中创建一个user_controller.go文件,然后添加以下代码片段:



package controllers

import (
    "context"
    "gin-mongo-api/configs"
    "gin-mongo-api/models"
    "gin-mongo-api/responses"
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
)

var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()

func CreateUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        var user models.User
        defer cancel()

        //validate the request body
        if err := c.BindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //use the validator library to validate required fields
        if validationErr := validate.Struct(&user); validationErr != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": validationErr.Error()}})
            return
        }

        newUser := models.User{
            Id:       primitive.NewObjectID(),
            Name:     user.Name,
            Location: user.Location,
            Title:    user.Title,
        }

        result, err := userCollection.InsertOne(ctx, newUser)
        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        c.JSON(http.StatusCreated, responses.UserResponse{Status: http.StatusCreated, Message: "success", Data: map[string]interface{}{"data": result}})
    }
}


Enter fullscreen mode Exit fullscreen mode

上面的代码片段执行以下操作:

  • 导入所需的依赖项。
  • 分别使用我们之前安装的库创建集合和验证模型的变量userCollectionvalidategithub.com/go-playground/validator/v10
  • 创建一个CreateUser返回 Gin-gonic 处理程序的函数。在返回的处理程序中,我们首先定义了将用户插入文档的超时时间为 10 秒,并使用验证器库验证请求体和必填字段。我们使用UserResponse之前创建的结构体返回相应的消息和状态码。其次,我们创建了一个newUser变量,使用该函数插入它userCollection.InsertOne,并检查是否存在错误。最后,如果插入成功,则返回正确的响应。

接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:



package routes

import (
    "gin-mongo-api/controllers" //add this
    "github.com/gin-gonic/gin"
)

func UserRoute(router *gin.Engine)  {
    router.POST("/user", controllers.CreateUser()) //add this
}


Enter fullscreen mode Exit fullscreen mode

获取用户端点
要获取用户详细信息,我们需要user_controller.go按如下所示进行修改:



package controllers

import (
    //other import goes here
    "go.mongodb.org/mongo-driver/bson" //add this
)

var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()

func CreateUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        //create user code goes here
    }
}

func GetAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        userId := c.Param("userId")
        var user models.User
        defer cancel()

        objId, _ := primitive.ObjectIDFromHex(userId)

        err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&user)
        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        c.JSON(http.StatusOK, responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": user}})
    }
}


Enter fullscreen mode Exit fullscreen mode

上面的代码片段执行以下操作:

  • 导入所需的依赖项。
  • 创建一个GetAUser返回 Gin-gonic 处理程序的函数。在返回的处理程序中,我们首先定义了在文档中查找用户的超时时间(10 秒),一个用于从 URL 参数中userId获取用户ID的变量,以及一个用于将字符串user转换为MongoDB 使用的 BSON 类型的变量。其次,我们使用 URL 参数搜索用户,并将 URL作为过滤器传递,然后使用属性方法获取相应的对象。最后,我们返回解码后的响应。userIdprimitive.ObjectIDuserCollection.FindOneobjIdDecode

接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:



package routes

import (
    //import goes here
)

func UserRoute(router *gin.Engine) {
    //other routes goes here
    router.GET("/user/:userId", controllers.GetAUser()) //add this
}


Enter fullscreen mode Exit fullscreen mode

PS: 我们还 userId 向 URL 路径传递了一个参数。该参数必须与我们在控制器中指定的参数一致。

编辑用户端点
要编辑用户,我们需要user_controller.go按如下所示进行修改:



package controllers

import (
    //import goes here
)

var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()

func CreateUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        //create user code goes here
    }
}

func GetAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        // get a user code goes here
    }
}

func EditAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        userId := c.Param("userId")
        var user models.User
        defer cancel()
        objId, _ := primitive.ObjectIDFromHex(userId)

        //validate the request body
        if err := c.BindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //use the validator library to validate required fields
        if validationErr := validate.Struct(&user); validationErr != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": validationErr.Error()}})
            return
        }

        update := bson.M{"name": user.Name, "location": user.Location, "title": user.Title}
        result, err := userCollection.UpdateOne(ctx, bson.M{"id": objId}, bson.M{"$set": update})
        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //get updated user details
        var updatedUser models.User
        if result.MatchedCount == 1 {
            err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&updatedUser)
            if err != nil {
                c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
                return
            }
        }

        c.JSON(http.StatusOK, responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": updatedUser}})
    }
}


Enter fullscreen mode Exit fullscreen mode

上面的函数EditAUser与原函数功能相同CreateUser。但是,我们添加了一个update变量来获取更新后的字段,并使用该变量更新了集合userCollection.UpdateOne。最后,我们搜索了更新后的用户详细信息,并返回了解码后的响应。

接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:



package routes

import (
    //import goes here
)

func UserRoute(router *gin.Engine) {
    //other routes goes here
    router.PUT("/user/:userId", controllers.EditAUser()) //add this
}


Enter fullscreen mode Exit fullscreen mode

删除用户端点
要删除用户,我们需要user_controller.go按如下所示进行修改:



package controllers

import (
    //import goes here
)

var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()

func CreateUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        //create user code goes here
    }
}

func GetAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        // get a user code goes here
    }
}

func EditAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        //edit a user code goes here
    }
}

func DeleteAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        userId := c.Param("userId")
        defer cancel()

        objId, _ := primitive.ObjectIDFromHex(userId)

        result, err := userCollection.DeleteOne(ctx, bson.M{"id": objId})
        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        if result.DeletedCount < 1 {
            c.JSON(http.StatusNotFound,
                responses.UserResponse{Status: http.StatusNotFound, Message: "error", Data: map[string]interface{}{"data": "User with specified ID not found!"}},
            )
            return
        }

        c.JSON(http.StatusOK,
            responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": "User successfully deleted!"}},
        )
    }
}


Enter fullscreen mode Exit fullscreen mode

DeleteAUser函数遵循之前的步骤,使用删除匹配的记录userCollection.DeleteOne。我们还检查了项目是否已成功删除,并返回了相应的响应。

接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:



package routes

import (
    //import goes here
)

func UserRoute(router *gin.Engine) {
    //other routes goes here
    router.DELETE("/user/:userId", controllers.DeleteAUser()) //add this
}


Enter fullscreen mode Exit fullscreen mode

获取用户列表端点
要获取用户列表,我们需要user_controller.go按如下所示进行修改:



package controllers

import (
    //import goes here
)

var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()

func CreateUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        //create user code goes here
    }
}

func GetAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        // get a user code goes here
    }
}

func EditAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        //edit a user code goes here
    }
}

func DeleteAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        //delete a user code goes here
    }
}

func GetAllUsers() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        var users []models.User
        defer cancel()

        results, err := userCollection.Find(ctx, bson.M{})

        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //reading from the db in an optimal way
        defer results.Close(ctx)
        for results.Next(ctx) {
            var singleUser models.User
            if err = results.Decode(&singleUser); err != nil {
                c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            }

            users = append(users, singleUser)
        }

        c.JSON(http.StatusOK,
            responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": users}},
        )
    }
}


Enter fullscreen mode Exit fullscreen mode

GetAllUsers函数遵循之前的步骤,通过获取用户列表来实现。我们还使用属性方法遍历返回的用户列表,userCollection.Find从而以最优方式读取该列表。Next

接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:



package routes

import (
    //import goes here
)

func UserRoute(router *gin.Engine) {
    //other routes goes here
    router.GET("/users", controllers.GetAllUsers()) //add this
}


Enter fullscreen mode Exit fullscreen mode

完成 user_controller.go



package controllers

import (
    "context"
    "gin-mongo-api/configs"
    "gin-mongo-api/models"
    "gin-mongo-api/responses"
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
)

var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()

func CreateUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        var user models.User
        defer cancel()

        //validate the request body
        if err := c.BindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //use the validator library to validate required fields
        if validationErr := validate.Struct(&user); validationErr != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": validationErr.Error()}})
            return
        }

        newUser := models.User{
            Id:       primitive.NewObjectID(),
            Name:     user.Name,
            Location: user.Location,
            Title:    user.Title,
        }

        result, err := userCollection.InsertOne(ctx, newUser)
        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        c.JSON(http.StatusCreated, responses.UserResponse{Status: http.StatusCreated, Message: "success", Data: map[string]interface{}{"data": result}})
    }
}

func GetAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        userId := c.Param("userId")
        var user models.User
        defer cancel()

        objId, _ := primitive.ObjectIDFromHex(userId)

        err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&user)
        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        c.JSON(http.StatusOK, responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": user}})
    }
}

func EditAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        userId := c.Param("userId")
        var user models.User
        defer cancel()

        objId, _ := primitive.ObjectIDFromHex(userId)

        //validate the request body
        if err := c.BindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //use the validator library to validate required fields
        if validationErr := validate.Struct(&user); validationErr != nil {
            c.JSON(http.StatusBadRequest, responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: map[string]interface{}{"data": validationErr.Error()}})
            return
        }

        update := bson.M{"name": user.Name, "location": user.Location, "title": user.Title}
        result, err := userCollection.UpdateOne(ctx, bson.M{"id": objId}, bson.M{"$set": update})

        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //get updated user details
        var updatedUser models.User
        if result.MatchedCount == 1 {
            err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&updatedUser)
            if err != nil {
                c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
                return
            }
        }

        c.JSON(http.StatusOK, responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": updatedUser}})
    }
}

func DeleteAUser() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        userId := c.Param("userId")
        defer cancel()

        objId, _ := primitive.ObjectIDFromHex(userId)

        result, err := userCollection.DeleteOne(ctx, bson.M{"id": objId})

        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        if result.DeletedCount < 1 {
            c.JSON(http.StatusNotFound,
                responses.UserResponse{Status: http.StatusNotFound, Message: "error", Data: map[string]interface{}{"data": "User with specified ID not found!"}},
            )
            return
        }

        c.JSON(http.StatusOK,
            responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": "User successfully deleted!"}},
        )
    }
}

func GetAllUsers() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        var users []models.User
        defer cancel()

        results, err := userCollection.Find(ctx, bson.M{})

        if err != nil {
            c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            return
        }

        //reading from the db in an optimal way
        defer results.Close(ctx)
        for results.Next(ctx) {
            var singleUser models.User
            if err = results.Decode(&singleUser); err != nil {
                c.JSON(http.StatusInternalServerError, responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: map[string]interface{}{"data": err.Error()}})
            }

            users = append(users, singleUser)
        }

        c.JSON(http.StatusOK,
            responses.UserResponse{Status: http.StatusOK, Message: "success", Data: map[string]interface{}{"data": users}},
        )
    }
}


Enter fullscreen mode Exit fullscreen mode

完成 user_route.go



package routes

import (
    "gin-mongo-api/controllers"
    "github.com/gin-gonic/gin"
)

func UserRoute(router *gin.Engine) {
    router.POST("/user", controllers.CreateUser())
    router.GET("/user/:userId", controllers.GetAUser())
    router.PUT("/user/:userId", controllers.EditAUser())
    router.DELETE("/user/:userId", controllers.DeleteAUser())
    router.GET("/users", controllers.GetAllUsers())
}


Enter fullscreen mode Exit fullscreen mode

完成上述步骤后,我们可以通过在终端中运行以下命令来启动开发服务器,从而测试我们的应用程序。

go run main.go
Enter fullscreen mode Exit fullscreen mode

终端输出

创建用户端点

获取用户端点

编辑用户端点

删除用户端点

获取用户列表端点

包含用户文档的数据库

结论

本文讨论了如何构建 Gin-gonic 应用程序、构建 REST API 以及使用 MongoDB 持久化数据。

您可能会发现以下资源很有用:

文章来源:https://dev.to/hackmamba/build-a-rest-api-with-golang-and-mongodb-gin-gonic-version-269m