<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          echo源碼分析

          共 29318字,需瀏覽 59分鐘

           ·

          2021-05-10 17:59

          web框架的核心作用有三個(gè):分層、路由、中間件。針對(duì)于go比較有名的web框架有

          https://github.com/labstack/echo

          https://github.com/gin-gonic/gin

          https://github.com/kataras/iris

          https://beego.me/docs/intro/

          https://github.com/go-martini/martini


          其中echo 是一個(gè)比較輕量級(jí)的框架,下面基于[email protected]對(duì)它的源碼進(jìn)行分析。

          主要有下面6個(gè)文件和三個(gè)目錄組成。

          binder.gocontext.goecho.gogroup.goresponse.gorouter.gomiddleware_fixturewebsite


          其中middleware里面定義了最基本最常用的四個(gè)中間件

          auth.gocompress.gologger.gorecover.go

          _fixture是一些網(wǎng)頁(yè)資源

           % ls _fixturefavicon.ico  folder    images    index.html

          website 是說(shuō)明文檔,中間有個(gè)Dockerfile 可以在本地編譯鏡像,跑起來(lái)

           % ls websiteDockerfile  config.json  layoutsargo.json  content    static

          首先我們看下如何使用echo

          package main
          import ( "net/http"
          "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware")
          func main() {  // 創(chuàng)建一個(gè)echo實(shí)例 e := echo.New()
          // 注冊(cè)中間件  // 需要我們?cè)谌肟谖募謩?dòng)注入基礎(chǔ)中間件 e.Use(middleware.Logger()) e.Use(middleware.Recover())
            // 注冊(cè)路由 e.GET("/", hello)
            // 啟動(dòng)服務(wù) e.Logger.Fatal(e.Start(":1323"))}
          // 路由handle提出來(lái)了而已// 匿名函數(shù)方式 不重要func hello(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!")}


          1,echo.go文件


          New函數(shù)定義在echo.go 文件里面

          func New() (e *Echo) {  e = &Echo{    // 創(chuàng)建一個(gè)http Server指針    Server:    new(http.Server),    // 創(chuàng)建一個(gè)https的 Server指針    TLSServer: new(http.Server),    AutoTLSManager: autocert.Manager{      Prompt: autocert.AcceptTOS,    },    // 日志實(shí)例    Logger:   log.New("echo"),    // 控制臺(tái)、日志可以彩色輸出的實(shí)例    colorer:  color.New(),    maxParam: new(int),  }  // http server綁定實(shí)現(xiàn)了server.Handler的實(shí)例  // 也就是說(shuō)Echo框架自身實(shí)現(xiàn)了http.Handler接口  e.Server.Handler = e  // https server綁定實(shí)現(xiàn)了server.Handler的實(shí)例  e.TLSServer.Handler = e  // 綁定http服務(wù)異常處理的handler  e.HTTPErrorHandler = e.DefaultHTTPErrorHandler  //   e.Binder = &DefaultBinder{}  // 設(shè)置日志輸出級(jí)別  e.Logger.SetLevel(log.ERROR)  // 綁定標(biāo)準(zhǔn)日志輸出實(shí)例  e.StdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)  // 綁定獲取請(qǐng)求上下文實(shí)例的閉包  e.pool.New = func() interface{} {    return e.NewContext(nil, nil)  }  // 綁定路由實(shí)例  e.router = NewRouter(e)  // 綁定路由map  // 注意這個(gè)屬性的含義:路由分組用的,key為host,則按host分組  // 記住與Router.routes區(qū)別  // Router.routes存的路由的信息(不包含路由的handler)  e.routers = map[string]*Router{}  return}

          手先初始化了一個(gè)echo對(duì)象,然后定義了一個(gè)DefaultBinder,實(shí)現(xiàn)了Binder接口,這個(gè)接口定義在bind.go文件里,后面介紹

            Binder interface {    Bind(i interface{}, c Context) error  }

          接著設(shè)置了日志級(jí)別為ERROR,設(shè)置了標(biāo)準(zhǔn)輸出Logger,然后初始化了一個(gè)Context 對(duì)象

          // NewContext returns a Context instance.func (e *Echo) NewContext(r *http.Request, w http.ResponseWriter) Context {  return &context{    request:  r,    response: NewResponse(w, e),    store:    make(Map),    echo:     e,    pvalues:  make([]string, *e.maxParam),    handler:  NotFoundHandler,  }}

          context 對(duì)象和interface 都定義在context.go文件中,后面講解context .go文件的時(shí)候詳細(xì)講解.

          其中的Map定義如下

            Map map[string]interface{}

          context里面存儲(chǔ)了

          r *http.Request, w http.ResponseWriter

          這也就是為什么寫(xiě)echo的controller的時(shí)候只用傳 context對(duì)象就行,而標(biāo)準(zhǔn)的http包需要頂替包含參數(shù)r *http.Request, w http.ResponseWriter的HandleFunc

          最后初始化了路由(router.go)里面實(shí)現(xiàn)的

          func NewRouter(e *Echo) *Router {  // 初始化Router  return &Router{    // 路由樹(shù)    // 路由的信息(包含路由的handler)    // 查找路由用的LCP (最長(zhǎng)公共前綴)算法    tree: &node{      // 節(jié)點(diǎn)對(duì)應(yīng)的不同http method的handler      methodHandler: new(methodHandler),    },    // Router.routes存的路由的信息(不包含路由的handler)    routes: map[string]*Route{},    // 框架實(shí)例自身    echo:   e,  }

          Router的定義如下

            Router struct {    tree   *node    routes map[string]*Route    echo   *Echo  }

          是一個(gè)棵多叉樹(shù),其中tree存儲(chǔ)了當(dāng)前節(jié)點(diǎn)里面的值,它的定義如下

            node struct {    kind          kind    label         byte    prefix        string    parent        *node    children      children    ppath         string    pnames        []string    methodHandler *methodHandler  }

          接著我們看下如何定義路由的,已get請(qǐng)求為例

            e.GET("/stats", func(c echo.Context) error {    return c.JSON(200, s.Data())  })

          它的定義如下

          func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {  return e.Add(http.MethodGet, path, h, m...)}

          我們可以看到,它的內(nèi)部調(diào)用了Add方法,其實(shí)POST、PATCH、DELETE等http method類似都是對(duì)Add方法進(jìn)行了包裹

          Add方法的參數(shù)有http請(qǐng)求的方法,請(qǐng)求路徑,對(duì)應(yīng)的處理函數(shù)和中間件參數(shù)。

          func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {  // 獲取handler的名稱  // ??這個(gè)方法里面盡然用了反射獲取name 只是個(gè)name有必要么 沒(méi)別的辦法了嗎?  name := handlerName(handler)  // 注冊(cè)路由  // 注意第三個(gè)參數(shù)是個(gè)閉包 匹配到路由就會(huì)執(zhí)行這個(gè)閉包  e.router.Add(method, path, func(c Context) error {    h := handler    // Chain middleware    for i := len(middleware) - 1; i >= 0; i-- {       // 注意這里的中間件是這個(gè)路由專屬的      // 而Use、Pre注冊(cè)的中間件是全局公共的      // 遍歷中間件      // 注意返回值類型是HandlerFunc      //典型的洋蔥模式      h = middleware[i](h)    }     // 執(zhí)行最后一個(gè)中間件    return h(c)  })
            // 本次注冊(cè)進(jìn)來(lái)的路由的信息,只存放了handler的方法名 r := &Route{ Method: method, Path: path, Name: name, } // map存路由信息 e.router.routes[method+path] = r return r}

          注意這里的Route 不是Router定義如下

            Route struct {    Method string `json:"method"`    Path   string `json:"path"`    Name   string `json:"name"`  }

          Method 存的是通過(guò)反射獲取handler的名字

          func handlerName(h HandlerFunc) string {  t := reflect.ValueOf(h).Type()  if t.Kind() == reflect.Func {    return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()  }  return t.String()}

          最后看下start函數(shù)

          e.Logger.Fatal(e.Start(":1323"))
          func (e *Echo) Start(address string) error {  e.Server.Addr = address  return e.StartServer(e.Server)}
          func (e *Echo) StartServer(s *http.Server) (err error) {  // Setup  e.colorer.SetOutput(e.Logger.Output())  s.ErrorLog = e.StdLogger  // 設(shè)置框架實(shí)例到http server的Handler  // Echo框架結(jié)構(gòu)體實(shí)現(xiàn)了http.Handler接口  s.Handler = e  if e.Debug {    e.Logger.SetLevel(log.DEBUG)  }
          if !e.HideBanner { e.colorer.Printf(banner, e.colorer.Red("v"+Version), e.colorer.Blue(website)) }
          if s.TLSConfig == nil { if e.Listener == nil {     // 監(jiān)聽(tīng)ip+port e.Listener, err = newListener(s.Addr) if err != nil { return err } } if !e.HidePort { e.colorer.Printf("? http server started on %s\n", e.colorer.Green(e.Listener.Addr())) } // 啟動(dòng)http server return s.Serve(e.Listener) } if e.TLSListener == nil {     // 設(shè)置https配置 l, err := newListener(s.Addr) if err != nil { return err } e.TLSListener = tls.NewListener(l, s.TLSConfig) } if !e.HidePort { e.colorer.Printf("? https server started on %s\n", e.colorer.Green(e.TLSListener.Addr())) } return s.Serve(e.TLSListener)}

          接下來(lái)的流程就是標(biāo)準(zhǔn)httpserver的執(zhí)行流程

          s.Serve()??// accept網(wǎng)絡(luò)請(qǐng)求rw, e := l.Accept()??// goroutine處理請(qǐng)求go c.serve(ctx)??// 執(zhí)行serverHandler的ServeHTTPserverHandler{c.server}.ServeHTTP(w, w.req)??// 執(zhí)行當(dāng)前框架實(shí)例的ServeHTTP方法handler.ServeHTTP(rw, req)

          看下echo是怎么實(shí)現(xiàn)ServeHTTP方法的

          // ServeHTTP implements `http.Handler` interface, which serves HTTP requests.func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {  // Acquire context  // 獲取上下文實(shí)例  c := e.pool.Get().(*context)  c.Reset(r, w)
          h := NotFoundHandler // 不存在預(yù)執(zhí)行中間件時(shí) // 說(shuō)說(shuō)這個(gè)預(yù)執(zhí)行中間件的含義: // 看源碼注釋的含義是在尋找到路由之前執(zhí)行的中間件 // 簡(jiǎn)單來(lái)說(shuō)和普通中間件的的區(qū)別就是,還沒(méi)走到匹配路由的邏輯就會(huì)執(zhí)行的中間件,從下面來(lái)看只是代碼邏輯的區(qū)別,實(shí)際的中間件執(zhí)行順序還是誰(shuí)先注冊(cè)誰(shuí)先執(zhí)行。所以無(wú)論是存在普通中間件還是預(yù)執(zhí)行中間件,路由的handle總是最后執(zhí)行。 // 個(gè)人感覺(jué)預(yù)執(zhí)行中間件的意義不大 if e.premiddleware == nil {    // 先找當(dāng)前host組的router // LCP算法尋找當(dāng)前path的handler e.router.Find(r.Method, getPath(r), c) h = c.Handler() for i := len(e.middleware) - 1; i >= 0; i-- { h = e.middleware[i](h) } } else {     // 看見(jiàn)這個(gè)預(yù)執(zhí)行中間件的區(qū)別了吧 // 把注冊(cè)普通中間件的邏輯又包裝成了一個(gè)HandlerFunc注冊(cè)到中間件鏈中 h = func(c Context) error { e.router.Find(r.Method, getPath(r), c) h := c.Handler() for i := len(e.middleware) - 1; i >= 0; i-- { h = e.middleware[i](h) } return h(c) } for i := len(e.premiddleware) - 1; i >= 0; i-- { h = e.premiddleware[i](h) } }
          // Execute chain // 執(zhí)行中間件鏈 // 在applyMiddleware中所有中間件構(gòu)成了一個(gè)鏈 if err := h(c); err != nil { e.HTTPErrorHandler(err, c) }
          // Release context // 釋放上下文 e.pool.Put(c)}

          echo.go的核心邏輯基本講完了,里面還定義了一系列的輔助類型和方法


          // MiddlewareFunc defines a function to process middleware. MiddlewareFunc func(HandlerFunc) HandlerFunc
          // HandlerFunc defines a function to serve HTTP requests. HandlerFunc func(Context) error
          // HTTPErrorHandler is a centralized HTTP error handler. HTTPErrorHandler func(error, Context)
          // Validator is the interface that wraps the Validate function. Validator interface { Validate(i interface{}) error }
          // Renderer is the interface that wraps the Render function. Renderer interface { Render(io.Writer, string, interface{}, Context) error  } // i is the interface for Echo and Group. i interface { GET(string, HandlerFunc, ...MiddlewareFunc) *Route }
          // HTTP methods// NOTE: Deprecated, please use the stdlib constants directly instead.const (  CONNECT = http.MethodConnect  DELETE  = http.MethodDelete
          // MIME typesconst (  MIMEApplicationJSON                  = "application/json"  MIMEApplicationJSONCharsetUTF8       = MIMEApplicationJSON + "; " + charsetUTF8
          // Headersconst (  HeaderAccept              = "Accept"  HeaderAcceptEncoding      = "Accept-Encoding"
          // Errorsvar (  ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType)
          // DefaultHTTPErrorHandler is the default HTTP error handler. It sends a JSON response// with status code.func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
          // Static registers a new route with path prefix to serve static files from the// provided root directory.func (e *Echo) Static(prefix, root string) *Route {  if root == "" {    root = "." // For security we want to restrict to CWD.  }  return static(e, prefix, root)}
          func static(i i, prefix, root string) *Route { h := func(c Context) error { p, err := url.PathUnescape(c.Param("*")) if err != nil { return err } name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security return c.File(name) } i.GET(prefix, h) if prefix == "/" { return i.GET(prefix+"*", h) }
          return i.GET(prefix+"/*", h)}

          把url中的namd/:id 中的id解析出來(lái)存到url中

          // Reverse generates an URL from route name and provided parameters.func (e *Echo) Reverse(name string, params ...interface{}) string {  uri := new(bytes.Buffer)  ln := len(params)  n := 0  for _, r := range e.router.routes {    if r.Name == name {      for i, l := 0, len(r.Path); i < l; i++ {        if r.Path[i] == ':' && n < ln {          for ; i < l && r.Path[i] != '/'; i++ {          }          uri.WriteString(fmt.Sprintf("%v", params[n]))          n++        }        if i < l {          uri.WriteByte(r.Path[i])        }      }      break    }  }  return uri.String()}

          2,context.go文件

          首先定義了

          Context interface {}

          和對(duì)應(yīng)的對(duì)象

            context struct {    request  *http.Request    response *Response    path     string    pnames   []string    pvalues  []string    query    url.Values    handler  HandlerFunc    store    Map    echo     *Echo  }

          獲取參數(shù)值

          func (c *context) Param(name string) string {  for i, n := range c.pnames {    if i < len(c.pvalues) {      if n == name {        return c.pvalues[i]      }    }  }  return ""}
          func (c *context) QueryParam(name string) string {  if c.query == nil {    c.query = c.request.URL.Query()  }  return c.query.Get(name)}
          func (c *context) FormValue(name string) string {  return c.request.FormValue(name)}

          參數(shù)綁定

          func (c *context) Bind(i interface{}) error {  return c.echo.Binder.Bind(i, c)}

          參數(shù)校驗(yàn)

          func (c *context) Validate(i interface{}) error {  if c.echo.Validator == nil {    return ErrValidatorNotRegistered  }  return c.echo.Validator.Validate(i)}

          輸出json

          func (c *context) json(code int, i interface{}, indent string) error {  enc := json.NewEncoder(c.response)  if indent != "" {    enc.SetIndent("", indent)  }  c.writeContentType(MIMEApplicationJSONCharsetUTF8)  c.response.WriteHeader(code)  return enc.Encode(i)}

          3,router.go

          主要是定義了Router的一系列結(jié)構(gòu)體

            Router struct {    tree   *node    routes []Route    echo   *Echo  }  node struct {    kind          kind    label         byte    prefix        string    parent        *node    children      children    ppath         string    pnames        []string    methodHandler *methodHandler    echo          *Echo  }


          一系列處理方法

            methodHandler struct {    connect HandlerFunc    delete  HandlerFunc    get     HandlerFunc    head    HandlerFunc    options HandlerFunc    patch   HandlerFunc    post    HandlerFunc    put     HandlerFunc    trace   HandlerFunc  }

          初始化一個(gè)router對(duì)象

          func NewRouter(e *Echo) *Router {  return &Router{    tree: &node{      methodHandler: new(methodHandler),    },    routes: []Route{},    echo:   e,  }}

          添加路由,構(gòu)建tire樹(shù)

          // Add registers a new route with a matcher for the URL path.func (r *Router) Add(method, path string, h HandlerFunc, e *Echo) {  ppath := path        // Pristine path  pnames := []string{} // Param names
          for i, l := 0, len(path); i < l; i++ { if path[i] == ':' { j := i + 1      //提取路由中的參數(shù)前面部分,構(gòu)建樹(shù) r.insert(method, path[:i], nil, skind, "", nil, e) for ; i < l && path[i] != '/'; i++ { }      //把參數(shù)名放到pnames里面 pnames = append(pnames, path[j:i]) path = path[:j] + path[i:] i, l = j, len(path)
          if i == l { r.insert(method, path[:i], h, pkind, ppath, pnames, e) return } r.insert(method, path[:i], nil, pkind, ppath, pnames, e) } else if path[i] == '*' { r.insert(method, path[:i], nil, skind, "", nil, e) pnames = append(pnames, "_*") r.insert(method, path[:i+1], h, akind, ppath, pnames, e) return } }
          r.insert(method, path, h, skind, ppath, pnames, e)}

          提取參數(shù)和正則以后,構(gòu)建tire樹(shù)

          func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string, e *Echo) {  // Adjust max param  l := len(pnames)  if *e.maxParam < l {    *e.maxParam = l  }
          cn := r.tree // Current node as root if cn == nil { panic("echo => invalid method") } search := path
          for { sl := len(search) pl := len(cn.prefix) l := 0
          // LCP max := pl if sl < max { max = sl } for ; l < max && search[l] == cn.prefix[l]; l++ { }
          if l == 0 { // At root node cn.label = search[0] cn.prefix = search if h != nil { cn.kind = t cn.addHandler(method, h) cn.ppath = ppath cn.pnames = pnames cn.echo = e } } else if l < pl { // Split node n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames, cn.echo)
          // Reset parent node cn.kind = skind cn.label = cn.prefix[0] cn.prefix = cn.prefix[:l] cn.children = nil cn.methodHandler = new(methodHandler) cn.ppath = "" cn.pnames = nil cn.echo = nil
          cn.addChild(n)
          if l == sl { // At parent node cn.kind = t cn.addHandler(method, h) cn.ppath = ppath cn.pnames = pnames cn.echo = e } else { // Create child node n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames, e) n.addHandler(method, h) cn.addChild(n) } } else if l < sl { search = search[l:] c := cn.findChildWithLabel(search[0]) if c != nil { // Go deeper cn = c continue } // Create child node n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames, e) n.addHandler(method, h) cn.addChild(n) } else { // Node already exists if h != nil { cn.addHandler(method, h) cn.ppath = ppath cn.pnames = pnames cn.echo = e } } return }}


          接著就是路由查找的過(guò)程

          func (r *Router) Find(method, path string, ctx *Context) (h HandlerFunc, e *Echo) {  // r.tree.printTree("", true)  h = notFoundHandler  e = r.echo  cn := r.tree // Current node as root
          var ( search = path c *node // Child node n int // Param counter nk kind // Next kind nn *node // Next node ns string // Next search )
          // Search order static > param > any for { if search == "" { goto End }
          pl := 0 // Prefix length l := 0 // LCP length
          if cn.label != ':' { sl := len(search) pl = len(cn.prefix)
          // LCP max := pl if sl < max { max = sl } for ; l < max && search[l] == cn.prefix[l]; l++ { } }
          if l == pl { // Continue search search = search[l:] } else { cn = nn search = ns if nk == pkind { goto Param } else if nk == akind { goto Any } // Not found return }
          if search == "" { goto End }
          // Static node if c = cn.findChild(search[0], skind); c != nil { // Save next if cn.prefix[len(cn.prefix)-1] == '/' { nk = pkind nn = cn ns = search } cn = c continue }
          // Param node Param: if c = cn.findChildByKind(pkind); c != nil { // Issue #378 if len(ctx.pvalues) == n { continue }
          // Save next if cn.prefix[len(cn.prefix)-1] == '/' { nk = akind nn = cn ns = search } cn = c
          i, l := 0, len(search) for ; i < l && search[i] != '/'; i++ { } ctx.pvalues[n] = search[:i] n++ search = search[i:] continue }
          // Any node Any: if cn = cn.findChildByKind(akind); cn == nil { if nn != nil { cn = nn nn = nil // Next search = ns if nk == pkind { goto Param } else if nk == akind { goto Any } } // Not found return } ctx.pvalues[len(cn.pnames)-1] = search goto End }
          End: ctx.path = cn.ppath ctx.pnames = cn.pnames h = cn.findHandler(method) if cn.echo != nil { e = cn.echo }
          // NOTE: Slow zone... if h == nil { h = cn.check405()
          // Dig further for any, might have an empty value for *, e.g. // serving a directory. Issue #207. if cn = cn.findChildByKind(akind); cn == nil { return } ctx.pvalues[len(cn.pnames)-1] = "" if h = cn.findHandler(method); h == nil { h = cn.check405() } } return}

          最后是serveHTTP方法,這個(gè)方法是在echo的同名方法里,把router轉(zhuǎn)化成handleFunc,然后調(diào)用的。

          func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {  c := r.echo.pool.Get().(*Context)  h, _ := r.Find(req.Method, req.URL.Path, c)  c.reset(req, w, r.echo)  if err := h(c); err != nil {    r.echo.httpErrorHandler(err, c)  }  r.echo.pool.Put(c)}

          4,binder.go

          defaultBinder實(shí)現(xiàn)了Bind方法,通過(guò)http方法以及header里面的ContentType來(lái)實(shí)現(xiàn)綁定

          func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {  req := c.Request()  if req.ContentLength == 0 {    if req.Method == http.MethodGet || req.Method == http.MethodDelete {      if err = b.bindData(i, c.QueryParams(), "query"); err != nil {        return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)      }      return    }    return NewHTTPError(http.StatusBadRequest, "Request body can't be empty")  }  ctype := req.Header.Get(HeaderContentType)  switch {  case strings.HasPrefix(ctype, MIMEApplicationJSON):    if err = json.NewDecoder(req.Body).Decode(i); err != nil {      if ute, ok := err.(*json.UnmarshalTypeError); ok {        return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset)).SetInternal(err)      } else if se, ok := err.(*json.SyntaxError); ok {        return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error())).SetInternal(err)      } else {        return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)      }      return NewHTTPError(http.StatusBadRequest, err.Error())    }  case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):    if err = xml.NewDecoder(req.Body).Decode(i); err != nil {      if ute, ok := err.(*xml.UnsupportedTypeError); ok {        return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())).SetInternal(err)      } else if se, ok := err.(*xml.SyntaxError); ok {        return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error())).SetInternal(err)      } else {        return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)      }      return NewHTTPError(http.StatusBadRequest, err.Error())    }  case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):    params, err := c.FormParams()    if err != nil {      return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)    }    if err = b.bindData(i, params, "form"); err != nil {      return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)    }  default:    return ErrUnsupportedMediaType  }  return}

          綁定數(shù)據(jù)的過(guò)程是通過(guò)reflect實(shí)現(xiàn)的

          func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag string) error {  typ := reflect.TypeOf(ptr).Elem()  val := reflect.ValueOf(ptr).Elem()
          if typ.Kind() != reflect.Struct { return errors.New("binding element must be a struct") }
          for i := 0; i < typ.NumField(); i++ { typeField := typ.Field(i) structField := val.Field(i) if !structField.CanSet() { continue } structFieldKind := structField.Kind() inputFieldName := typeField.Tag.Get(tag)
          if inputFieldName == "" { inputFieldName = typeField.Name // If tag is nil, we inspect if the field is a struct. if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct { if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil { return err } continue } }
          inputValue, exists := data[inputFieldName] if !exists { // Go json.Unmarshal supports case insensitive binding. However the // url params are bound case sensitive which is inconsistent. To // fix this we must check all of the map values in a // case-insensitive search. inputFieldName = strings.ToLower(inputFieldName) for k, v := range data { if strings.ToLower(k) == inputFieldName { inputValue = v exists = true break } } }
          if !exists { continue }
          // Call this first, in case we're dealing with an alias to an array type if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok { if err != nil { return err } continue }
          numElems := len(inputValue) if structFieldKind == reflect.Slice && numElems > 0 { sliceOf := structField.Type().Elem().Kind() slice := reflect.MakeSlice(structField.Type(), numElems, numElems) for j := 0; j < numElems; j++ { if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil { return err } } val.Field(i).Set(slice) } else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil { return err
          } } return nil}

          5,response.go

          對(duì)http response做了一個(gè)包裝

            Response struct {    echo        *Echo    beforeFuncs []func()    afterFuncs  []func()    Writer      http.ResponseWriter    Status      int    Size        int64    Committed   bool  }

          寫(xiě)響應(yīng)頭

          func (r *Response) WriteHeader(code int) {  if r.Committed {    r.echo.Logger.Warn("response already committed")    return  }  for _, fn := range r.beforeFuncs {    fn()  }  r.Status = code  r.Writer.WriteHeader(code)  r.Committed = true}

          寫(xiě)響應(yīng)body

          func (r *Response) Write(b []byte) (n int, err error) {  if !r.Committed {    r.WriteHeader(http.StatusOK)  }  n, err = r.Writer.Write(b)  r.Size += int64(n)  for _, fn := range r.afterFuncs {    fn()  }  return}

          6,group.go

          group主要是定義了子路由,針對(duì)大家有一個(gè)共同ns的復(fù)雜路由場(chǎng)景很有用

            Group struct {    prefix     string    middleware []MiddlewareFunc    echo       *Echo  }

          和普通路由的區(qū)別是path存儲(chǔ)變成了prefix+path

          // Add implements `Echo#Add()` for sub-routes within the Group.func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {  // Combine into a new slice to avoid accidentally passing the same slice for  // multiple routes, which would lead to later add() calls overwriting the  // middleware from earlier calls.  m := make([]MiddlewareFunc, 0, len(g.middleware)+len(middleware))  m = append(m, g.middleware...)  m = append(m, middleware...)  return g.echo.Add(method, g.prefix+path, handler, m...)}


          7,middleware

          7.1auth.go

          實(shí)現(xiàn)了basic auth

            // BasicAuthConfig defines the config for BasicAuth middleware.  BasicAuthConfig struct {    // Skipper defines a function to skip middleware.    Skipper Skipper
          // Validator is a function to validate BasicAuth credentials. // Required. Validator BasicAuthValidator
          // Realm is a string to define realm attribute of BasicAuth. // Default value "Restricted". Realm string }
          // BasicAuthValidator defines a function to validate BasicAuth credentials. BasicAuthValidator func(string, string, echo.Context) (bool, error)

          // BasicAuthWithConfig returns an BasicAuth middleware with config.// See `BasicAuth()`.func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {  // Defaults  if config.Validator == nil {    panic("echo: basic-auth middleware requires a validator function")  }  if config.Skipper == nil {    config.Skipper = DefaultBasicAuthConfig.Skipper  }  if config.Realm == "" {    config.Realm = defaultRealm  }
          return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { if config.Skipper(c) { return next(c) }
          auth := c.Request().Header.Get(echo.HeaderAuthorization) l := len(basic)
          if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic { b, err := base64.StdEncoding.DecodeString(auth[l+1:]) if err != nil { return err } cred := string(b) for i := 0; i < len(cred); i++ { if cred[i] == ':' { // Verify credentials valid, err := config.Validator(cred[:i], cred[i+1:], c) if err != nil { return err } else if valid { return next(c) } break } } }
          realm := defaultRealm if config.Realm != defaultRealm { realm = strconv.Quote(config.Realm) }
          // Need to return `401` for browsers to pop-up login box. c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm) return echo.ErrUnauthorized } }}

          7.2compress.go

          進(jìn)行g(shù)zip壓縮

          unc Gzip() echo.MiddlewareFunc {  scheme := "gzip"
          return func(h echo.HandlerFunc) echo.HandlerFunc { return func(c *echo.Context) error { c.Response().Header().Add(echo.Vary, echo.AcceptEncoding) if strings.Contains(c.Request().Header.Get(echo.AcceptEncoding), scheme) { w := writerPool.Get().(*gzip.Writer) w.Reset(c.Response().Writer()) defer func() { w.Close() writerPool.Put(w) }() gw := gzipWriter{Writer: w, ResponseWriter: c.Response().Writer()} c.Response().Header().Set(echo.ContentEncoding, scheme) c.Response().SetWriter(gw) } if err := h(c); err != nil { c.Error(err) } return nil } }}


          7.3logger.go

          記錄請(qǐng)求的一些基本信息,如ip等等

          func Logger() echo.MiddlewareFunc {  return func(h echo.HandlerFunc) echo.HandlerFunc {    return func(c *echo.Context) error {      req := c.Request()      res := c.Response()      logger := c.Echo().Logger()
          remoteAddr := req.RemoteAddr if ip := req.Header.Get(echo.XRealIP); ip != "" { remoteAddr = ip } else if ip = req.Header.Get(echo.XForwardedFor); ip != "" { remoteAddr = ip } else { remoteAddr, _, _ = net.SplitHostPort(remoteAddr) }
          start := time.Now() if err := h(c); err != nil { c.Error(err) } stop := time.Now() method := req.Method path := req.URL.Path if path == "" { path = "/" } size := res.Size()
          n := res.Status() code := color.Green(n) switch { case n >= 500: code = color.Red(n) case n >= 400: code = color.Yellow(n) case n >= 300: code = color.Cyan(n) }
          logger.Printf(format, remoteAddr, method, path, code, stop.Sub(start), size) return nil } }}


          7.4recover.go

          對(duì)panic進(jìn)行recover操作

          func Recover() echo.MiddlewareFunc {  // TODO: Provide better stack trace `https://github.com/go-errors/errors` `https://github.com/docker/libcontainer/tree/master/stacktrace`  return func(h echo.HandlerFunc) echo.HandlerFunc {    return func(c *echo.Context) error {      defer func() {        if err := recover(); err != nil {          trace := make([]byte, 1<<16)          n := runtime.Stack(trace, true)          c.Error(fmt.Errorf("panic recover\n %v\n stack trace %d bytes\n %s",            err, n, trace[:n]))        }      }()      return h(c)    }  }}


          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 80
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲五月天堂 | 成人网站在线免费观看 | 亚洲私人影院日韩 | 欧美日韩不卡 | 欧美变态性爱一区二区三区 |