简体   繁体   中英

save session in mongodb golang

I m using golang in the back end and mongodb is my database. I wanted to store the user session(betwen login and logout )of my web application in the mongodb for the persistence.Since there is provider available only for mysql not for mongodb,I edited it to support mongodb.But when i try to use it im getting the invalid memory address or nil pointer dereference. The code is as follows and please if there is any better way to code please let me know.Thanks

type (
    SessionStore struct {
        c      *mgo.Session
        sid    string
        lock   sync.RWMutex
        values map[interface{}]interface{}
    }
)

var mgopder = &Provider{}

func (st *SessionStore) Set(key, value interface{}) error {
    st.lock.Lock()
    defer st.lock.Unlock()
    st.values[key] = value
    return nil
}

// Get value from mongodb session
func (st *SessionStore) Get(key interface{}) interface{} {
    st.lock.RLock()
    defer st.lock.RUnlock()
    if v, ok := st.values[key]; ok {
        return v
    }
    return nil
}

// Delete value in mongodb session
func (st *SessionStore) Delete(key interface{}) error {
    st.lock.Lock()
    defer st.lock.Unlock()
    delete(st.values, key)
    return nil
}

// Flush clear all values in mongodb session
func (st *SessionStore) Flush() error {
    st.lock.Lock()
    defer st.lock.Unlock()
    st.values = make(map[interface{}]interface{})
    return nil
}

// SessionID get session id of this mongodb session store
func (st *SessionStore) SessionID() string {
    return st.sid
}

// SessionRelease save mongodb session values to database.
// must call this method to save values to database.
func (st *SessionStore) SessionRelease(w http.ResponseWriter) {
    defer st.c.Close()
    b, err := session.EncodeGob(st.values)
    if err != nil {
        return
    }
    st.c.DB("Employee").C("Sessions").Update(nil, bson.M{"$set": bson.M{
        "session_data":   b,
        "session_expiry": time.Now().Unix(),
        "session_key":    st.sid,
    },
    },
    )

    /*st.c.Exec("UPDATE "+TableName+" set `session_data`=?, `session_expiry`=? where session_key=?",
    b, time.Now().Unix(), st.sid)*/
}

type Provider struct {
    maxlifetime int64
    savePath    string
    Database    string
}

// connect to mongodb
func (mp *Provider) connectInit() *mgo.Session {
    ds, err := mgo.Dial("Employee")
    if err != nil {
        return nil
    }
    return ds
}

// SessionInit init mongodb session.
// savepath is the connection string of mongodb
func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error {
    mp.maxlifetime = maxlifetime
    mp.savePath = savePath
    mp.Database = "Employee"
    return nil
}

// SessionRead get mysql session by sid
func (mp *Provider) SessionRead(sid string) (session.Store, error) {
    var sessiondata []byte
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB(mp.Database).C("Session")
    err := c.Find(bson.M{
        "session_key": sid,
    }).Select(bson.M{"session_data": 1}).All(&sessiondata)
    if err != nil {
        if err.Error() == "not found" {
            c.Insert(bson.M{
                "session_key":    sid,
                "session_data":   " ",
                "session_expiry": time.Now().Unix(),
            })
        }
    }

    var kv map[interface{}]interface{}
    if len(sessiondata) == 0 {
        kv = make(map[interface{}]interface{})
    } else {
        kv, err = session.DecodeGob(sessiondata)
        if err != nil {
            return nil, err
        }
    }
    rs := &SessionStore{c: ds, sid: sid, values: kv}
    return rs, nil
}

// SessionExist check mongodb session exist
func (mp *Provider) SessionExist(sid string) bool {
    var sessiondata []byte
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    err := c.Find(bson.M{
        "session_key": sid,
    }).Select(bson.M{
        "session_data": 1,
    }).One(&sessiondata)
    if err != nil {
        if err.Error() == "not found" {
            return false
        }
    }
    return true

}

// SessionRegenerate generate new sid for mysql session
func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) {
    var sessiondata []byte
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    err := c.Find(bson.M{
        "session_key": oldsid,
    }).Select(bson.M{
        "session_data": 1,
    }).One(&sessiondata)
    if err != nil {
        if err.Error() == "not found" {
            c.Insert(bson.M{
                "sessoin_key":    oldsid,
                "session_data":   " ",
                "session_expiry": time.Now().Unix(),
            })
        }
    }
    /*  row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid)

        err := row.Scan(&sessiondata)

    c.Update(bson.M{"sessoin_key": oldsid}, bson.M{
        "$set": bson.M{
            "session_key": sid,
        },
    })
    /*c.Exec("update "+TableName+" set `session_key`=? where session_key=?", sid, oldsid)
     */
    var kv map[interface{}]interface{}
    if len(sessiondata) == 0 {
        kv = make(map[interface{}]interface{})
    } else {
        kv, err = session.DecodeGob(sessiondata)
        if err != nil {
            return nil, err
        }
    }
    rs := &SessionStore{c: ds, sid: sid, values: kv}
    return rs, nil
}

// SessionDestroy delete mysql session by sid
func (mp *Provider) SessionDestroy(sid string) error {
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    c.Remove(bson.M{
        "session_key": sid,
    }) 
    return nil
}

// SessionGC delete expired values in mysql session
func (mp *Provider) SessionGC() {
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    c.Remove(bson.M{
        "session_expiry": bson.M{
            "$lt": time.Now().Unix() - mp.maxlifetime,
        },
    })
return
}

// SessionAll count values in mysql session
func (mp *Provider) SessionAll() int {
    var total int
    ds := mp.connectInit()
    defer ds.Close()
    c := ds.DB("Employee").C("Sessions")
    total, err := c.Count()

    if err != nil {
        return 0
    }
    return total
}

