duanchuaiwan0063 2016-02-17 03:42
浏览 38
已采纳

将JSON解码为接口值

As encoding/json needs a non-nil interface to unmarshal into: how can I reliably make a (full) copy of a user-provided pointer type, store that in my User interface, and then JSON decode into that ad-hoc?

Note: the goal here is to do this 'unattended' - that is, pull the bytes from Redis/BoltDB, decode into the interface type, and then check that the GetID() method the interface defines returns a non-empty string, with request middleware.

Playground: http://play.golang.org/p/rYODiNrfWw

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"

    "time"
)

type session struct {
    ID      string
    User    User
    Expires int64
}

type User interface {
    GetID() string
}

type LocalUser struct {
    ID      string
    Name    string
    Created time.Time
}

func (u *LocalUser) GetID() string {
    return u.ID
}

type Auth struct {
    key []byte
    // We store an instance of userType here so we can unmarshal into it when
    // deserializing from JSON (or other non-gob decoders) into *session.User.
    // Attempting to unmarshal into a nil session.User would otherwise fail.
    // We do this so we can unmarshal from our data-store per-request *without
    // the package user having to do so manually* in the HTTP middleware. We can't
    // rely on the user passing in an fresh instance of their User satisfying type.
    userType User
}

func main() {
    // auth is a pointer to avoid copying the struct per-request: although small
    // here, it contains a 32-byte key, options fields, etc. outside of this example.
    var auth = &Auth{key: []byte("abc")}
    local := &LocalUser{"19313", "Matt", time.Now()}

    b, _, _, err := auth.SetUser(local)
    if err != nil {
        log.Fatalf("SetUser: %v", err)
    }

    user, err := auth.GetUser(b)
    if err != nil {
        log.Fatalf("GetUser: %#v", err)
    }

    fmt.Fprintf(os.Stdout, "%v
", user)

}

func (auth *Auth) SetUser(user User) (buf []byte, id string, expires int64, err error) {
    sess := newSession(user)

    // Shallow copy the user into our config. struct so we can copy and then unmarshal
    // into it in our middleware without requiring the user to provide it themselves
    // at the start of every request
    auth.userType = user

    b := bytes.NewBuffer(make([]byte, 0))
    err = json.NewEncoder(b).Encode(sess)
    if err != nil {
        return nil, id, expires, err
    }

    return b.Bytes(), sess.ID, sess.Expires, err
}

func (auth *Auth) GetUser(b []byte) (User, error) {
    sess := &session{}

    // Another shallow copy, which means we're re-using the same auth.userType
    // across requests (bad).
    // Also note that we need to copy into it session.User so that encoding/json
    // can unmarshal into its fields.
    sess.User = auth.userType

    err := json.NewDecoder(bytes.NewBuffer(b)).Decode(sess)
    if err != nil {
        return nil, err
    }

    return sess.User, err
}

func (auth *Auth) RequireAuth(h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        // e.g. user, err := store.Get(r, auth.store, auth.userType)
        // This would require us to have a fresh instance of userType to unmarshal into
        // on each request.

        // Alternative might be to have:
        // func (auth *Auth) RequireAuth(userType User) func(h http.Handler) http.Handler
        // e.g. called like http.Handle("/monitor", RequireAuth(&LocalUser{})(SomeHandler)
        // ... which is clunky and using closures like that is uncommon/non-obvious.
    }

    return http.HandlerFunc(fn)
}

func newSession(u User) *session {
    return &session{
        ID:      "12345",
        User:    u,
        Expires: time.Now().Unix() + 3600,
    }
}
  • 写回答

2条回答 默认 最新

  • duanshan3331 2016-02-17 14:57
    关注

    If you need to deep copy an interface, add that method to your interface.

    type User interface {
      GetID() string
      Copy() User
    }
    
    type LocalUser struct {
      ID string
      Name string
      Created time.Time
    }
    
    // Copy receives a copy of LocalUser and returns a pointer to it.
    func (u LocalUser) Copy() User {
      return &u
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000
  • ¥30 ppOCRLabel导出识别结果失败
  • ¥15 Centos7 / PETGEM
  • ¥15 csmar数据进行spss描述性统计分析
  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题
  • ¥15 wpf界面一直接收PLC给过来的信号,导致UI界面操作起来会卡顿
  • ¥15 init i2c:2 freq:100000[MAIXPY]: find ov2640[MAIXPY]: find ov sensor是main文件哪里有问题吗