Docs 菜单
Docs 主页
/ /

教程:使用Go驾驶员和 Gin 构建 Web 应用程序

本教程向您展示如何使用MongoDB Go驱动程序和 Gin Web框架构建Web应用程序。

Gin 是一个快速、轻量级的Go Web框架。其极简主义和模块化设计使其成为构建 Web 服务器、API 和微服务的理想选择。

在本教程中,您可以学习;了解如何创建具有以下连接到MongoDB 数据库的端点的 Web应用程序:

  • 用于从集合中检索所有电影的端点

  • 按ID检索单部电影的端点

  • 用于在 movies集合上运行聚合操作的端点

moviessample_mflix本教程使用Atlas示例数据集的 数据库中的 集合。要学习;了解如何创建免费的MongoDB Atlas 群集并加载示例数据集,请参阅Atlas入门指南。

在开始本教程之前,请确保您具备以下条件:

1

为项目创建一个新目录并导航到该目录:

mkdir mflix
cd mflix

初始化新的Go模块来管理项目依赖项:

go mod init mflix
2

安装项目所需的依赖项:

go get github.com/gin-gonic/gin
go get go.mongodb.org/mongo-driver/v2/mongo
1

在项目目录中创建一个 main.go文件并添加以下代码以设立基本的 Gin应用程序:

package main
import (
"log"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello World",
})
})
if err := r.Run(); err != nil {
log.Fatal("Failed to start server:", err)
}
}
2

运行以下代码来测试基本应用程序是否正常工作:

go run main.go

在网络浏览器中导航到 http://localhost:8080,查看包含“Hello World”消息的JSON响应。

更新 main.go文件以连接到MongoDB。将现有代码替换为以下代码以连接到MongoDB 部署:

package main
import (
"context"
"log"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
// Replace with your MongoDB connection string
const uri = "<connection-string>"
func main() {
// Connects to MongoDB
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
client, err := mongo.Connect(opts)
if err != nil {
log.Fatal("Could not connect to MongoDB:", err)
}
// Ensures the client disconnects when main exits
defer func() {
if err := client.Disconnect(context.TODO()); err != nil {
log.Fatal("Error disconnecting from MongoDB:", err)
}
}()
// Pings the database to verify connection
if err := client.Ping(context.TODO(), nil); err != nil {
log.Fatal("Could not ping MongoDB:", err)
}
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello World",
})
})
if err := r.Run(); err != nil {
log.Fatal("Failed to start server:", err)
}
}

<connection-string> 占位符替换为实际的MongoDB连接字符串。要学习;了解有关如何获取连接字符串的更多信息,请参阅连接到集群指南。

现在,您可以添加三个端点来与电影数据交互。更新 main.go文件以包含必要的导入和端点实现。

1

在文件顶部添加所需的导入:

import (
"context"
"log"
"net/http"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
2

connectToMongoDB 函数之后添加以下路由处理程序:

// GET /movies - Retrieves all movies
func getMovies(c *gin.Context, client *mongo.Client) {
// Find all movies in the collection
cursor, err := client.Database("sample_mflix").Collection("movies").Find(c.Request.Context(), bson.D{})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
defer cursor.Close(c.Request.Context())
// Decodes all results
var movies []bson.D
if err = cursor.All(c.Request.Context(), &movies); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Returns the movies
c.JSON(http.StatusOK, movies)
}
// GET /movies/:id - Retrieves a movie by ID
func getMovieByID(c *gin.Context, client *mongo.Client) {
// Gets the movie ID from the URL parameter as a string
idStr := c.Param("id")
// Converts string ID to MongoDB ObjectID
id, err := bson.ObjectIDFromHex(idStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid movie ID format"})
return
}
// Finds the movie by ObjectID
var movie bson.D
err = client.Database("sample_mflix").Collection("movies").FindOne(c.Request.Context(), bson.D{{"_id", id}}).Decode(&movie)
if err != nil {
if err == mongo.ErrNoDocuments {
c.JSON(http.StatusNotFound, gin.H{"error": "Movie not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Returns the movie
c.JSON(http.StatusOK, movie)
}
// POST /movies/aggregations - Runs aggregation pipeline
func aggregateMovies(c *gin.Context, client *mongo.Client) {
// Gets aggregation pipeline from request body
var pipeline interface{}
if err := c.ShouldBindJSON(&pipeline); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid aggregation pipeline"})
return
}
// Executes the aggregation pipeline
cursor, err := client.Database("sample_mflix").Collection("movies").Aggregate(c.Request.Context(), pipeline)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
defer cursor.Close(c.Request.Context())
// Decodes the results
var result []bson.D
if err = cursor.All(c.Request.Context(), &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Returns the aggregation result
c.JSON(http.StatusOK, result)
}
3

更新 main 函数以注册新路由:

func main() {
// Connects to MongoDB
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
client, err := mongo.Connect(opts)
if err != nil {
log.Fatal("Could not connect to MongoDB:", err)
}
// Ensures the client disconnects when main exits
defer func() {
if err := client.Disconnect(context.TODO()); err != nil {
log.Fatal("Error disconnecting from MongoDB:", err)
}
}()
// Pings the database to verify connection
if err := client.Ping(context.TODO(), nil); err != nil {
log.Fatal("Could not ping MongoDB:", err)
}
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello World",
})
})
// Registers movie endpoints
r.GET("/movies", func(c *gin.Context) {
getMovies(c, client)
})
r.GET("/movies/:id", func(c *gin.Context) {
getMovieByID(c, client)
})
r.POST("/movies/aggregations", func(c *gin.Context) {
aggregateMovies(c, client)
})
if err := r.Run(); err != nil {
log.Fatal("Failed to start server:", err)
}
}

您可以测试创建的端点,确保它们按预期工作。

重新启动服务器并在网络浏览器中导航至 http://localhost:8080/movies。您应该会看到一个JSON响应,其中包含示例数据集中的电影大量。

要测试单个电影端点,请从电影响应中复制 _id 值并导航到 http://localhost:8080/movies/{id}

示例,如果导航到 http://localhost:8080/movies/573a1390f29313caabcd42e8,浏览器中的响应将类似于以下内容:

{
"_id": "573a1390f29313caabcd42e8",
"awards": {
"wins": 1,
"nominations": 0,
"text": "1 win."
},
"cast": [
"A.C. Abadie",
"Gilbert M. 'Broncho Billy' Anderson",
"George Barnes",
"Justus D. Barnes"
],
"countries": [
"USA"
],
"directors": [
"Edwin S. Porter"
],
"fullplot": "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.",
"genres": [
"Short",
"Western"
],"
....
}

要测试聚合端点,您需要发出 POST请求。您可以使用 curl 或 Postman 等工具发送在请求正文中包含聚合管道的请求。

以下示例聚合按年份对喜剧电影进行计数:

[
{"$match": {"genres": "Comedy"}},
{"$group": {
"_id": "$year",
"count": {"$sum": 1}
}},
{"$sort": {"count": -1}}
]

在终端中运行以下代码以使用 curl 测试此端点:

curl -X POST http://localhost:8080/movies/aggregations \
-H "Content-Type: application/json" \
-d '[
{"$match": {"genres": "Comedy"}},
{"$group": {"_id": "$year", "count": {"$sum": 1}}},
{"$sort": {"count": -1}}
]'
[
{
"_id": 2014,
"count": 287
},
{
"_id": 2013,
"count": 286
},
{
"_id": 2009,
"count": 268
},
{
"_id": 2011,
"count": 263
},
{
"_id": 2006,
"count": 260
},
...
]

