1. 程式人生 > 程式設計 >golang常用庫之gorilla/mux-http路由庫使用詳解

golang常用庫之gorilla/mux-http路由庫使用詳解

golang常用庫:gorilla/mux-http路由庫使用
golang常用庫:配置檔案解析庫-viper使用
golang常用庫:操作資料庫的orm框架-gorm基本使用

一:golang自帶路由介紹

golang自帶路由庫 http.ServerMux ,實際上是一個 map[string]Handler,是請求的url路徑和該url路徑對於的一個處理函式的對映關係。這個實現比較簡單,有一些缺點:

不支援引數設定,例如/user/:uid 這種泛型型別匹配無法很友好的支援REST模式,無法限制訪問方法(POST,GET等)也不支援正則

二:gorilla/mux路由

github地址:https://github.com/gorilla/mux

http://www.gorillatoolkit.org/pkg/mux
https://github.com/gorilla/mux#examples

上面所指出來的glang自帶路由的缺點,gorilla/mux 都具備,而且還相容 http.ServerMux。除了支援路徑正則,命名路由,還支援中介軟體等等功能。所以mux是一個短小精悍,功能很全的路由。

1. 普通路由

示例 demo1.go

package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
)

func main() {
	r := mux.NewRouter()
	//普通路由
	r.HandleFunc("/",IndexHandler)
	r.HandleFunc("/products",ProductsHandler)

	http.ListenAndServe(":8080",r)
}

func IndexHandler(w http.ResponseWriter,r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w,"hello world")
}

func ProductsHandler(w http.ResponseWriter,"hello,Products")
}

上面mux的普通路由是不是似曾相識,跟golang標準庫用法一樣

在瀏覽器訪問:http://localhost:8080/products
輸出:hello,Products

2. 引數路由

引數路由,可以是普通路由,還可以是正則匹配
示例 demo2.go:

package main

import (
	"net/http"

	"fmt"

	"github.com/gorilla/mux"
)

//路由引數
func main() {
	r := mux.NewRouter()
	//1. 普通路由引數
	// r.HandleFunc("/articles/{title}",TitleHandler)

	//2. 正則路由引數,下面例子中限制為英文字母
	r.HandleFunc("/articles/{title:[a-z]+}",TitleHandler)

	http.ListenAndServe(":8080",r)
}

//https://github.com/gorilla/mux#examples
func TitleHandler(w http.ResponseWriter,r *http.Request) {
	vars := mux.Vars(r) // 獲取引數
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w,"title: %v\n",vars["title"])
}


第1個普通路由引數,就是啥引數都可以,不管是字母,數字,還是中文等
第2個正則路由引數,限制了只能是英文字母,否則會報 404 page not found

3. 路由匹配Matching Routes

https://github.com/gorilla/mux#matching-routes
我們也可以限制路由或者子路由。

3.1 匹配host

r := mux.NewRouter()
//只匹配 www.example.com
r.Host("www.example.com")
// 動態匹配子路由
r.Host("{subdomain:[a-z]+}.example.com")

3.2 更多的一些其他匹配

見下面的更多匹配的例子:

r := mux.NewRouter()

r.PathPrefix("/products/") //字首匹配
r.Methods("GET","POST") //請求方法匹配
r.Schemes("https") //schemes
r.Headers("X-Requested-With","XMLHttpRequest") //header 匹配
r.Queries("key","value") //query的值匹配

// 使用者自定義方法 匹配
r.MatcherFunc(func(r *http.Request,rm *RouteMatch) bool { 
 return r.ProtoMajor == 0
})

把上面的聯合起來在一個單獨的route裡

r.HandleFunc("/products",ProductsHandler).
 Host("www.example.com").
 Methods("GET").
 Schemes("http")

3.3 子路由匹配

Subrouter()可以設定子路由

r := mux.NewRouter()
s := r.Host("www.example.com").Subrouter()

s.HandleFunc("/products/",ProductsHandler)
s.HandleFunc("/products/{key}",ProductHandler)
s.HandleFunc("/articles/{category}/{id:[0-9]+}",ArticleHandler)

3.4 多個路由匹配的順序

如果有多個路由新增到路由器裡面,那麼匹配順序是怎麼樣?按照新增的先後順序匹配。比如有2個路由都匹配了,那麼優先匹配第一個路由。

r := mux.NewRouter()
r.HandleFunc("/specific",specificHandler)
r.PathPrefix("/").Handler(catchAllHandler)

4. 設定路由字首

PathPrefix()設定路由字首

r := mux.NewRouter()

//PathPrefix() 可以設定路由字首
product := r.PathPrefix("/products").HandleFunc("/",ProductsHandler)

路由字首一般情況下不會單獨使用,而是和子路由結合起來用,實現路由分組

5. 分組路由

可以根據前面的子路由和路由字首的功能,綜合運用就可以設定分組路由了
例項:grouprouter.go

package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
)

//子路由,分組路由
func main() {
	r := mux.NewRouter()

	//PathPrefix() 可以設定路由字首,設定路由字首為products
	products := r.PathPrefix("/products").Subrouter()
	//"http://localhost:8080/products/",最後面的斜線一定要,不然路由不正確,頁面出現404
	products.HandleFunc("/",ProductsHandler)
	//"http://localhost:8080/products/{key}"
	products.HandleFunc("/{key}",ProductHandler)

	users := r.PathPrefix("/users").Subrouter()
	// "/users"
	users.HandleFunc("/",UsersHandler)
	// "/users/id/引數/name/引數"
	users.HandleFunc("/id/{id}/name/{name}",UserHandler)

	http.ListenAndServe(":8080",r)
}

func ProductsHandler(w http.ResponseWriter,"%s","products")
}

func ProductHandler(w http.ResponseWriter,r *http.Request) {
	vars := mux.Vars(r) //獲取路由的值
	fmt.Fprintf(w,"key: %s",vars["key"])
}

func UsersHandler(w http.ResponseWriter,r *http.Request) {
	fmt.Fprintf(w," %s \r\n","users handler")
}

func UserHandler(w http.ResponseWriter,r *http.Request) {
	vars := mux.Vars(r) //獲取值
	id := vars["id"]
	name := vars["name"]
	fmt.Fprintf(w,"id: %s,name: %s \r\n",id,name)
}

6. 路由中介軟體

https://github.com/gorilla/mux#middleware
Mux middlewares are defined using the de facto standard type: 在mux中路由中介軟體的定義

type MiddlewareFunc func(http.Handler) http.Handler

示例1:middleware1.go

package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/",handler)

	r.Use(loggingMiddleware)

	http.ListenAndServe(":8080",r)
}

func loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter,r *http.Request) {
		//Do stuff here
		fmt.Println(r.RequestURI)
		fmt.Fprintf(w,"%s\r\n",r.URL)
		// Call the next handler,which can be another middleware in the chain,or the final handler.
		next.ServeHTTP(w,r)
	})
}

func handler(w http.ResponseWriter,r *http.Request) {
	w.Write([]byte("handle middleware"))
	fmt.Println("print handler")
}

示例2:middleware2.go

在來看一個複雜點的例子:

package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
)

type authMiddleware struct {
	tokenUsers map[string]string
}

func (amw *authMiddleware) Populate() {
	amw.tokenUsers = make(map[string]string)
	amw.tokenUsers["000"] = "user0"
	amw.tokenUsers["aaa"] = "userA"
	amw.tokenUsers["05ft"] = "randomUser"
	amw.tokenUsers["deadbeef"] = "user0"
}

func (amw *authMiddleware) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter,r *http.Request) {
		token := strings.Trim(r.Header.Get("X-Session-Token")," ")
		if token == "" {
			fmt.Fprintf(w,"token is error \r\n")
		}

		if user,found := amw.tokenUsers[token]; found {
			//we found the token in out map
			fmt.Printf("Authenticated user: %s\n",user)
			fmt.Fprintf(w,"Authenticated user: %s\n",user)
			// Pass down the request to the next middleware (or final handler)
			next.ServeHTTP(w,r)
		} else {
			// Write an error and stop the handler chain
			http.Error(w,"Forbidden",http.StatusForbidden)
		}
	})
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/",handler)

	amw := authMiddleware{}
	amw.Populate()

	r.Use(amw.Middleware)

	http.ListenAndServe(":8080",r)
}

func handler(w http.ResponseWriter,r *http.Request) {
	w.Write([]byte("handler"))
}

用 insomnia 軟體測試,如下圖:

golang常用庫之gorilla/mux-http路由庫使用詳解

X-Session-Token=aaa 返回時正確


那-Session-Token=aaaa 呢

golang常用庫之gorilla/mux-http路由庫使用詳解

返回 403 了

7. Walking Routes 遍歷註冊的所有路由

package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
)

func handler(w http.ResponseWriter,r *http.Request) {
	return
}

