Golang笔记

一个应用级别的变量(全局变量)使用示例

一个应用级别的变量使用示例

type appContext struct {
  db *sql.DB
}

func (c *appContext) authHandler(next http.Handler) http.Handler {
  fn := func(w http.ResponseWriter, r *http.Request) {
    authToken := r.Header.Get("Authorization")
    user, err := getUser(c.db, authToken)

    if err != nil {
      http.Error(w, http.StatusText(401), 401)
      return
    }

    context.Set(r, "user", user)
    next.ServeHTTP(w, r)
  }

  return http.HandlerFunc(fn)
}

func (c *appContext) adminHandler(w http.ResponseWriter, r *http.Request) {
  user := context.Get(r, "user")
  // Maybe other operations on the database
  json.NewEncoder(w).Encode(user)
}

func main() {
  db := sql.Open("postgres", "...")
  appC := appContext{db}
  commonHandlers := alice.New(context.ClearHandler, loggingHandler, recoverHandler)
  http.Handle("/admin", commonHandlers.Append(appC.authHandler).ThenFunc(appC.adminHandler))
  http.Handle("/about", commonHandlers.ThenFunc(aboutHandler))
  http.Handle("/", commonHandlers.ThenFunc(indexHandler))
  http.ListenAndServe(":8080", nil)
}

参考文章

https://www.nicolasmerouze.com/share-values-between-middlewares-context-golang/ 3

https://joeshaw.org/net-context-and-http-handler/ 1

https://elithrar.github.io/article/custom-handlers-avoiding-globals/ 6

https://groups.google.com/forum/#!topic/golang-nuts/pCB81QylF6M 1

package main

import (
    "encoding/json"
    "fmt"
    "net"
    "net/http"
    "time"

    "golang.org/x/net/context"
)

type ContextHandler interface {
    ServeHTTPContext(context.Context, http.ResponseWriter, *http.Request) error
}

type ContextHandlerFunc func(context.Context, http.ResponseWriter, *http.Request) error

func (f ContextHandlerFunc) ServeHTTPContext(ctx context.Context, w http.ResponseWriter, req *http.Request) error {
    return f(ctx, w, req)
}

type ContextAdapter struct {
    ctx     context.Context
    handler ContextHandler // to wrap func(context.Context, ResponseWriter, *Request) error
}

func (ca *ContextAdapter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    if err := ca.handler.ServeHTTPContext(ca.ctx, w, req); err != nil {
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(map[string]string{
            "status":  "error",
            "message": err.Error(),
        })
    }
}

func NewRootContext(ts string) context.Context {
    return context.WithValue(context.Background(), appStartTSKey, ts)
}

func main() {
    rootContext := context.Background()
    mainRouter := http.NewServeMux()
    mainRouter.Handle("/", &ContextAdapter{
        ctx:     rootContext,
        handler: ContextHandlerFunc(handlerRoot),
    })
    mainRouter.Handle("/set", &ContextAdapter{
        ctx:     rootContext,
        handler: ContextHandlerFunc(handlerSet),
    })

    port := ":8080"
    fmt.Println("Serving http://localhost" + port)
    if err := http.ListenAndServe(port, mainRouter); err != nil {
        panic(err)
    }
}

func handlerRoot(ctx context.Context, w http.ResponseWriter, req *http.Request) error {
    switch req.Method {
    case "GET":
        fmt.Println("handlerRoot")
        ts, _ := getAppStartTSFromContext(ctx)
        ip, _ := getIPFromContext(ctx)
        ua, _ := getUserAgentFromContext(ctx)
        fmt.Fprintf(w, "Root = AppStartTS: %s / IP: %v / UserAgent: %s", ts, ip, ua)
        return nil

    default:
        http.Error(w, "Method Not Allowed", 405)
        return fmt.Errorf("Method Not Allowed:", req.Method)
    }
}

func handlerSet(ctx context.Context, w http.ResponseWriter, req *http.Request) error {
    switch req.Method {
    case "GET":
        fmt.Println("handlerSet")
        ts := time.Now().String()
        ip, err := getIP(req)
        if err != nil {
            return err
        }
        ua := req.UserAgent()
        ctx = setContextWithAppStartTS(ctx, ts)
        ctx = setContextWithIP(ctx, ip)
        ctx = setContextWithUserAgent(ctx, ua)
        fmt.Fprintf(w, "Set = AppStartTS: %s / IP: %v / UserAgent: %s", ts, ip, ua)
        return nil

    default:
        http.Error(w, "Method Not Allowed", 405)
        return fmt.Errorf("Method Not Allowed:", req.Method)
    }
}

type key int

const appStartTSKey key = 0

const userIPKey key = 1

const userAgentKey key = 2

func setContextWithAppStartTS(ctx context.Context, ts string) context.Context {
    return context.WithValue(ctx, appStartTSKey, ts)
}

func setContextWithIP(ctx context.Context, userIP net.IP) context.Context {
    return context.WithValue(ctx, userIPKey, userIP)
}

func setContextWithUserAgent(ctx context.Context, userAgent string) context.Context {
    return context.WithValue(ctx, userAgentKey, userAgent)
}

func getAppStartTSFromContext(ctx context.Context) (string, bool) {
    // ctx.Value returns nil if ctx has no value for the key;
    // the string type assertion returns ok=false for nil.
    ts, ok := ctx.Value(appStartTSKey).(string)
    return ts, ok
}

func getIPFromContext(ctx context.Context) (net.IP, bool) {
    userIP, ok := ctx.Value(userIPKey).(net.IP)
    return userIP, ok
}

func getUserAgentFromContext(ctx context.Context) (string, bool) {
    userAgent, ok := ctx.Value(userAgentKey).(string)
    return userAgent, ok
}

// getIP extracts the user IP address from req, if present.
// https://blog.golang.org/context/userip/userip.go
func getIP(req *http.Request) (net.IP, error) {
    ip, _, err := net.SplitHostPort(req.RemoteAddr)
    if err != nil {
        return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
    }
    userIP := net.ParseIP(ip)
    if userIP == nil {
        return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
    }
    return userIP, nil
}
本文网址: https://golangnote.com/topic/123.html (转载注明出处)
关于GolangNote:记录在工作中使用golang 遇到、面临的相关问题及解决方法。如果你在这里获得一些知识或信息,解决你的编程问题,请考虑捐赠给不幸的人或者你喜欢的慈善机构,除捐赠外,种植树木、志愿服务或减少排碳的行为也很有益处。如果你有任何问题可以在下面 留言
Be the first to comment!
Captcha image
Relative Articles