警告

本教程中的聚合端点允许用户运行任何聚合管道。在生产环境中,应限制操作类型并验证输入,以防止潜在的安全问题或性能问题。

以下文件显示了 Web应用程序的完整代码:

package main
import (
"context"
"log"
"net/http"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
// Replace with your MongoDB connection string
const uri = "YOUR-CONNECTION-STRING-HERE"
func main() {
// Connects to MongoDB
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
client, err := mongo.Connect(opts)
if err != nil {
log.Fatal("Could not connect to MongoDB:", err)
}
// Ensures the client disconnects when main exits
defer func() {
if err := client.Disconnect(context.TODO()); err != nil {
log.Fatal("Error disconnecting from MongoDB:", err)
}
}()
// Pings the database to verify connection
if err := client.Ping(context.TODO(), nil); err != nil {
log.Fatal("Could not ping MongoDB:", err)
}
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello World",
})
})
// Registers movie endpoints
r.GET("/movies", func(c *gin.Context) {
getMovies(c, client)
})
r.GET("/movies/:id", func(c *gin.Context) {
getMovieByID(c, client)
})
r.POST("/movies/aggregations", func(c *gin.Context) {
aggregateMovies(c, client)
})
if err := r.Run(); err != nil {
log.Fatal("Failed to start server:", err)
}
}
// GET /movies - Retrieves all movies
func getMovies(c *gin.Context, client *mongo.Client) {
// Find all movies in the collection
cursor, err := client.Database("sample_mflix").Collection("movies").Find(c.Request.Context(), bson.D{})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
defer cursor.Close(c.Request.Context())
// Decodes all results
var movies []bson.D
if err = cursor.All(c.Request.Context(), &movies); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Returns the movies
c.JSON(http.StatusOK, movies)
}
// GET /movies/:id - Retrieves a movie by ID
func getMovieByID(c *gin.Context, client *mongo.Client) {
// Gets the movie ID from the URL parameter as a string
idStr := c.Param("id")
// Converts string ID to MongoDB ObjectID
id, err := bson.ObjectIDFromHex(idStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid movie ID format"})
return
}
// Finds the movie by ObjectID
var movie bson.D
err = client.Database("sample_mflix").Collection("movies").FindOne(c.Request.Context(), bson.D{{"_id", id}}).Decode(&movie)
if err != nil {
if err == mongo.ErrNoDocuments {
c.JSON(http.StatusNotFound, gin.H{"error": "Movie not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Returns the movie
c.JSON(http.StatusOK, movie)
}
// POST /movies/aggregations - Runs aggregation pipeline
func aggregateMovies(c *gin.Context, client *mongo.Client) {
// Gets aggregation pipeline from request body
var pipeline interface{}
if err := c.ShouldBindJSON(&pipeline); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid aggregation pipeline"})
return
}
// Executes the aggregation pipeline
cursor, err := client.Database("sample_mflix").Collection("movies").Aggregate(c.Request.Context(), pipeline)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
defer cursor.Close(c.Request.Context())
// Decodes the results
var result []bson.D
if err = cursor.All(c.Request.Context(), &result); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Returns the aggregation result
c.JSON(http.StatusOK, result)
}

您现在拥有一个基本应用程序,可演示以下内容:

  • 使用 Gin Web框架和Go驾驶员设置Go项目。

  • 使用Go驾驶员连接到MongoDB 数据库。

  • 实现执行MongoDB操作以查找文档、查找单个文档和运行聚合管道的REST API端点。

  • 处理常见场景,例如将字符串 ID 转换为 ObjectID。

  • 为数据库操作实施正确的错误处理。

要进一步开发您的应用程序,请考虑以下后续步骤:

  • 为聚合端点添加输入验证和请求限制

  • 实施 身份验证和授权

  • 添加索引以提高查询性能

要详细学习;了解本教程中的概念,请参阅以下资源:

后退

存储大文件