Gorilla/mux Class Library parsing

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Golang comes with the HTTP. Severmux routing implementation is simple, the essence is a map[string]handler, is the request path and the path corresponding to the processing function mapping relationship. Simple functionality is also relatively single:

    1. Regular routing is not supported, this is more deadly
    2. Only path matching is supported, and information matching such as Method,header,host is not supported, so it is not possible to implement a restful architecture

And Gorilla/mux is a powerful route, small but stable and efficient, not only can support the regular route can also be matched according to Method,header,host and other information, can be extracted from the routing expression we set up the parameters for the upper-level application, and fully compatible with HTTP. Servermux

Using the example

r := mux.NewRouter()   //与http.ServerMux不同的是mux.Router是完全的正则匹配,设置路由路径/index/,如果访问路径/idenx/hello会返回404//设置路由路径为/index/访问路径/index也是会报404的,需要设置r.StrictSlash(true), /index/与/index才能匹配 r.HandleFunc("/index/", func(w http.ResponseWriter, r *http.Request) {    w.Write([]byte("root path"))})    //mux.Vars(r)会返回该请求所解析出的所有参数(map[string]string)//访问/hello/ghbai 会输出 hello ghbai r.HandleFunc("/hello/{name:[a-zA-Z]+}", func(w http.ResponseWriter, r *http.Request) {    w.Write([]byte(fmt.Sprintf("hello %s", mux.Vars(r)["name"])))})    http.Handle("/", r)

Source Code Implementation

The realization of router

The routing information is stored in an array of route types ([]route), and each route object in the array represents a route information that contains all the conditions that the route should meet and the corresponding upper processing hanlder. When the request arrives, router iterates through the route array, finds the first matching route, executes the corresponding handler, and executes Notfoundhandler if it is not found.

type Router struct {    routes []*Route}// Match matches registered routes against the request.func (r *Router) Match(req *http.Request, match *RouteMatch) bool {    for _, route := range r.routes {        //Route.Match会检查http.Request是否满足其设定的各种条件(路径,Header,Host..)        if route.Match(req, match) {            return true        }    }    return false}func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {    var match RouteMatch    var handler http.Handler    if r.Match(req, &match) {        handler = match.Handler    }    if handler == nil {        handler = http.NotFoundHandler()    }    handler.ServeHTTP(w, req)}

The implementation of the route

The implementation of the route is also relatively simple, the resolution of the regular expression is not very good understanding, the implementation of Subrouter is a need to study the code, but these to understand the design of the route to achieve little impact. Each route contains a Matcher array, which is a collection of all the qualifying conditions, and Matcher is an interface that returns a bool value.

When we add a routing qualification, we are adding a qualifying function to the Matcher array. When the request arrives, Route.match () iterates through the Matcher array, stating that the request satisfies the qualification of the route only if all elements in the array return True .

Suppose we specify only get access to/user/{userid:[0-9]+} and the header must contain "Refer": "example.com" in order to get the results we want we can set the route

func userHandler(w http.ResponseWriter,r* http.Request) {    w.write([]byte(fmt.Sprintf("user %s visited",mux.Vars(r)["userid"])))}r.HandleFunc("/user/{userid:[0-9]+}", userHandler).Methods("GET").Headers("Refer", "example.com")

And then we'll look at how the route saves the three qualifying conditions.

Type route struct {//Request handler for the route. Handler http.    Handler//List of matchers. matchers []matcher}//Add header qualification, the requested header must contain "Refer", the value is "example.com" func (R *route) Headers (Pairs ... string) * Route {if R.err = nil {var headers map[string]string//mapfrompairs Returns a map[string][string]{"Refer": "Ex        Ample.com "} headers, R.err = Mapfrompairs (Pairs ...) Return R.addmatcher (Headermatcher (Headers))} return R}type Headermatcher Map[string]stringfunc (M headermatcher) Ma TCH (R *http. Request, match *routematch) bool {//matchmap will determine if the R.header contains "Refer", and the value is "example.com" return Matchmap (M, R.header, True)}//methodmatcher is to remove r.method and then determine if the mode is set Methodtype methodmatcher []stringfunc (M Methodmatcher) Match (R *http.     Request, match *routematch) bool {return Matchinarray (M, R.method)}func (R *route) Methods (Methods ... string) *route { For k, V: = Range methods {Methods[k] = strings. ToUpper (v)} return R.addMatcher (Methodmatcher (methods))}//with regular expression path matching is a more complex TPL is/user/{userid:[0-9]+}func (r *route) path (TPL string) * Route {r.err = R.addregexpmatcher (TPL, False, False, false) return R}func (R *route) Addregexpmatcher (TPL string,st         rictslash bool) Error {//braceindices determines if {} is paired and appears correctly, Idxs is ' {'} ' subscript array in the expression TPL idxs, errbraces: = Braceindices (TPL)     Template: = TPL Defaultpattern: = "[^/]+"//Save all variable names required for extraction, this example is userid VARSN: = make ([]string, Len (IDXS)/2) var end int//end is now 0 pattern: = bytes. Newbufferstring ("") for I: = 0; I < Len (IDXS); i + = 2 {raw: = Tpl[end:idxs[i]]//raw= "/user/" end = idxs[i+1] Parts: = Strings.  SPLITN (Tpl[idxs[i]+1:end-1], ":", 2)//parts=[]{"userid", "[0-9]+"} Name: = Parts[0]//name= "userid" Patt: = Defaultpattern if len (parts) = = 2 {Patt = parts[1]//patt= "[0-9]+"}//constructs the final regular expression/usr/ ([0-9]+) fmt. fprintf (Pattern, "%s (%s)", RegExp. Quotemeta (Raw), Patt)       VARSN[I/2] = name//Save the parameter name UserID to be extracted to VARSN}//if there are other regular expressions that continue to traverse raw: = Tpl[end:] pattern. WriteString (RegExp. Quotemeta (Raw)) if Strictslash {pattern.    WriteString ("[/]?") }//Compile the final regular expression Reg, Errcompile: = Regexp.compile (pattern.    String ()) rr = &routeregexp{template:template, Regexp:reg, VARSN:VARSN, } r.addmatcher (RR)}func (R *routeregexp) Match (req *http. Request, match *routematch) bool {return r.regexp.matchstring (GetHost (req))}

Context contexts

The above three qualification is how to implement has been analyzed, the final path matching regular expression is/user/([0-9]+), the parameter name "userid" is saved in the VARSN array, when the regular match to extract the value of the parameter in the regular expression, and the VARSN array with the name of the parameter is associated , create a map[string][string]{"userid": "123456"}

var Vars map[string]stringpathVars := regexp.FindStringSubmatch(req.URL.Path)if pathVars != nil {    for k, v := range varsN {        Vars[v] = pathVars[k+1]    }}

Because Gorilla/mux is selected with HTTP. The interface of the SERVERMUX is consistent, so the processing function of the upper application becomes a fixed hanlder

type Handler interface {    ServeHTTP(ResponseWriter, *Request)}

How can the parameter VARs parsed by the regular match be passed to the upper processing function? Gorilla/mux uses a third-party module, Gorilla/context. MUX when the HTTP request arrives. Router will select the appropriate route and extract some parameter information with the HTTP. The request object establishes a mapping relationship in Gorilla/context, and the upper processing function is based on HTTP. The request object to the context to find the corresponding parameter information for the http.request.

The context is implemented as follows

var data  = make(map[*http.Request]map[interface{}]interface{}) func Set(r *http.Request, key, val interface{}) {    mutex.Lock()    if data[r] == nil {        data[r] = make(map[interface{}]interface{})        datat[r] = time.Now().Unix()    }    data[r][key] = val    mutex.Unlock()}func Get(r *http.Request, key interface{}) interface{} {    mutex.RLock()    if ctx := data[r]; ctx != nil {        value := ctx[key]        mutex.RUnlock()        return value    }    mutex.RUnlock()    return nil}

The MUX is called in the upper processing function. Vars (R) can remove the HTTP. Parameter information associated with request

//val实际上时一个map[string][string],存放该请求对应的变量值集合func setVars(r *http.Request, val interface{}) {    context.Set(r, varsKey, val)}func Vars(r *http.Request) map[string]string {    if rv := context.Get(r, varsKey); rv != nil {        //类型转换,如果失败直接panic        return rv.(map[string]string)    }    return nil}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.