还是在继续学习 Go 的路上,曾经在使用 PHP 的时候吃过过度依赖框架的亏。现在学习 Go 的时候决定先打好基础,从标准库学起走。

源码分析

我们知道最简单的建立 http 服务器代码基本上都是这样的:

http.HandleFunc('/', func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello world")
})
http.ListenAndServe(":8080", nil)
8080Hello world
HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
DefaultServeMuxHandleFuncDefaultServeMux
type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
	explicit bool
	h        Handler
	pattern  string
}

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux
DefaultServeMuxnet/httpServeMuxServeMuxHandler
go c.serve(ctx)goroutineHandlerServeHTTP
ServeMuxnet/http
HandleFunc
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	mux.Handle(pattern, HandlerFunc(handler))
}
ServeMuxHandleFuncHandlerFuncHandleHandlerFuncHandler
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}
HandleHandlepatternHandlerServeMuxmap[string]muxEntryHandleHandler
net/http
ServeHTTPmap

自制路由

通过以上的分析,我们可以依样画葫芦,实现自己的路由功能。

package route

import (
	"net/http"
	"strings"
)

// 返回一个Router实例
func NewRouter() *Router {
	return new(Router)
}

// 路由结构体,包含一个记录方法、路径的map
type Router struct {
	Route map[string]map[string]http.HandlerFunc
}

// 实现Handler接口,匹配方法以及路径
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if h, ok := r.Route[req.Method][req.URL.String()]; ok {
		h(w, req)
	}
}

// 根据方法、路径将方法注册到路由
func (r *Router) HandleFunc(method, path string, f http.HandlerFunc) {
	method = strings.ToUpper(method)
	if r.Route == nil {
		r.Route = make(map[string]map[string]http.HandlerFunc)
	}
	if r.Route[method] == nil {
		r.Route[method] = make(map[string]http.HandlerFunc)
	}
	r.Route[method][path] = f
}

使用:

r := route.NewRouter()
r.HandleFunc("GET", "/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello Get!")
})
r.HandleFunc("POST", "/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hello POST!")
})
http.ListenAndServe(":8080", r)

这个例子只是依样画葫芦的简单功能实现。

一个完整的路由框架应该包含更复杂的匹配、错误检测等等功能,大家可以试着自己动手试试。

阅读源码和重复造轮子都是学习的方法。

最后,欢迎大家关注我的博客 http://targetliu.com/