func init() {
    session.Register("mongodb", mgopder)
}

Error:

panic: runtime error: invalid memory address or nil pointer dereference
        panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x6db254]

goroutine 6 [running]:
panic(0xa2f560, 0xc0820080b0)
        C:/Go/src/runtime/panic.go:481 +0x3f4
gopkg.in/mgo%2ev2.(*Session).Close(0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:1612 +0x144
panic(0xa2f560, 0xc0820080b0)
        C:/Go/src/runtime/panic.go:443 +0x4f7
gopkg.in/mgo%2ev2.(*Session).acquireSocket(0x0, 0xc082290000, 0x0, 0x0, 0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:4409 +0x4ba
gopkg.in/mgo%2ev2.(*Collection).writeOp(0xc082279f30, 0x8feb80, 0xc082326060, 0xc082326001, 0x0, 0x0, 0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:4604 +0xe7
gopkg.in/mgo%2ev2.(*Collection).Remove(0xc082279f30, 0x9d4700, 0xc082326030, 0x0, 0x0)
        C:/Projects/Go/src/gopkg.in/mgo.v2/session.go:2586 +0x15c
sample/models.(*Provider).SessionGC(0xe2f5a0)
        C:/Projects/Go/src/sample/models/model.go:234 +0x3dc
github.com/astaxie/beego/session.(*Manager).GC(0xc082258b20)
        C:/Projects/Go/src/github.com/astaxie/beego/session/session.go:271 +0x48
created by github.com/astaxie/beego.registerSession
        C:/Projects/Go/src/github.com/astaxie/beego/hooks.go:68 +0x31d

This is what i normally do.

package mongo

import (
    "time"

    "gopkg.in/mgo.v2"
)

// DataStore containing a pointer to a mgo session
type DataStore struct {
    Session *mgo.Session
}

// ConnectToTagserver is a helper method that connections to pubgears' tagserver
// database
func (ds *DataStore) ConnectToTagserver() {
    mongoDBDialInfo := &mgo.DialInfo{
        Addrs:    []string{"some IP"},
        Timeout:  60 * time.Second,
        Database: "some db",
    }
    sess, err := mgo.DialWithInfo(mongoDBDialInfo)
    if err != nil {
        panic(err)
    }
    sess.SetMode(mgo.Monotonic, true)
    ds.Session = sess
}

// Close is a helper method that ensures the session is properly terminated
func (ds *DataStore) Close() {
    ds.Session.Close()
}

Then in my models package I do something like this

package models

import (
    "./mongo"
    "gopkg.in/mgo.v2/bson"
)

// AdSize represents the data object stucture that is returned by querying
// mongo's account collection
type AdSize struct {
    ID        bson.ObjectId `bson:"_id,omitempty"`
    Providers []string      `bson:"providers"`
    Size      string        `bson:"size"`
}

// GetAllAdsizes is a helper function designed to retrieve all the objects in the
// adsize collection
func GetAllAdsizes() ([]AdSize, error) {
    ds := mongo.DataStore{}
    ds.ConnectToTagserver()
    defer ds.Close()
    adSizes := []AdSize{}
    adSizeCollection := ds.Session.DB("some database").C("some collection")
    err := adSizeCollection.Find(bson.M{}).Sort("name").All(&adSizes)
    return adSizes, err
}

So I created a session wrapper in the mongo file, then create a session object in the models file, then lastly in some route file i call the method GetAllAdsizes(), which handled my mongo session. The session is keep alive until the the end of the GetAllAdsizes() method, as it was closed on defer. However something like this can be modified, where you handle all the user stuff and then close the session if user logs out. Also take a look here Best practice to maintain a mgo session , where you can see a similar type of logic.

Not sure about your code, here is a sample of a package file that create a session to mongodb in go and used it.

package mydb

import (
   "fmt"
   "os"

   "gopkg.in/mgo.v2"
   "gopkg.in/mgo.v2/bson"
)

var Db *mgo.Session

type DbLink struct {
     DbName string
}

func NewDbSession(url string) {
    var e error

    Db, e = mgo.Dial(url)

    if e != nil {
        panic(e)
        os.Exit(-1)
    }
}

Then in your main package:

package main

import (
    "fmt"
    "os"

    "mydb"
)

func main() {
    // <user> the user to access the db
    // <pwd> the database user password
    // <host> hostname or ip address of the database server (localhost i.e.)
    // <port> the port where the database server is listening (usually 27016).
    mydb.NewDbSession("mongodb://<user>:<pwd>@<host>:<port>/?authSource=<db_name>")

    session := mydb.Db.Copy()
    defer session.Close()

    col := session.DB("my_db_schema").C("my_collection_name")

    // row is a list of struct depends on collection
    err := col.Find(bson.M{"field_name": "value"}).All(&row)
    if err != nil {
        fmt.Printf("%s\n", err)
    }

}

This is a very trivial example that should help you to start.

Consider that MongoDB it's very different from MySQL, as it is a NoSQL and schema less database management system.

Please refer to the documentation of the mgo package for details.

Your user session struct have some issues:

type SessionStore struct {
   c      *mgo.Session
   sid    string
   lock   sync.RWMutex
   values map[interface{}]interface{}
}

First you have to use capital letters to export the key.

For example instead of c you should use C , and so on with Sid , Lock and Values .

type SessionStore struct {
   C      *mgo.Session                `json:"C" bson:"c"`
   Sid    string                      `json:"Sid" bson:"sid,omitempty"`
   Lock   sync.RWMutex                `json:"Lock" bson:"lock,omitempty"`
   Values map[interface{}]interface{} `json:"Values" bson:"values,omitempty"`
}

I don't think that is going to work with pointers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM