router

1. router in golang

1). sample code

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "net/http"
 6 )
 7 
 8 func main() {
 9 
10     http.HandleFunc("/handlefunc", func(w http.ResponseWriter, r *http.Request) {
11         fmt.Fprintf(w, "http.HandleFunc")
12     })
13 
14     http.Handle("/handle", &myHandler{})
15 
16     http.ListenAndServe(":8080", nil)
17 }
18 
19 type myHandler struct{}
20 
21 func (this *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
22     fmt.Fprintf(w, "http.Handle")
23 }

 2). implement

a) top layer:

// Objects implementing the Handler interface can be
// registered to serve a particular path or subtree
// in the HTTP server.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return.  Returning signals that the request is finished
// and that the HTTP server can move on to the next request on
// the connection.
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers.  If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

Now we know that both of http.HandleFunc and http.Handle invoke function of DefaultServeMux.

b) DefaultServeMux

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} }

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = NewServeMux()
// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.
//
type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
    explicit bool
    h        Handler
    pattern  string
}
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern " + pattern)
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }

    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

    if pattern[0] != '/' {
        mux.hosts = true
    }

    // Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.
    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
        // If pattern contains a host name, strip it and use remaining
        // path for redirect.
        path := pattern
        if pattern[0] != '/' {
            // In pattern, at least the last character is a '/', so
            // strings.Index can't be -1.
            path = pattern[strings.Index(pattern, "/"):]
        }
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}
    }
}

2. rounter in beego

1) Get and Post router

(i) sample code

beego.Get("/",func(ctx *context.Context){
     ctx.Output.Body([]byte("hello world"))
})
beego.Post("/alice",func(ctx *context.Context){
     ctx.Output.Body([]byte("bob"))
})

(ii) implementation of Get router

(a) func Get(rootpath string, f FilterFunc) *App

// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)

// register router for Get method
// usage:
//    beego.Get("/", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func Get(rootpath string, f FilterFunc) *App {
    BeeApp.Handlers.Get(rootpath, f)
    return BeeApp
}

// create beego application
BeeApp = NewApp()

// NewApp returns a new beego application.
func NewApp() *App {
    cr := NewControllerRegister()
    app := &App{Handlers: cr, Server: &http.Server{}}
    return app
}


// App defines beego application with a new PatternServeMux.
type App struct {
    Handlers *ControllerRegistor
    Server   *http.Server
}

(b) ControllerRegistor

// ControllerRegistor containers registered router rules, controller handlers and filters.
type ControllerRegistor struct {
    routers      map[string]*Tree
    enableFilter bool
    filters      map[int][]*FilterRouter
}

// NewControllerRegister returns a new ControllerRegistor.
func NewControllerRegister() *ControllerRegistor {
    return &ControllerRegistor{
        routers: make(map[string]*Tree),
        filters: make(map[int][]*FilterRouter),
    }
}
// add get method
// usage:
//    Get("/", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) Get(pattern string, f FilterFunc) {
    p.AddMethod("get", pattern, f)
}
// add http method router
// usage:
//    AddMethod("get","/api/:id", func(ctx *context.Context){
//          ctx.Output.Body("hello world")
//    })
func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) {
    if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok {
        panic("not support http method: " + method)
    }
    route := &controllerInfo{}
    route.pattern = pattern
    route.routerType = routerTypeRESTFul
    route.runfunction = f
    methods := make(map[string]string)
    if method == "*" {
        for _, val := range HTTPMETHOD {
            methods[val] = val
        }
    } else {
        methods[strings.ToUpper(method)] = strings.ToUpper(method)
    }
    route.methods = methods
    for k, _ := range methods {
        if k == "*" {
            for _, m := range HTTPMETHOD {
                p.addToRouter(m, pattern, route)
            }
        } else {
            p.addToRouter(k, pattern, route)
        }
    }
}
func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) {
    if t, ok := p.routers[method]; ok {
        t.AddRouter(pattern, r)
    } else {
        t := NewTree()
        t.AddRouter(pattern, r)
        p.routers[method] = t
    }
}

appendix:

// supported http methods.
    HTTPMETHOD = map[string]string{
        "GET":     "GET",
        "POST":    "POST",
        "PUT":     "PUT",
        "DELETE":  "DELETE",
        "PATCH":   "PATCH",
        "OPTIONS": "OPTIONS",
        "HEAD":    "HEAD",
        "TRACE":   "TRACE",
        "CONNECT": "CONNECT",
    }
type controllerInfo struct {
    pattern        string
    controllerType reflect.Type
    methods        map[string]string
    handler        http.Handler
    runfunction    FilterFunc
    routerType     int
}

c) Tree

type Tree struct {
    //search fix route first
    fixrouters map[string]*Tree

    //if set, failure to match fixrouters search then search wildcard
    wildcard *Tree

    //if set, failure to match wildcard search
    leaves []*leafInfo
}

func NewTree() *Tree {
    return &Tree{
        fixrouters: make(map[string]*Tree),
    }
}

// add Tree to the exist Tree
// prefix should has no params
func (t *Tree) AddTree(prefix string, tree *Tree) {
    t.addtree(splitPath(prefix), tree, nil, "")
}
func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg string) {
    if len(segments) == 0 {
        panic("prefix should has path")
    }
    seg := segments[0]
    iswild, params, regexpStr := splitSegment(seg)
    if len(segments) == 1 {
        if iswild {
            if regexpStr != "" {
                if reg == "" {
                    rr := ""
                    for _, w := range wildcards {
                        if w == "." || w == ":" {
                            continue
                        }
                        if w == ":splat" {
                            rr = rr + "(.+)/"
                        } else {
                            rr = rr + "([^/]+)/"
                        }
                    }
                    regexpStr = rr + regexpStr
                } else {
                    regexpStr = "/" + regexpStr
                }
            } else {
                for _, w := range wildcards {
                    if w == "." || w == ":" {
                        continue
                    }
                    regexpStr = "([^/]+)/" + regexpStr
                }
            }
            reg = strings.Trim(reg+regexpStr, "/")
            filterTreeWithPrefix(tree, append(wildcards, params...), reg)
            t.wildcard = tree
        } else {
            filterTreeWithPrefix(tree, append(wildcards, params...), reg)
            t.fixrouters[seg] = tree
        }
        return
    }
    if iswild {
        if t.wildcard == nil {
            t.wildcard = NewTree()
        }
        if regexpStr != "" {
            if reg == "" {
                rr := ""
                for _, w := range wildcards {
                    if w == "." || w == ":" {
                        continue
                    }
                    if w == ":splat" {
                        rr = rr + "(.+)/"
                    } else {
                        rr = rr + "([^/]+)/"
                    }
                }
                regexpStr = rr + regexpStr + "/"
            } else {
                regexpStr = "/" + regexpStr + "/"
            }
        } else {
            for _, w := range wildcards {
                if w == "." || w == ":" {
                    continue
                }
                if w == ":splat" {
                    regexpStr = "(.+)/" + regexpStr
                } else {
                    regexpStr = "([^/]+)/" + regexpStr
                }
            }
        }
        reg = reg + regexpStr
        t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg)
    } else {
        subTree := NewTree()
        t.fixrouters[seg] = subTree
        subTree.addtree(segments[1:], tree, append(wildcards, params...), reg)
    }
}

I don't know what the Tree presents now.

I will figure it out later.

原文地址:https://www.cnblogs.com/harrysun/p/4079940.html