Visão geral
Este tutorial mostra como criar um aplicação web usando o MongoDB Go Driver e a estrutura web Gi.
O Gi é um framework web rápido e leve para Go. Seu design mínima e modular o torna ideal para construir servidores web, APIs e microsserviços.
Neste tutorial, você pode aprender como criar um aplicação da web com os seguintes pontos de conexão que se conectam a um banco de dados MongoDB :
Um endpoint para recuperar todos os filmes de uma coleção
Um endpoint para recuperar um único filme por ID
Um endpoint para executar operações de agregação na collection
movies
Dados de amostra
O tutorial utiliza a movies collection no sample_mflix banco de dados a partir do conjunto de dados de amostra do Atlas . Para saber como criar um cluster MongoDB Atlas gratuito e carregar os conjuntos de dados de amostra, consulte o guia Iniciar com Atlas .
Pré-requisitos
Antes de iniciar este tutorial, verifique se você tem o seguinte:
Go instalado em sua máquina de desenvolvimento
Um sistema MongoDB . Você pode utilizar um agrupamento de MongoDB Atlas gratuito para este tutorial. Para mais informações, consulte o guia Iniciar com Atlas .
Configurar seu projeto
Criar a Estrutura do Aplicativo
Criar o arquivo do aplicação principal
Crie um arquivo main.go no diretório do projeto e adicione o seguinte código para configurar um aplicação Gi básico :
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) } }
Conecte-se ao MongoDB
Atualize seu arquivo main.go para se conectar ao MongoDB. Substitua o código existente pelo seguinte código para se conectar à sua implantação do 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) } }
Substitua o <connection-string> espaço reservado por sua string de conexão real do MongoDB . Para saber mais sobre como obter sua string de conexão, consulte o guia Conectar a um cluster.
Implemente os endpoints da API
Agora, você pode adicionar três endpoints para interagir com os dados do filme. Atualize seu arquivo main.go para incluir as importações e implementações de endpoint necessárias.
Adicionar manipuladores de rota
Adicione os seguintes manipuladores de rota após sua função 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) }
Registrar rotas
Atualize sua função main para registrar as novas rotas:
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) } }
Teste seu aplicativo
Você pode testar os endpoints criados para garantir que funcionem conforme o esperado.
Teste Obter Todos os filmes
Reinicie seu servidor e navegue até http://localhost:8080/movies em seu navegador da web. Você deverá ver uma resposta JSON contendo uma array de filmes do conjunto de dados de amostra.
Teste Obter filme por ID
Para testar o endpoint de filme único, copie um valor _id da resposta do filme e navegue até http://localhost:8080/movies/{id}.
Por exemplo, se você navegar para http://localhost:8080/movies/573a1390f29313caabcd42e8, a resposta no navegador será semelhante ao seguinte:
{ "_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" ]," .... }
Endpoint da Agregação de Teste
Para testar o endpoint de agregação , é necessário fazer uma solicitação POST. Você pode usar uma ferramenta como curl ou Postman para enviar uma solicitação com um pipeline de agregação no corpo da solicitação.
O exemplo de agregação a seguir conta filmes de comedia por ano:
[ {"$match": {"genres": "Comedy"}}, {"$group": { "_id": "$year", "count": {"$sum": 1} }}, {"$sort": {"count": -1}} ]
Execute o seguinte código no seu terminal para testar esse endpoint com 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 }, ... ]
Aviso
O endpoint de agregação neste tutorial permite que os usuários executem qualquer pipeline de agregação . Em um ambiente de produção, você deve limitar os tipos de operações e validar a entrada para evitar possíveis problemas de segurança ou de desempenho.
Código completo
O seguinte arquivo mostra o código completo do seu aplicação da 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) }
Resumo
Agora você tem um aplicação básico que demonstra o seguinte:
Configura um projeto Go com o framework web Gi e o driver Go.
Conecta-se a um banco de dados MongoDB usando o driver Go.
Implementa endpoints de API REST que executam operações MongoDB para localizar documentos, localizar um único documento e executar pipelines de agregação .
Manipula cenários comuns, como converter IDs de string em ObjectIDs.
Implementa o tratamento adequado de erros para operações de banco de dados .
Próximos passos
Para desenvolver ainda mais seu aplicação, considere as seguintes etapas a seguir:
Adicionar validação de entrada e limitação de solicitação para o endpoint de agregação
Implementar autenticação e autorização do
Adicionar índices para melhorar o desempenho da query
Recursos adicionais
Para saber mais sobre os conceitos deste tutorial, consulte os seguintes recursos: