zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    Neko's loop HDU-6444(网络赛1007)
    Parameters
    SETLOCAL
    RD / RMDIR Command
    devenv 命令用法
    Cannot determine the location of the VS Common Tools folder.
    'DEVENV' is not recognized as an internal or external command,
    How to change Visual Studio default environment setting
    error signing assembly unknown error
    What is the Xcopy Command?:
  • 原文地址:https://www.cnblogs.com/harrysun/p/4079940.html
Copyright © 2011-2022 走看看