//https://github.com/gorilla/mux#walking-routes
func main() {
	r := mux.NewRouter()
	r.HandleFunc("/",handler)
	r.HandleFunc("/products",handler).Methods("POST")
	r.HandleFunc("/articles",handler).Methods("GET")
	r.HandleFunc("/articles/{id}",handler).Methods("GET","PUT")
	r.HandleFunc("/authors",handler).Queries("surname","{surname}")
	err := r.Walk(func(route *mux.Route,router *mux.Router,ancestors []*mux.Route) error {
		pathTemplate,err := route.GetPathTemplate()
		if err == nil {
			fmt.Println("ROUTE:",pathTemplate)
		}
		pathRegexp,err := route.GetPathRegexp()
		if err == nil {
			fmt.Println("Path regexp:",pathRegexp)
		}
		queriesTemplates,err := route.GetQueriesTemplates()
		if err == nil {
			fmt.Println("Queries templates:",strings.Join(queriesTemplates,","))
		}
		queriesRegexps,err := route.GetQueriesRegexp()
		if err == nil {
			fmt.Println("Queries regexps:",strings.Join(queriesRegexps,"))
		}
		methods,err := route.GetMethods()
		if err == nil {
			fmt.Println("Methods:",strings.Join(methods,"))
		}
		fmt.Println()
		return nil
	})

	if err != nil {
		fmt.Println(err)
	}

	http.Handle("/",r)
	http.ListenAndServe(":8080",nil)
}

8. 其他示例

請求方法限制

demo3.go:

package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
)

// 請求方法的限制,Methods()
func main() {
	r := mux.NewRouter()

	r.HandleFunc("/products",ProductsHandler).Methods("GET","POST")

	r.Handle("/products/{id}",&ProductsIdHandler{}).Methods("GET")
	http.ListenAndServe(":8080",products! ")
}

type ProductsIdHandler struct{}

func (handler *ProductsIdHandler) ServeHTTP(w http.ResponseWriter,r *http.Request) {
	vars := mux.Vars(r)
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w,"products id: %s",vars["id"])
}

請求頭限制

在路由定義中可以通過Headers() 方法來限制設定請求頭的匹配。
demo4.go

package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

// 請求頭的限制,用Headers() 來限制
func main() {
	r := mux.NewRouter()

	r.HandleFunc("/products",func(w http.ResponseWriter,r *http.Request) {
		header := "Request-Limit-Test"
		fmt.Fprintf(w,"contain headers: %s = %s \n",header,r.Header[header])
	}).Headers("Request-Limit-Test","RequestLimitTest").Methods("POST")

	http.ListenAndServe(":8080",r)
}

自定義匹配規

用 MatcherFunc() 來自定義規則
示例 demo5.go:**

package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

//自定義匹配 MatcherFunc()
func main() {
	r := mux.NewRouter()

	r.HandleFunc("/products/matcher",r *http.Request) {
		fmt.Fprintf(w,"FormValue: %s ",r.FormValue("func"))
	}).MatcherFunc(func(req *http.Request,match *mux.RouteMatch) bool {
		b := false
		if req.FormValue("func") == "matcherfunc" {
			b = true
		}
		return b
	})

	http.ListenAndServe(":8080",r)
}

在瀏覽器中:http://127.0.0.1:8080/products/matcher?func=matcherfunc
輸出:FormValue: matcherfunc

命名路由Registered URLs

namerouter.go

package main

import (
	"fmt"
	"github.com/gorilla/mux"
	// "log"
	"net/http"
)

// 命名路由 Name(),獲取路由URL, URL()
func main() {
	r := mux.NewRouter()
	r.HandleFunc("/products/{category}/{id:[0-9]+}",ProductHandler).Name("product")

	//獲取路由的URL
	url1,err := r.Get("product").URL()
	fmt.Println(err) //error: mux: number of parameters must be multiple of 2,got [/]
	if err == nil {
		fmt.Println("get URL: \r\n",url1)
	}

	//獲取路由的url後,也可以拼裝你需要的URL
	url2,err := r.Get("product").URL("category","tech","id","13")
	if err == nil {
		fmt.Println("new url: ",url2) //new url: /products/tech/13
	}

	http.ListenAndServe(":8080",r)
}

func ProductHandler(w http.ResponseWriter,r *http.Request) {
	w.WriteHeader(http.StatusOK)
	vars := mux.Vars(r)

	fmt.Fprintf(w,"url: %s,category: %s,id: %s",r.URL,vars["category"],vars["id"])
	//瀏覽器: http://localhost:8080/products/id/23

	//output
	//url: /products/id/23,category: id,id: 23
}

根據命名的路由來獲取路由URLr.Get("product").URL()

三:參考

https://github.com/gorilla/mux

到此這篇關於golang常用庫之gorilla/mux-http路由庫使用詳解的文章就介紹到這了,更多相關gorilla mux-http路由庫內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!