mongo客户端在main函数中设置,其他模块函数接收nil值

Posted

技术标签:

【中文标题】mongo客户端在main函数中设置,其他模块函数接收nil值【英文标题】:Mongo client set in main function, functions in other modules receive nil value 【发布时间】:2021-06-05 19:59:10 【问题描述】:

我有一个使用 mux 和 mongo-driver 的宁静 API。按照教程,我尝试在主包中设置服务器和 mongo 客户端:

package main

import (
    "context"
    "fmt"
    "net/http"
    "time"

    "github.com/gorilla/mux"
    c "github.com/moonlightfight/elo-backend/config"
    "github.com/moonlightfight/elo-backend/routes/admin"
    "github.com/moonlightfight/elo-backend/routes/tournament"
    "github.com/spf13/viper"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

var client *mongo.Client

func main() 
    // Set the file name of the configurations file
    viper.SetConfigName("config")

    // Set the path to look for the configurations file
    viper.AddConfigPath(".")

    // Enable VIPER to read Environment Variables
    viper.AutomaticEnv()

    viper.SetConfigType("yml")
    var configuration c.Configurations

    if err := viper.ReadInConfig(); err != nil 
        fmt.Printf("Error reading config file, %s", err)
    

    err := viper.Unmarshal(&configuration)
    if err != nil 
        fmt.Printf("Unable to decode into struct, %v", err)
    
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
    clientOptions := options.Client().ApplyURI(fmt.Sprintf("mongodb+srv://%s:%s@cluster0.ucnph.mongodb.net/%s?retryWrites=true&w=majority", configuration.Database.DBUser, configuration.Database.DBPass, configuration.Database.DBName))
    port := fmt.Sprintf(":%d", configuration.Server.Port)
    mongo.Connect(ctx, clientOptions)
    router := mux.NewRouter()
    router.HandleFunc("/api/admin", admin.CreateAdminEndpoint).Methods("POST")
    router.HandleFunc("/api/admin/login", admin.AdminLoginEndpoint).Methods("POST")
    router.HandleFunc("/api/tournament/getfromweb", tournament.GetTournamentData).Methods("GET")
    fmt.Printf("server listening on http://localhost%v", port)
    http.ListenAndServe(port, router)

现在,为了更简洁地管理我的代码,我设置了模块(正如您在 main 上的导入中看到的那样)来处理 mux 将用于端点的函数。

在一种特定情况下(处理“/api/admin”端点:

package admin

import (
    "context"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net/http"
    "time"

    "github.com/dgrijalva/jwt-go"
    c "github.com/moonlightfight/elo-backend/config"
    m "github.com/moonlightfight/elo-backend/models"
    "github.com/spf13/viper"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
    "golang.org/x/crypto/bcrypt"
)

var client *mongo.Client

// other code here

func CreateAdminEndpoint(response http.ResponseWriter, request *http.Request) 
    response.Header().Set("content-type", "application/json")
    var admin m.Admin
    err := json.NewDecoder(request.Body).Decode(&admin)
    if err != nil 
        log.Println(err)
    
    // encrypt user password
    admin.Password = HashPassword(admin.Password)
    fmt.Println(client)
    collection := client.Database("test").Collection("Admin")
    ctx, ctxErr := context.WithTimeout(context.Background(), 5*time.Second)

    if ctxErr != nil 
        log.Println(ctxErr)
    
    result, resErr := collection.InsertOne(ctx, admin)
    if resErr != nil 
        log.Println(resErr)
    
    json.NewEncoder(response).Encode(result)

运行时,我收到以下错误:

2021/06/05 02:02:39 http: panic serving [::1]:53359: runtime error: invalid memory address or nil pointer dereference

这指向我在端点函数中定义集合的行,它记录为具有 nil 值。我显然没有在模块中正确定义 mongo 客户端,并且不确定跨多个模块维护此客户端连接的最佳实践。

【问题讨论】:

一个模块不是一个包,也没有什么魔法:如果你想初始化一个包级变量,你必须导出它并初始化它(例如从 main)。 在这种情况下最好的方法是什么? @lanierc 在主包中,导入管理包创建客户端并分配给管理包变量。导出 admin 客户端变量,以便 main 可以访问它:admin.Client = client 【参考方案1】:

在避免全局变量的同时执行此操作的标准方法是定义一个代表您的服务器的struct,其方法将是处理程序。然后这些方法共享结构的数据,然后您将诸如 mongo 客户端之类的东西放在那里。

类似这样的东西(在您的admin 包中):

type Server struct 
  client *mongo.Client


func NewServer(client *mongo.Client) *Server 
  return &Serverclient: client


func (srv *Server) CreateAdminEndpoint(response http.ResponseWriter, request *http.Request) 
  // ...
  // use srv.client here
  //

现在在main 中,您可以这样创建服务器:

 client, err := mongo.Connect(...)
 // handle err!
 srv := admin.NewServer(client)
 
 router := mux.NewRouter()
 router.HandleFunc("/api/admin", srv.CreateAdminEndpoint).Methods("POST")
  

【讨论】:

以上是关于mongo客户端在main函数中设置,其他模块函数接收nil值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 VBA 中设置全局变量

37如何在函数中设置一个全局变量 ?

如何在另一个模块中设置全局变量?

如何在嵌套函数中在无状态函数中设置状态?

如何在WebStorm中设置mongo

如何在 Angular 指令的 Link 函数中设置 CSS 样式