# web

\[TOC]

## 0. 基于net/http搭建Http服务

```go
package main

import (
    "net/http"
)

func main() {
    // 注册路由/hello并绑定处理函数
    http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {
        writer.WriteHeader(http.StatusOK)
        writer.Write([]byte("hello world"))
    })
    // 启动HTTP服务并监听在端口8888，未指定Handler，使用默认mux
    //DefaultServeMux is the default ServeMux used by Serve.
    //var DefaultServeMux = &defaultServeMux
    http.ListenAndServe(":8888", nil)
}

/*
1. 请求
    curl http://127.0.0.1:8888/hello
1. 返回
    hello world

2. 请求
   curl http://127.0.0.1:8888/hell 
2. 返回
   404 page not found
*/
```

## 1. 自定义Handler Mux

```go
package main

import (
    "fmt"
    "net/http"
)

// Engine 自定义mux
type Engine struct{}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    switch r.URL.Path {
    case "/hello":
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("hello world"))
    default:
        fmt.Fprintf(w, "404 NOT FOUND: %s\n", r.URL)
    }
}

func main() {

    // 启动HTTP服务并监听在端口8888，使用自定义mux
    http.ListenAndServe(":8888", &Engine{})

}

/*
1. 请求
     curl http://127.0.0.1:8888/hello
1. 返回
   hello world

2. 请求
   curl http://127.0.0.1:8888/hell 
2. 返回
   404 NOT FOUND: /hell
*/
```

## 2. 静态路由

```go
### package main

import (
    "fmt"
    "net/http"
)

func main() {
    var e = New()
    e.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("hello world"))
    })
    e.Run(":8888")
}

type Method string

const (
    MethodGET  Method = http.MethodGet
    MethodPOST Method = http.MethodPost
)

type Engine struct {
    router
}

type HandlerFunc func(http.ResponseWriter, *http.Request)

func New() *Engine {
    return &Engine{router: map[string]HandlerFunc{}}
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if handle, has := e.router[genRouterKey(Method(r.Method), r.URL.Path)]; has {
        handle(w, r)
        return
    }
    fmt.Fprintf(w, "404 NOT FOUND: %s\n", r.URL)
}

func (e *Engine) Run(addr string) error {
    return http.ListenAndServe(addr, e)
}

type router map[string]HandlerFunc

func (r router) addRouter(method Method, path string, handle HandlerFunc) {
    r[genRouterKey(method, path)] = handle
}

func genRouterKey(method Method, path string) string {
    return string(method) + "-" + path
}

func (r router) Get(path string, handle HandlerFunc) {
    r.addRouter(MethodGET, path, handle)
}
func (r router) Post(path string, handle HandlerFunc) {
    r.addRouter(MethodPOST, path, handle)
}


/*
1. 请求
     curl http://127.0.0.1:8888/hello
1. 返回
   hello world

2. 请求
   curl http://127.0.0.1:8888/hell 
2. 返回
   404 NOT FOUND: /hell
*/
```

## 3. 上下文 快速构造HTTP响应

```go
package main

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

func main() {
    var e = New()
    e.Get("/hello", func(c *Context) {
        c.String(http.StatusOK, "hello world")
    })
    e.Run(":8888")
}

type Context struct {
    // origin
    r *http.Request
    w http.ResponseWriter
}

func NewContext(w http.ResponseWriter, req *http.Request) *Context {
    return &Context{
        w: w,
        r: req,
    }
}

func (c *Context) PostForm(key string) string {
    return c.r.FormValue(key)
}

func (c *Context) Query(key string) string {
    return c.r.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
    c.w.WriteHeader(code)
}

func (c *Context) SetHeader(key string, value string) {
    c.w.Header().Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
    c.SetHeader("Content-Type", "text/plain")
    c.Status(code)
    c.w.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
    c.SetHeader("Content-Type", "application/json")
    c.Status(code)
    encoder := json.NewEncoder(c.w)
    if err := encoder.Encode(obj); err != nil {
        http.Error(c.w, err.Error(), 500)
    }
}

func (c *Context) Data(code int, data []byte) {
    c.Status(code)
    c.w.Write(data)
}

func (c *Context) HTML(code int, html string) {
    c.SetHeader("Content-Type", "text/html")
    c.Status(code)
    c.w.Write([]byte(html))
}

type Method string

const (
    MethodGET  Method = http.MethodGet
    MethodPOST Method = http.MethodPost
)

type Engine struct {
    router
}

type HandlerFunc func(c *Context)

func New() *Engine {
    return &Engine{router: map[string]HandlerFunc{}}
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if handle, has := e.router[genRouterKey(Method(r.Method), r.URL.Path)]; has {
        handle(NewContext(w, r))
        return
    }
    fmt.Fprintf(w, "404 NOT FOUND: %s\n", r.URL)
}

func (e *Engine) Run(addr string) error {
    return http.ListenAndServe(addr, e)
}

type router map[string]HandlerFunc

func (r router) addRouter(method Method, path string, handle HandlerFunc) {
    r[genRouterKey(method, path)] = handle
}

func genRouterKey(method Method, path string) string {
    return string(method) + "-" + path
}

func (r router) Get(path string, handle HandlerFunc) {
    r.addRouter(MethodGET, path, handle)
}
func (r router) Post(path string, handle HandlerFunc) {
    r.addRouter(MethodPOST, path, handle)
}
```

## 4. 动态路由

```go
### package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strings"
)

func main() {
    var e = New()
    e.Get("/hello", func(c *Context) {
        c.String(http.StatusOK, "hello world")
    })
    e.Get("/hi/:name", func(c *Context) {
        c.String(http.StatusOK, "hi, %s", c.params["name"])
    })
    e.Get("/hi/:name/info/:xx/", func(c *Context) {
        c.String(http.StatusOK, "hi, %s, xx: %s", c.params["name"], c.params["xx"])
    })
    e.Get("/file/*filename", func(c *Context) {
        c.String(http.StatusOK, "file: %s", c.params["filename"])
    })
    e.Run(":8888")
}

/* Context */

type Context struct {
    // origin
    r *http.Request
    w http.ResponseWriter
    // params
    params map[string]string
}

func NewContext(w http.ResponseWriter, req *http.Request) *Context {
    return &Context{
        w: w,
        r: req,
    }
}

func (c *Context) PostForm(key string) string {
    return c.r.FormValue(key)
}

func (c *Context) Query(key string) string {
    return c.r.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
    c.w.WriteHeader(code)
}

func (c *Context) SetHeader(key string, value string) {
    c.w.Header().Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
    c.SetHeader("Content-Type", "text/plain")
    c.Status(code)
    c.w.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
    c.SetHeader("Content-Type", "application/json")
    c.Status(code)
    encoder := json.NewEncoder(c.w)
    if err := encoder.Encode(obj); err != nil {
        http.Error(c.w, err.Error(), 500)
    }
}

func (c *Context) Data(code int, data []byte) {
    c.Status(code)
    c.w.Write(data)
}

func (c *Context) HTML(code int, html string) {
    c.SetHeader("Content-Type", "text/html")
    c.Status(code)
    c.w.Write([]byte(html))
}

/*Engine*/

type Method string

const (
    MethodGET  Method = http.MethodGet
    MethodPOST Method = http.MethodPost
)

type Engine struct {
    router
}

type HandlerFunc func(c *Context)

func New() *Engine {
    return &Engine{router: router{roots: map[Method]*node{}, handleMap: map[string]HandlerFunc{}}}
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if root, has := e.roots[Method(r.Method)]; has {
        path, params := root.search(r.URL.Path)
        if handler, ok := e.handleMap[genRouterKey(Method(r.Method), path)]; ok {
            var ctx = NewContext(w, r)
            ctx.params = params
            handler(ctx)
        }
        return
    }
    fmt.Fprintf(w, "404 NOT FOUND: %s\n", r.URL)
}

func (e *Engine) Run(addr string) error {
    return http.ListenAndServe(addr, e)
}

/* router */

type router struct {
    roots     map[Method]*node
    handleMap map[string]HandlerFunc
}

func (r router) addRouter(method Method, path string, handle HandlerFunc) {
    var (
        root *node
        has  bool
    )
    if root, has = r.roots[method]; !has {
        root = newTrie()
        r.roots[method] = root
    }
    root.insert(path)
    // 绑定
    r.handleMap[genRouterKey(method, path)] = handle
}

func genRouterKey(method Method, path string) string {
    return string(method) + "-" + path
}

func (r router) Get(path string, handle HandlerFunc) {
    r.addRouter(MethodGET, path, handle)
}
func (r router) Post(path string, handle HandlerFunc) {
    r.addRouter(MethodPOST, path, handle)
}

/* trie */

const (
    slash             = "/"
    asteriskByte byte = '*'
    colonByte    byte = ':'
    codeMe            = 20210607
    codeNotFound      = -404
)

var (
    code      int32 = codeMe
    pattenMap       = make(map[int32]string)
)

func codeIncr() int32 {
    code++
    return code
}

func getCode() int32 {
    return code
}

type node struct {
    code     int32
    wild     byte
    children map[string]*node
    param    string
}

func newTrie() *node {
    return &node{children: map[string]*node{}}
}

func (n *node) search(path string) (string, map[string]string) {
    var (
        parts  = parsePattern(path)
        params = make(map[string]string)
        // curNode -> root
        // part[0]
        code = n.match(n, parts, 0, params)
    )
    log.Printf("pattenMap: %+v", pattenMap)
    log.Printf("code: %d", code)
    return pattenMap[code], params
}

func (n *node) match(curNode *node, parts []string, idx int, params map[string]string) int32 {
    if idx == len(parts) {
        // 所有part都匹配过了
        if curNode == nil {
            log.Printf("curNode is nil")
            // 未匹配到规则
            return codeNotFound
        }
        // 校验最后一位
        if curNode.isWildChild(curNode.wild) {
            params[curNode.param] = parts[idx-1]
        }
        return curNode.code
    }
    log.Printf("parts[i]: %s", parts[idx])
    if curNode.wild == asteriskByte {
        log.Printf("find params: %s, value: %s", params[curNode.param], parts[idx])
        params[curNode.param] = strings.Join(parts[idx-1:], slash)
        // 匹配到通配符'*'规则
        return curNode.code
    }
    if curNode.wild == colonByte {
        log.Printf("find params: %s, value: %s", params[curNode.param], parts[idx])
        params[curNode.param] = parts[idx-1]
    }
    // 字符串完全匹配
    if _, has := curNode.children[parts[idx]]; !has {
        var tNode = curNode.getNode()
        if tNode == nil {
            return codeNotFound
        }
        return curNode.match(tNode, parts, idx+1, params)
    }
    return n.match(curNode.children[parts[idx]], parts, idx+1, params)
}

func (n *node) getNode() *node {
    var (
        tCode = getCode() + 1
        ans   *node
    )
    for k := range n.children {
        if !n.isWildChild(n.children[k].wild) {
            continue
        }
        if n.children[k].code < tCode {
            ans = n.children[k]
            tCode = ans.code
        }
    }
    return ans
}

func (n *node) insert(pattern string) {
    var (
        parts   = parsePattern(pattern)
        curNode = n
    )
    log.Printf("pattern: %s, parts: %v", pattern, parts)
    for i := 0; i < len(parts); i++ {
        log.Printf("parts[i]: %s", parts[i])
        if _, has := curNode.children[parts[i]]; !has {
            curNode.children[parts[i]] = &node{children: map[string]*node{}, code: codeIncr()}
        }
        curNode = curNode.children[parts[i]]
        if n.isWildChild(parts[i][0]) {
            curNode.wild = parts[i][0]
            curNode.param = parts[i][1:]
            log.Printf("pattern: %s set param: %s", pattern, curNode.param)
        }
    }
    // bind
    pattenMap[curNode.code] = pattern
}

func (n *node) isWildChild(b byte) bool {
    return b == colonByte || b == asteriskByte
}

func parsePattern(pattern string) []string {
    var (
        vs    = strings.Split(pattern, slash)
        parts []string
    )
    for _, item := range vs {
        item = strings.TrimSpace(item)
        if item == "" {
            continue
        }
        parts = append(parts, item)
        if item[0] == asteriskByte {
            //如 /static/*filepath，可以匹配/static/fav.ico，也可以匹配/static/js/jQuery.js，
            //此模式常用于静态服务器，能够递归地匹配子路径
            break
        }
    }
    return parts
}
```

## 5. 分组控制

```go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strings"
)

// 分组
// 一个Engine下可以注册多个分组

type RouterGroup struct {
    // 前缀。分组名
    prefix string
    engine *Engine
}

func (e *Engine) Group(prefix string) *RouterGroup {
    return &RouterGroup{prefix: prefix, engine: e}
}

func main() {
    var e = New()
    var v1 = e.Group("v1")
    {
        v1.Get("/hello", func(c *Context) {
            c.String(http.StatusOK, "%s hello world", v1.GetPrefix())
        })
        v1.Get("/hi/:name", func(c *Context) {
            c.String(http.StatusOK, "%s hi, %s", v1.GetPrefix(), c.params["name"])
        })
        v1.Get("/hi/:name/info/:xx/", func(c *Context) {
            c.String(http.StatusOK, "%s hi, %s, xx: %s", v1.GetPrefix(), c.params["name"], c.params["xx"])
        })
        v1.Get("/file/*filename", func(c *Context) {
            c.String(http.StatusOK, "%s file: %s", v1.GetPrefix(), c.params["filename"])
        })
    }
    e.Get("/hi/:name/info/:xx/", func(c *Context) {
        c.String(http.StatusOK, "hi, %s, xx: %s", c.params["name"], c.params["xx"])
    })
    e.Get("/file/*filename", func(c *Context) {
        c.String(http.StatusOK, "file: %s", c.params["filename"])
    })
    e.Run(":8888")
}

/* Context */

type Context struct {
    // origin
    r *http.Request
    w http.ResponseWriter
    // params
    params map[string]string
}

func NewContext(w http.ResponseWriter, req *http.Request) *Context {
    return &Context{
        w: w,
        r: req,
    }
}

func (c *Context) PostForm(key string) string {
    return c.r.FormValue(key)
}

func (c *Context) Query(key string) string {
    return c.r.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
    c.w.WriteHeader(code)
}

func (c *Context) SetHeader(key string, value string) {
    c.w.Header().Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
    c.SetHeader("Content-Type", "text/plain")
    c.Status(code)
    c.w.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
    c.SetHeader("Content-Type", "application/json")
    c.Status(code)
    encoder := json.NewEncoder(c.w)
    if err := encoder.Encode(obj); err != nil {
        http.Error(c.w, err.Error(), http.StatusInternalServerError)
    }
}

func (c *Context) Data(code int, data []byte) {
    c.Status(code)
    c.w.Write(data)
}

func (c *Context) HTML(code int, html string) {
    c.SetHeader("Content-Type", "text/html")
    c.Status(code)
    c.w.Write([]byte(html))
}

/*Engine*/

type Method string

const (
    MethodGET  Method = http.MethodGet
    MethodPOST Method = http.MethodPost
)

type Engine struct {
    router
    RouterGroup
}

type HandlerFunc func(c *Context)

func New() *Engine {
    var e = &Engine{router: router{roots: map[Method]*node{}, handleMap: map[string]HandlerFunc{}},
        RouterGroup: RouterGroup{prefix: ""}}
    e.engine = e
    return e
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if root, has := e.roots[Method(r.Method)]; has {
        path, params := root.search(r.URL.Path)
        if handler, ok := e.handleMap[genRouterKey(Method(r.Method), path)]; ok {
            var ctx = NewContext(w, r)
            ctx.params = params
            handler(ctx)
        }
        return
    }
    fmt.Fprintf(w, "404 NOT FOUND: %s\n", r.URL)
}

func (e *Engine) Run(addr string) error {
    return http.ListenAndServe(addr, e)
}

/* router */

type router struct {
    roots     map[Method]*node
    handleMap map[string]HandlerFunc
}

func (r router) addRouter(method Method, path string, handle HandlerFunc) {
    var (
        root *node
        has  bool
    )
    if root, has = r.roots[method]; !has {
        root = newTrie()
        r.roots[method] = root
    }
    root.insert(path)
    // 绑定
    r.handleMap[genRouterKey(method, path)] = handle
}

func genRouterKey(method Method, path string) string {
    return string(method) + "-" + path
}

func (r *RouterGroup) Get(path string, handle HandlerFunc) {
    r.engine.addRouter(MethodGET, r.prefix+path, handle)
}
func (r *RouterGroup) Post(path string, handle HandlerFunc) {
    r.engine.addRouter(MethodPOST, r.prefix+path, handle)
}

func (r *RouterGroup) GetPrefix() string {
    return r.prefix
}

/* trie */

const (
    slash             = "/"
    asteriskByte byte = '*'
    colonByte    byte = ':'
    codeMe            = 20210607
    codeNotFound      = -404
)

var (
    code      int32 = codeMe
    pattenMap       = make(map[int32]string)
)

func codeIncr() int32 {
    code++
    return code
}

func getCode() int32 {
    return code
}

type node struct {
    code     int32
    wild     byte
    children map[string]*node
    param    string
}

func newTrie() *node {
    return &node{children: map[string]*node{}}
}

func (n *node) search(path string) (string, map[string]string) {
    var (
        parts  = parsePattern(path)
        params = make(map[string]string)
        // curNode -> root
        // part[0]
        code = n.match(n, parts, 0, params)
    )
    log.Printf("pattenMap: %+v", pattenMap)
    log.Printf("code: %d", code)
    return pattenMap[code], params
}

func (n *node) match(curNode *node, parts []string, idx int, params map[string]string) int32 {
    if idx == len(parts) {
        // 所有part都匹配过了
        if curNode == nil {
            log.Printf("curNode is nil")
            // 未匹配到规则
            return codeNotFound
        }
        // 校验最后一位
        if curNode.isWildChild(curNode.wild) {
            params[curNode.param] = parts[idx-1]
        }
        return curNode.code
    }
    log.Printf("parts[i]: %s", parts[idx])
    if curNode.wild == asteriskByte {
        log.Printf("find params: %s, value: %s", params[curNode.param], parts[idx])
        params[curNode.param] = strings.Join(parts[idx-1:], slash)
        // 匹配到通配符'*'规则
        return curNode.code
    }
    if curNode.wild == colonByte {
        log.Printf("find params: %s, value: %s", params[curNode.param], parts[idx])
        params[curNode.param] = parts[idx-1]
    }
    // 字符串完全匹配
    if _, has := curNode.children[parts[idx]]; !has {
        var tNode = curNode.getNode()
        if tNode == nil {
            return codeNotFound
        }
        return curNode.match(tNode, parts, idx+1, params)
    }
    return n.match(curNode.children[parts[idx]], parts, idx+1, params)
}

func (n *node) getNode() *node {
    var (
        tCode = getCode() + 1
        ans   *node
    )
    for k := range n.children {
        if !n.isWildChild(n.children[k].wild) {
            continue
        }
        if n.children[k].code < tCode {
            ans = n.children[k]
            tCode = ans.code
        }
    }
    return ans
}

func (n *node) insert(pattern string) {
    var (
        parts   = parsePattern(pattern)
        curNode = n
    )
    log.Printf("pattern: %s, parts: %v", pattern, parts)
    for i := 0; i < len(parts); i++ {
        log.Printf("parts[i]: %s", parts[i])
        if _, has := curNode.children[parts[i]]; !has {
            curNode.children[parts[i]] = &node{children: map[string]*node{}, code: codeIncr()}
        }
        curNode = curNode.children[parts[i]]
        if n.isWildChild(parts[i][0]) {
            curNode.wild = parts[i][0]
            curNode.param = parts[i][1:]
            log.Printf("pattern: %s set param: %s", pattern, curNode.param)
        }
    }
    // bind
    pattenMap[curNode.code] = pattern
}

func (n *node) isWildChild(b byte) bool {
    return b == colonByte || b == asteriskByte
}

func parsePattern(pattern string) []string {
    var (
        vs    = strings.Split(pattern, slash)
        parts []string
    )
    for _, item := range vs {
        item = strings.TrimSpace(item)
        if item == "" {
            continue
        }
        parts = append(parts, item)
        if item[0] == asteriskByte {
            //如 /static/*filepath，可以匹配/static/fav.ico，也可以匹配/static/js/jQuery.js，
            //此模式常用于静态服务器，能够递归地匹配子路径
            break
        }
    }
    return parts
}
```

## 6. 中间件

```go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strings"
)

//http://127.0.0.1:8888/v1/file/xxx.css
//2021/06/12 15:11:20 测试 中间件demo1
//2021/06/12 15:11:20 测试 中间件demo2
//2021/06/12 15:11:20 file: xxx.css
//2021/06/12 15:11:20 测试 demo2中间件执行完毕
//2021/06/12 15:11:20 测试 demo1中间件执行完毕

func demo() HandlerFunc {
    return func(c *Context) {
        log.Println("测试 中间件demo1")
        c.Next()
        log.Println("测试 demo1中间件执行完毕")
    }
}

func demo2() HandlerFunc {
    return func(c *Context) {
        log.Println("测试 中间件demo2")
        c.Next()
        log.Println("测试 demo2中间件执行完毕")
    }
}

func main() {
    var e = New()
    var v1 = e.Group("v1").Use(demo(), demo2())
    {
        v1.Get("/hello", func(c *Context) {
            c.String(http.StatusOK, "%s hello world", v1.GetPrefix())
        })
        v1.Get("/hi/:name", func(c *Context) {
            c.String(http.StatusOK, "%s hi, %s", v1.GetPrefix(), c.params["name"])
        })
        v1.Get("/hi/:name/info/:xx/", func(c *Context) {
            c.String(http.StatusOK, "%s hi, %s, xx: %s", v1.GetPrefix(), c.params["name"], c.params["xx"])
        })
        v1.Get("/file/*filename", func(c *Context) {
            log.Printf("file: %s", c.params["filename"])
            c.String(http.StatusOK, "%s file: %s", v1.GetPrefix(), c.params["filename"])
        })
    }
    e.Get("/hi/:name/info/:xx/", func(c *Context) {
        c.String(http.StatusOK, "hi, %s, xx: %s", c.params["name"], c.params["xx"])
    })
    e.Get("/file/*filename", func(c *Context) {
        c.String(http.StatusOK, "file: %s", c.params["filename"])
    })
    e.Run(":8888")
}

// 分组
// 一个Engine下可以注册多个分组

var prefixMiddlewares = make(map[string][]HandlerFunc)

type RouterGroup struct {
    // 前缀。分组名
    prefix string
    engine *Engine
}

func (r *RouterGroup) Use(fs ...HandlerFunc) *RouterGroup {
    if _, has := prefixMiddlewares[r.GetPrefix()]; has {
        prefixMiddlewares[r.GetPrefix()] = append(prefixMiddlewares[r.GetPrefix()], fs...)
    } else {
        prefixMiddlewares[r.GetPrefix()] = fs
    }
    return r
}

func (e *Engine) Group(prefix string) *RouterGroup {
    return &RouterGroup{prefix: prefix, engine: e}
}

/* Context */

type Context struct {
    // origin
    r *http.Request
    w http.ResponseWriter
    // params
    params map[string]string
    // handlers
    handlers []HandlerFunc
    index    int
}

func NewContext(w http.ResponseWriter, req *http.Request) *Context {
    return &Context{
        w:     w,
        r:     req,
        index: -1,
    }
}

func (c *Context) SetHandlers(handlers []HandlerFunc) {
    c.handlers = handlers
}

func (c *Context) Next() {
    // 确保在执行函数前index指向下一个函数，否则会出现死循环
    c.index++
    c.handlers[c.index](c)
}

func (c *Context) PostForm(key string) string {
    return c.r.FormValue(key)
}

func (c *Context) Query(key string) string {
    return c.r.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
    c.w.WriteHeader(code)
}

func (c *Context) SetHeader(key string, value string) {
    c.w.Header().Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
    c.SetHeader("Content-Type", "text/plain")
    c.Status(code)
    c.w.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
    c.SetHeader("Content-Type", "application/json")
    c.Status(code)
    encoder := json.NewEncoder(c.w)
    if err := encoder.Encode(obj); err != nil {
        http.Error(c.w, err.Error(), http.StatusInternalServerError)
    }
}

func (c *Context) Data(code int, data []byte) {
    c.Status(code)
    c.w.Write(data)
}

func (c *Context) HTML(code int, html string) {
    c.SetHeader("Content-Type", "text/html")
    c.Status(code)
    c.w.Write([]byte(html))
}

/*Engine*/

type Method string

const (
    MethodGET  Method = http.MethodGet
    MethodPOST Method = http.MethodPost
)

type Engine struct {
    router
    RouterGroup
}

type HandlerFunc func(c *Context)

func New() *Engine {
    var e = &Engine{router: router{roots: map[Method]*node{}, handleMap: map[string]HandlerFunc{}},
        RouterGroup: RouterGroup{prefix: ""}}
    e.engine = e
    return e
}

func GetMiddlewareByRouterKey(key string) []HandlerFunc {
    var (
        prefix = routerKeyPrefixMap[key]
        ans    = prefixMiddlewares[prefix]
    )
    return ans
}

func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if root, has := e.roots[Method(r.Method)]; has {
        path, params := root.search(r.URL.Path)
        var routerKey = genRouterKey(Method(r.Method), path)
        if handler, ok := e.handleMap[routerKey]; ok {
            var ctx = NewContext(w, r)
            ctx.params = params
            var handlers = GetMiddlewareByRouterKey(routerKey)
            if handlers != nil {
                handlers = append(handlers, handler)
            } else {
                handlers = []HandlerFunc{handler}
            }
            ctx.SetHandlers(handlers)
            ctx.Next()
        }
        return
    }
    fmt.Fprintf(w, "404 NOT FOUND: %s\n", r.URL)
}

func (e *Engine) Run(addr string) error {
    return http.ListenAndServe(addr, e)
}

var routerKeyPrefixMap = make(map[string]string)

/* router */

type router struct {
    roots     map[Method]*node
    handleMap map[string]HandlerFunc
}

func (r router) addRouter(method Method, prefix string, path string, handle HandlerFunc) {
    var (
        root      *node
        has       bool
        pattern   = prefix + path
        routerKey = genRouterKey(method, pattern)
    )
    if root, has = r.roots[method]; !has {
        root = newTrie()
        r.roots[method] = root
    }
    root.insert(pattern)
    // 绑定 路径、函数
    r.handleMap[routerKey] = handle
    // 绑定 路径、前缀
    routerKeyPrefixMap[routerKey] = prefix
}

func genRouterKey(method Method, path string) string {
    return string(method) + "-" + path
}

func (r *RouterGroup) Get(path string, handle HandlerFunc) {
    r.engine.addRouter(MethodGET, r.prefix, path, handle)
}
func (r *RouterGroup) Post(path string, handle HandlerFunc) {
    r.engine.addRouter(MethodPOST, r.prefix, path, handle)
}

func (r *RouterGroup) GetPrefix() string {
    return r.prefix
}

/* trie */

const (
    slash             = "/"
    asteriskByte byte = '*'
    colonByte    byte = ':'
    codeMe            = 20210607
    codeNotFound      = -404
)

var (
    code      int32 = codeMe
    pattenMap       = make(map[int32]string)
)

func codeIncr() int32 {
    code++
    return code
}

func getCode() int32 {
    return code
}

type node struct {
    code     int32
    wild     byte
    children map[string]*node
    param    string
}

func newTrie() *node {
    return &node{children: map[string]*node{}}
}

func (n *node) search(path string) (string, map[string]string) {
    var (
        parts  = parsePattern(path)
        params = make(map[string]string)
        // curNode -> root
        // part[0]
        code = n.match(n, parts, 0, params)
    )
    log.Printf("pattenMap: %+v", pattenMap)
    log.Printf("code: %d", code)
    return pattenMap[code], params
}

func (n *node) match(curNode *node, parts []string, idx int, params map[string]string) int32 {
    if idx == len(parts) {
        // 所有part都匹配过了
        if curNode == nil {
            log.Printf("curNode is nil")
            // 未匹配到规则
            return codeNotFound
        }
        // 校验最后一位
        if curNode.isWildChild(curNode.wild) {
            params[curNode.param] = parts[idx-1]
        }
        return curNode.code
    }
    log.Printf("parts[i]: %s", parts[idx])
    if curNode.wild == asteriskByte {
        log.Printf("find params: %s, value: %s", params[curNode.param], parts[idx])
        params[curNode.param] = strings.Join(parts[idx-1:], slash)
        // 匹配到通配符'*'规则
        return curNode.code
    }
    if curNode.wild == colonByte {
        log.Printf("find params: %s, value: %s", params[curNode.param], parts[idx])
        params[curNode.param] = parts[idx-1]
    }
    // 字符串完全匹配
    if _, has := curNode.children[parts[idx]]; !has {
        var tNode = curNode.getNode()
        if tNode == nil {
            return codeNotFound
        }
        return curNode.match(tNode, parts, idx+1, params)
    }
    return n.match(curNode.children[parts[idx]], parts, idx+1, params)
}

func (n *node) getNode() *node {
    var (
        tCode = getCode() + 1
        ans   *node
    )
    for k := range n.children {
        if !n.isWildChild(n.children[k].wild) {
            continue
        }
        if n.children[k].code < tCode {
            ans = n.children[k]
            tCode = ans.code
        }
    }
    return ans
}

func (n *node) insert(pattern string) {
    var (
        parts   = parsePattern(pattern)
        curNode = n
    )
    log.Printf("pattern: %s, parts: %v", pattern, parts)
    for i := 0; i < len(parts); i++ {
        log.Printf("parts[i]: %s", parts[i])
        if _, has := curNode.children[parts[i]]; !has {
            curNode.children[parts[i]] = &node{children: map[string]*node{}, code: codeIncr()}
        }
        curNode = curNode.children[parts[i]]
        if n.isWildChild(parts[i][0]) {
            curNode.wild = parts[i][0]
            curNode.param = parts[i][1:]
            log.Printf("pattern: %s set param: %s", pattern, curNode.param)
        }
    }
    // bind
    pattenMap[curNode.code] = pattern
}

func (n *node) isWildChild(b byte) bool {
    return b == colonByte || b == asteriskByte
}

func parsePattern(pattern string) []string {
    var (
        vs    = strings.Split(pattern, slash)
        parts []string
    )
    for _, item := range vs {
        item = strings.TrimSpace(item)
        if item == "" {
            continue
        }
        parts = append(parts, item)
        if item[0] == asteriskByte {
            //如 /static/*filepath，可以匹配/static/fav.ico，也可以匹配/static/js/jQuery.js，
            //此模式常用于静态服务器，能够递归地匹配子路径
            break
        }
    }
    return parts
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://1005281342.gitbook.io/code-porter/lab/web-kuang-jia.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
