golang常用库:gorilla/mux-http路由库使用

golang常用库:配置文件解析库/管理工具-viper使用

golang常用库:操作数据库的orm框架-gorm基本使用

一:golang自带路由介绍

golang自带路由库 http.ServerMux ,实际上是一个 map[string]Handler,是请求的url路径和该url路径对于的一个处理函数的映射关系。这个实现比较简单,有一些缺点:

  1. 不支持参数设定,例如/user/:uid 这种泛型类型匹配
  2. 无法很友好的支持REST模式,无法限制访问方法(POST,GET等)
  3. 也不支持正则

二: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, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "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, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "%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 软件测试,如下图

X-Session-Token=aaa 返回时正确


那 -Session-Token=aaaa 呢

返回 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", r)
} func ProductsHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "hello, 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", func(w http.ResponseWriter, 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
}

根据命名的路由来获取路由URL r.Get("product").URL()


也欢迎到我的公众号继续讨论:九卷技术录-golang常用库:gorilla/mux-http路由库使用

三:参考

https://github.com/gorilla/mux

golang常用库:gorilla/mux-http路由库使用的更多相关文章

  1. golang常用库:cli命令行/应用程序生成工具-cobra使用

    golang常用库:cli命令行/应用程序生成工具-cobra使用 一.Cobra 介绍 我前面有一篇文章介绍了配置文件解析库 Viper 的使用,这篇介绍 Cobra 的使用,你猜的没错,这 2 个 ...

  2. Golang 源码剖析:log 标准库

    Golang 源码剖析:log 标准库 原文地址:Golang 源码剖析:log 标准库 日志 输出 2018/09/28 20:03:08 EDDYCJY Blog... 构成 [日期]<空格 ...

  3. Golang调用windows下的dll动态库中的函数

    Golang调用windows下的dll动态库中的函数 使用syscall调用. package main import ( "fmt" "syscall" & ...

  4. [golang]golang如何覆盖输出console,实现进度条;golang一个骚气的进度提示库

    [golang]golang如何覆盖输出console,实现进度条 package main import( "fmt" "os" "time&quo ...

  5. 数据结构和算法(Golang实现)(7)简单入门Golang-标准库

    使用标准库 一.避免重复造轮子 官方提供了很多库给我们用,是封装好的轮子,比如包fmt,我们多次使用它来打印数据. 我们可以查看到其里面的实现: package fmt func Println(a ...

  6. Golang调用windows下的dll动态库中的函数 Golang 编译成 DLL 文件

    Golang调用windows下的dll动态库中的函数 package main import ( "fmt" "syscall" "time&quo ...

  7. 从源码对比DefaultServeMux 与 gorilla/mux

    从源码对比DefaultServeMux 与 gorilla/mux DefaultServeMux Golang自带的net/http库中包含了DefaultServeMux方法,以此可以搭建一个稳 ...

  8. gorilla/mux类库解析

    golang自带的http.SeverMux路由实现简单,本质是一个map[string]Handler,是请求路径与该路径对应的处理函数的映射关系.实现简单功能也比较单一: 不支持正则路由, 这个是 ...

  9. boost库的安装,使用,介绍,库分类

    1)首先去官网下载boost源码安装包:http://www.boost.org/ 选择下载对应的boost源码包.本次下载使用的是 boost_1_60_0.tar.gz (2)解压文件:tar - ...

  10. 第一百六十一节,封装库--JavaScript,完整封装库文件

    封装库--JavaScript,完整封装库文件 /** *feng_zhuang_ku_1.0版本,js封装库,2016/12/29日:林贵秀 **/ /** 前台调用 * 每次调用$()创建库对象, ...

随机推荐

  1. [转帖]TIDB-Error 1105: Out Of Memory Quota问题解决

    一.背景 复杂sql查询报错 二.原因 单条s q l使用内存默认为1G 三.解决 tiup cluster edit_config tidb-test server_configs: tidb: m ...

  2. [转帖]PostgreSQL 慢查询SQL跟踪

    https://www.cnblogs.com/VicLiu/p/12017704.html PostgreSQL 开启慢SQL捕获在排查问题时是个很有效的手段.根据慢SQL让我在工作中真正解决了实际 ...

  3. [转帖]Day63_Kafka(一)

    第一讲 Kafka基础操作 课程大纲 课程内容 学习效果 掌握目标 Kafka简介 消息队列 掌握 Kafka简介 Kafka分布式环境 Kafka操作 Kafka shell 掌握 Kafka ap ...

  4. [转帖]一次 Java 进程 OOM 的排查分析(glibc 篇)

    https://juejin.cn/post/6854573220733911048 遇到了一个 glibc 导致的内存回收问题,查找原因和实验的的过程是比较有意思的,主要会涉及到下面这些: Linu ...

  5. [转帖]面对龙芯3A5000的逼迫,3A4000要为生存抗争!

      https://baijiahao.baidu.com/s?id=1709233817860985518&wfr=spider&for=pc 龙芯3A5000是龙芯中科自主设计的最 ...

  6. bcc工具的简要学习

    摘要 继续补充假期落下的内容. 其实有很多知识需要学习, 自己掌握的还是偏少一些. bcc的全貌 # 注意 bcc 需要较高的内核. 3.10 系列的内核基本不可用. argdist drsnoop ...

  7. 浪潮的CS5260F CS5260H CS5260Z

    助力国产操作系统新生态!麒麟信安与东方通.浪潮.新华三.长城超云等多家生态伙伴完成产品兼容性认证 作者:湖南麒麟信安科技股份有限公司时间:2022-03-11 16:28:11 我要发布 关键词: 国 ...

  8. Windows 审计日志 安全部分不刷新的解决办法

    现在存在一个问题如图示: 有接近15个小时的日志没有进行记录和展示. 要追查问题比较麻烦. 后来发现必须要手动刷新一下 审计记录才可以实现. 感觉比较奇怪 位置为  计算机配置->windows ...

  9. clion运行单个c和c++文件(.c.cpp)

    运行方法 在clion中安装插件:C/C++Single File Execution 在要执行的cpp文件中添加main函数 在cpp文件的编辑器界面中点右键会出现[Add executable f ...

  10. 开启中文智能之旅:探秘超乎想象的 Llama2-Chinese 大模型世界

    "开启中文智能之旅:探秘超乎想象的 Llama2-Chinese 大模型世界" 1.国内Llama2最新下载地址 本仓库中的代码示例主要是基于Hugging Face版本参数进行调 ...