Go語言筆記[實現一個Web框架實戰]——EzWeb框架(一)

學習先生發表於2021-08-16

Go語言筆記[實現一個Web框架實戰]——EzWeb框架(一)

一、Golang中的net/http標準庫如何處理一個請求

func main() {
    http.HandleFunc("/", handler)
    http.HandleFunc("/count", counter)
    log.Fatal(http.ListenAndServe("localhost:9090", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}

標準庫中實現了監聽,對映路由,和解析http報文。

瀏覽器的請求,http.ListenAndServe方法中,第一個引數是監聽地址響,第二個引數代表處理所有的HTTP請求的例項,nil代表使用標準庫中的例項處理。

我們通過第二個引數,轉入到我們的例項,這樣所有的HTTP請求就都交給了我們的例項來處理。

二、實現http.Handler介面

/*
@Time : 2021/8/5 下午4:08
@Author : mrxuexi
@File : main
@Software: GoLand
*/
package main

import (
   "fmt"
   "log"
   "net/http"
)

type Engine struct {}
/**
 * @Description:
   對Engine結構體的ServeHttp方法實現。ServeHTTP是Handler介面中的一個方法,在這裡我們對這個方法進行實現,放入到我們的Engine中。這裡就用上了我們的自定義方法來處理請求。
 * @receiver engine
 * @param w http.ResponseWriter(通過該ResponseWriter構造針對該請求的響應)
 * @param req *http.Request(Request中包括了傳送來的HTTP請求的全部資訊)
 */
func (engine *Engine)ServeHTTP(w http.ResponseWriter, req *http.Request) {
   switch req.URL.Path {
   case "/":
      fmt.Fprintf(w,"URL.Path = %q\n", req.URL.Path)
   case "/hello":
      for k, v := range req.Header {
         fmt.Fprintf(w,"Header[%q] = %q \n", k, v)
      }
   default:
      fmt.Fprintf(w,"404 NOT FOUND: %s\n", req.URL)
   }
}

func main() {
   engine := new(Engine)
    //此處後者的引數填入engine,讓請求交由我們實現的engine例項來處理
   log.Fatal(http.ListenAndServe(":9090",engine))
}

三、實現簡單的GET,POST方法,實現框架簡單原型

設計自己的Engine結構體,然後將原來啟動web專案的

   log.Fatal(http.ListenAndServe(":9090",engine))

封裝成Run(addr),並將第二個引數的預設例項,直接寫為我們的engine例項。

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

這裡engine涉及到一個轉換,實現了介面方法的struct轉換為介面型別

我們要實現的GET,POST方法。作用是,將路由和方法,註冊進來,然後在監聽的時候進行查詢和處理。

這裡設計一個router放入到我們的Engine結構體中:

// Engine 實現了"net/http"標準庫中的 Handler 介面中的ServeHTTP方法
type Engine struct {
   //用於儲存路由處理方法
   //key是方法型別加路徑,value是使用者的處理方法
   router map[string]HandlerFunc
}

我們的GET和POST方法就是這樣實現的,呼叫addRoute方法,將路由和響應方法存入到router中:

// GET 實現的是Engine的處理GET請求的方法,註冊到router中
func (engine *Engine) GET(path string, handler HandlerFunc) {
   engine.addRoute("GET", path, handler)
}

// POST 同上
func (engine *Engine) POST(path string, handler HandlerFunc) {
   engine.addRoute("POST", path, handler)
}

這裡就是addRoute方法,method-path作為key,handler作為value

// Engine 中 addRoute 方法,在 router map[string]HandlerFunc 中存入對應處理方法
//存入形式為例如:{ "GET-/index" : 定義的處理方法 }engine
func (engine *Engine) addRoute(method string, path string, handler HandlerFunc) {
   key := method + "-" + path
   engine.router[key] = handler
}

我們再重寫Handler介面中的ServeHTTP方法,通過req請求的路由查詢router中註冊的方法執行。

// ServeHTTP 方法的實現,用於實現處理HTTP請求
// 先解析req對應傳入的路徑,查詢router中,如果有相應的處理方法,則執行處理方法,如果沒有則返回找不到的提示
// 來自Handler介面
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   key := req.Method + "-" + req.URL.Path
   //根據請求req中的資料,從router中取出對應的方法
   if handler, ok := engine.router[key]; ok {
      handler(w, req)
   } else {
      fmt.Fprintf(w, "could not find the route: %s\n", req.URL)
   }
}

雛形的全部原始碼:

/*
@Time : 2021/8/16 下午4:03
@Author : Mrxuexi
@File : Ez
@Software: GoLand
*/

package Ez

import (
   "fmt"
   "net/http"
)

// HandlerFunc 是Ez框架中定義的對請求的響應處理方法,預設傳入這兩個引數,針對http請求處理
type HandlerFunc func(http.ResponseWriter, *http.Request)

// Engine 實現了"net/http"標準庫中的 Handler 介面中的ServeHTTP方法
type Engine struct {
   //用於儲存路由處理方法
   //key是方法型別加路徑,value是使用者的處理方法
   router map[string]HandlerFunc
}

// ServeHTTP 方法的實現,用於實現處理HTTP請求
// 先解析req對應傳入的路徑,查詢router中,如果有相應的處理方法,則執行處理方法,如果沒有則返回找不到的提示
// 來自Handler介面
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   key := req.Method + "-" + req.URL.Path
   //根據請求req中的資料,從router中取出對應的方法
   if handler, ok := engine.router[key]; ok {
      handler(w, req)
   } else {
      fmt.Fprintf(w, "could not find the route: %s\n", req.URL)
   }
}

// New 是Ez.Engine的建構函式
func New() *Engine {
   return &Engine{router: make(map[string]HandlerFunc)}
}

// Engine 中 addRoute 方法,在 router map[string]HandlerFunc 中存入對應處理方法
//存入形式為例如:{ "GET-/index" : 定義的處理方法 }engine
func (engine *Engine) addRoute(method string, path string, handler HandlerFunc) {
   key := method + "-" + path
   engine.router[key] = handler
}

// GET 實現的是Engine的處理GET請求的方法,註冊到router中
func (engine *Engine) GET(path string, handler HandlerFunc) {
   engine.addRoute("GET", path, handler)
}

// POST 同上
func (engine *Engine) POST(path string, handler HandlerFunc) {
   engine.addRoute("POST", path, handler)
}

// Run 用於啟動服務,直接制定用該路由的engine
func (engine *Engine) Run(addr string) (err error) {
   return http.ListenAndServe(addr, engine)
}

嘗試使用:

/*
@Time : 2021/8/16 下午4:01
@Author : mrxuexi
@File : main
@Software: GoLand
*/
package main

import (
   "Ez"
   "fmt"
   "net/http"
)
func main() {
   r := Ez.New()
   r.GET("/hello", func(w http.ResponseWriter, req *http.Request) {
      fmt.Fprintf(w,"Hello")
   })
   r.Run(":9090")
}

成功!
後續我們繼續完善內容

參考:

[1]: https://github.com/geektutu/7days-golang/tree/master/gee-web ""gee""
[2]: https://github.com/gin-gonic/gin ""gin""

相關文章