在主函数中设置了Mongo客户端,其他模块中的函数接收到了空值。

huangapple go评论85阅读模式
英文:

Mongo client set in main function, functions in other modules receive nil value

问题

我有一个使用mux和mongo-driver的RESTful 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() {
	// 设置配置文件的文件名
	viper.SetConfigName("config")

	// 设置配置文件的路径
	viper.AddConfigPath(".")

	// 允许VIPER读取环境变量
	viper.AutomaticEnv()

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

	if err := viper.ReadInConfig(); err != nil {
		fmt.Printf("读取配置文件错误,%s", err)
	}

	err := viper.Unmarshal(&configuration)
	if err != nil {
		fmt.Printf("无法解码为结构体,%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("服务器监听在 http://localhost%v", port)
	http.ListenAndServe(port, router)
}

为了更简洁地管理我的代码,我设置了模块(如主要导入部分所示),用于处理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

// 其他代码

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)
	}
	// 加密用户密码
	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

这指向了在端点函数中定义collection的那一行,它被记录为具有nil值。显然,我没有正确地在模块中定义mongo客户端,并且不确定在多个模块之间保持此客户端连接的最佳实践。

英文:

I have a restful API utilizing mux and mongo-driver. Following a tutorial, I attempted to setup the server and mongo client like so in the main package:

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

Now, in order to manage my code more concisely, I set up modules (as you can see in the imports on main) to handle the functions that mux will use for the endpoints.

On one specific case (handling the "/api/admin" endpoint:

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

And when ran, I receive the following error:

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

This points to the line where I define collection in the endpoint function, which logged as having a nil value. I am clearly not getting the mongo client properly defined in the module, and not sure of the best practice for maintaining this client connection across multiple modules.

答案1

得分: 0

避免使用全局变量的标准方法是定义一个表示服务器的struct,其方法将作为处理程序。然后,这些方法共享struct的数据,你可以将诸如mongo客户端之类的内容放在其中。

在你的admin包中,可以这样写:

type Server struct {
  client *mongo.Client
}

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

func (srv *Server) CreateAdminEndpoint(response http.ResponseWriter, request *http.Request) {
  // ...
  // 在这里使用srv.client
  //
}

现在在main函数中,你可以这样创建服务器:

client, err := mongo.Connect(...)
// 处理错误!
srv := admin.NewServer(client)

router := mux.NewRouter()
router.HandleFunc("/api/admin", srv.CreateAdminEndpoint).Methods("POST")

希望对你有所帮助!

英文:

The standard way of doing this while avoiding globals would be to define a struct that represents your server, and its methods would be the handlers. Then the methods share the struct's data, and you place things like your mongo client in there.

Something like this (in your admin package):

type Server struct {
client *mongo.Client
}
func NewServer(client *mongo.Client) *Server {
return &Server{client: client}
}
func (srv *Server) CreateAdminEndpoint(response http.ResponseWriter, request *http.Request) {
// ...
// use srv.client here
//
}

And now in main, you create the server like this:

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

huangapple
  • 本文由 发表于 2021年6月6日 03:59:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/67853474.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定