Go 語言 Web 編程系列(四)—— 基於 gorilla/mux 包實現路由定義:基本使用篇

  • 2019 年 12 月 31 日
  • 筆記

1、功能簡介

前面我們介紹了 Go 官方標準庫 net/http 自帶的 DefaultServeMux 底層實現,通過 DefaultServeMux 提供的路由處理器雖然簡單易上手,但是存在很多不足,比如:

  • 不支援參數設定,例如 /user/:uid 這種泛類型匹配;
  • 對 REST 風格介面支援不友好,無法限制訪問路由的方法;
  • 對於擁有很多路由規則的應用,編寫大量路由規則非常繁瑣。

為此,我們可以使用第三方庫 gorilla/mux 提供的更加強大的路由處理器(mux 代表 HTTP request multiplexer,即 HTTP 請求多路復用器),和 http.ServeMux 實現原理一樣,gorilla/mux 提供的路由器實現類 mux.Router 也會匹配用戶請求與系統註冊的路由規則,然後將用戶請求轉發過去。

mux.Router 主要具備以下特性:

  • 實現了 http.Handler 介面,所以和 http.ServeMux 完全兼容;
  • 可以基於 URL 主機、路徑、前綴、scheme、請求頭、請求參數、請求方法進行路由匹配;
  • URL 主機、路徑、查詢字元串支援可選的正則匹配;
  • 支援構建或反轉已註冊的 URL 主機,以便維護對資源的引用;
  • 支援路由嵌套(類似 Laravel 中的路由分組),以便不同路由可以共享通用條件,比如主機、路徑前綴等。

2、使用入門

開始使用 mux.Router 之前,需要安裝這個擴展包:

go get -u github.com/gorilla/mux

然後在我們可以這樣實現創建第一個Web應用中演示的註冊路由:


package main    import (      "fmt"      "github.com/gorilla/mux"      "log"      "net/http"  )    func sayHelloWorld(w http.ResponseWriter, r *http.Request)  {      w.WriteHeader(http.StatusOK)  // 設置響應狀態碼為 200      fmt.Fprintf(w, "Hello, World!")  // 發送響應到客戶端  }    func main()  {      r := mux.NewRouter()      r.HandleFunc("/hello", sayHelloWorld)      log.Fatal(http.ListenAndServe(":8080", r))  }

main 函數中的第一行顯式初始化了 mux.Router 作為路由器,然後在這個路由器中註冊路由規則,最後將這個路由器傳入 http.ListenAndServe 方法,整個調用過程和之前並無二致,因為我們前面說了,mux.Router 也實現了 Handler 介面。

運行這段程式碼,在瀏覽器訪問 http://localhost:8080/hello,即可渲染出如下結果:

Hello, World!

3、路由參數

現在,我們想要在路由定義中設置路由參數,例如 /hello/world/hello/學院君,這可以通過如下方式來實現:

r.HandleFunc("/hello/{name}", sayHelloWorld)

你甚至還可以通過正則表達式限制參數字元:


r.HandleFunc("/hello/{name:[a-z]+}", sayHelloWorld)

以上規則表示路由參數只能是小寫字母,不支援其它字元。

相應地,在閉包處理函數中,我們需要這樣解析路由參數:

func sayHelloWorld(w http.ResponseWriter, r *http.Request)  {      params := mux.Vars(r)      w.WriteHeader(http.StatusOK)  // 設置響應狀態碼為 200      fmt.Fprintf(w, "Hello, %s!", params["name"])  // 發送響應到客戶端  }

重啟伺服器,這次,我們可以通過 http://localhost:8080/hello/xueyuanjun 這種方式請求路由了:

Hello, xueyuanjun!

如果參數中包含中文,則返回 404 響應,表示路由匹配失敗:

4、自定義處理器

http.ServeMux 一樣,在 mux.Router 中,還可以將請求轉發到自定義的處理器類,而不是閉包函數:

package main    import (      "fmt"      "github.com/gorilla/mux"      "log"      "net/http"  )    func sayHelloWorld(w http.ResponseWriter, r *http.Request)  {      params := mux.Vars(r)      w.WriteHeader(http.StatusOK)  // 設置響應狀態碼為 200      fmt.Fprintf(w, "Hello, %s!", params["name"])  // 發送響應到客戶端  }    type HelloWorldHandler struct {}    func (handler *HelloWorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)  {      params := mux.Vars(r)      w.WriteHeader(http.StatusOK)  // 設置響應狀態碼為 200      fmt.Fprintf(w, "你好, %s!", params["name"])  // 發送響應到客戶端  }    func main()  {      r := mux.NewRouter()      r.HandleFunc("/hello/{name:[a-z]+}", sayHelloWorld)      r.Handle("/zh/hello/{name}", &HelloWorldHandler{})      log.Fatal(http.ListenAndServe(":8080", r))  }

上篇教程介紹的自定義路由處理器一樣,這裡自定義的 HelloWorldHandler 也要實現 Handler 介面聲明的 ServeHTTP 方法,調用方式和之前一樣,只是需要通過 r.Handle 方法,因為第二個參數傳入的是處理器實例,而不是閉包函數。

重啟伺服器,我們就可以訪問如下 HTTP 路由了:

以上,就是 gorilla/mux 擴展包提供的路由器 mux.Router 的基本使用,下篇教程,我們繼續介紹它的更多路由匹配功能,包括限定主機、請求方法、scheme、路徑前綴、請求頭、查詢字元串等,通過 mux.Router,我們甚至可以構建出比 Laravel 路由還要強大的路由匹配規則。