当前位置: 首页 > 工具软件 > gorilla/mux > 使用案例 >

【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

皮煜
2023-12-01

一.概述

gorilla/mux程序包,是一个强大的url路由和调度器,它具有小巧但是稳定高效的特性。

不仅可以支持正则路由,还可以按照Method、header、host等信息匹配,可以从我们设定的路由表达式中提取出参数方便上层应用,而且完全兼容http.ServerMux。


二.对比: gorilla/mux与net/http DefaultServeMux

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 中的路由分组),以便不同路由可以共享通用条件,比如主机、路径前缀等。



三.简单使用

安装程序包:

go get -u github.com/gorilla/mux

样例①:
基于Golang的简单web服务程序开发——CloudGo
主要的相关函数为NewServer函数与initRoutes函数:

// NewServer configures and returns a Server.
func NewServer() *negroni.Negroni {

	formatter := render.New(render.Options{
		Directory:  "templates",
		Extensions: []string{".html"},
		IndentJSON: true,
	})

	n := negroni.Classic()
	mx := mux.NewRouter()

	initRoutes(mx, formatter)

	n.UseHandler(mx)
	return n
}

func initRoutes(mx *mux.Router, formatter *render.Render) {
	webRoot := os.Getenv("WEBROOT")
	if len(webRoot) == 0 {
		if root, err := os.Getwd(); err != nil {
			panic("Could not retrive working directory")
		} else {
			webRoot = root
			//fmt.Println(root)
		}
	}

	mx.HandleFunc("/api/test", apiTestHandler(formatter)).Methods("GET")
	mx.HandleFunc("/", homeHandler(formatter)).Methods("GET")
	mx.HandleFunc("/user", userHandler).Methods("POST")
	mx.PathPrefix("/").Handler(http.FileServer(http.Dir(webRoot + "/assets/")))
}

样例②:

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!



四.源码简析

gorilla/mux源码可分为context、mux、regex、route四个部分,在CloudGo项目开发过程中,我主要使用了NewRouter、HandleFunc和PathPrefix这三个函数。下面对这三个函数进行分析:

1.NewRouter函数

Router是一个结构体,如下:

type Router struct {
    // Configurable Handler to be used when no route matches.
    NotFoundHandler http.Handler

    // Configurable Handler to be used when the request method does not match the route.
    MethodNotAllowedHandler http.Handler

    // Parent route, if this is a subrouter.
    parent parentRoute
    // Routes to be matched, in order.
    routes []*Route
    // Routes by name for URL building.
    namedRoutes map[string]*Route
    // See Router.StrictSlash(). This defines the flag for new routes.
    strictSlash bool
    // See Router.SkipClean(). This defines the flag for new routes.
    skipClean bool
    // If true, do not clear the request context after handling the request.
    // This has no effect when go1.7+ is used, since the context is stored
    // on the request itself.
    KeepContext bool
    // see Router.UseEncodedPath(). This defines a flag for all routes.
    useEncodedPath bool
}

调用NewRouter函数可用来实例化一个Router:

// NewRouter returns a new router instance.
func NewRouter() *Router {
    return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
}

这里可以看见,它开辟了一个装Route指针的map,然后默认该Router的KeepContextfalse,意思是在请求被处理完之后清除该请求的上下文。



2.HandleFunc函数

// HandleFunc registers a new route with a matcher for the URL path.
// See Route.Path() and Route.HandlerFunc().
func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
	*http.Request)) *Route {
	return r.NewRoute().Path(path).HandlerFunc(f)
}

若只观察HandleFunc函数,会发现其代码只有几行,其主要功能是使用URL的匹配器注册新路由。

gorilla/mux的HandleFunc函数功能很强大,主要有以下功能:

设置路由的HTTP方法

限制路由处理器只处理指定的HTTP方法的请求:

router.HandleFunc("/books/{title}", CreateBook).Methods("POST")
router.HandleFunc("/books/{title}", ReadBook).Methods("GET")
router.HandleFunc("/books/{title}", UpdateBook).Methods("PUT")
router.HandleFunc("/books/{title}", DeleteBook).Methods("DELETE")

上面的就是一组可以响应具体HTTP方法的RESTful风格的接口的路由。

设置路由的域名

限制路由处理器只处理访问指定域名加路由的请求:

router.HandleFunc("/books/{title}", BookHandler).Host("www.mybookstore.com")

限制HTTP 方案

将请求处理程序可响应的HTTP方案限制为http或者https

router.HandleFunc("/secure", SecureHandler).Schemes("https")
router.HandleFunc("/insecure", InsecureHandler).Schemes("http")

设置路径前缀和子路由

bookrouter := router.PathPrefix("/books").Subrouter()
bookrouter.HandleFunc("/", AllBooks)
bookrouter.HandleFunc("/{title}", GetBook)



3.PathPrefix函数

// PathPrefix registers a new route with a matcher for the URL path prefix.
// See Route.PathPrefix().
func (r *Router) PathPrefix(tpl string) *Route {
	return r.NewRoute().PathPrefix(tpl)
}

PathPrefix函数源码也只有几行,它的功能只是简单地增加URL的前缀,通常结合HandleFunc函数和Handler函数来使用。



五.References

  1. 基于 gorilla/mux 包实现路由定义和请求分发:基本使用
  2. gorilla/mux类库解析
  3. Gorilla源码分析之gorilla/mux源码分析
  4. 从一个例子分析gorilla/mux源码
  5. gorilla/mux官方GitHub
 类似资料: