自定义路由实现方式核心需要用到http包提供的ListenAndServe方法,那就从ListenAndServe源码开始入手。

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

//从入参可知第一个参数是地址,第二个参数是一个Handler,Handler是一个interface,包含一个需要实现的ServeHTTP方法
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

//继续深入看一下这个addr是什么
func (srv *Server) ListenAndServe() error {
	if srv.shuttingDown() {
		return ErrServerClosed
	}
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
    //从此处可以看出,addr是一个监听端口,写法为":7670",有兴趣的朋友可以继续深入
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.Serve(ln)
}

//继续深入
func (srv *Server) Serve(l net.Listener) error {
	//...
    //...
	for {
		rw, err := l.Accept()
		if err != nil {
			select {
			case <-srv.getDoneChan():
				return ErrServerClosed
			default:
			}
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return err
		}
		connCtx := ctx
		if cc := srv.ConnContext; cc != nil {
			connCtx = cc(connCtx, rw)
			if connCtx == nil {
				panic("ConnContext returned nil")
			}
		}
		tempDelay = 0
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew, runHooks) // before Serve can return
		go c.serve(connCtx)
	}
}

//继续深入
func (c *conn) serve(ctx context.Context) {
    //...
    //...
    //核心是使用c.server实现的ServeHTTP,c.server是ListenAndServe中初始化的结构体server := &Server{Addr: addr, Handler: handler},由此可知,我们只需要自定义一个结构体去实现ServeHTTP方法,并将对应结构体当作ListenAndServe参数传入就可以实现自定义ServeHTTP
    serverHandler{c.server}.ServeHTTP(w, w.req)
    //...
    //...
}

原理明白后,简单测试一下:

//定一个类
type test struct {
}

//自定类实现ServeHTTP
func (t *test) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	fmt.Printf("%+v", w)
	res, _ := w.Write([]byte("sdasdasdasdsda"))
	fmt.Println(res)
	fmt.Printf("%+v", req)
}

//主函数启动
func main() {
	a := &test{}
	log.Fatal(http.ListenAndServe(":7670", a))
}


//终端测试
//curl 'http://127.0.0.1:7670/test/ob767' -v
//*   Trying 127.0.0.1...
//* TCP_NODELAY set
//* Connected to 127.0.0.1 (127.0.0.1) port 7670 (#0)
//> GET /test/ob767 HTTP/1.1
//> Host: 127.0.0.1:7670
//> User-Agent: curl/7.64.1
//> Accept: */*
//> 
//< HTTP/1.1 200 OK
//< Date: Tue, 19 Jul 2022 03:26:55 GMT
//< Content-Length: 14
//< Content-Type: text/plain; charset=utf-8
//< 
//* Connection #0 to host 127.0.0.1 left intact
//sdasdasdasdsda* Closing connection 0

//看一下服务终端的打印
//fmt.Printf("%+v", w) &{conn:0xc000132960 req:0xc0000b0000 reqBody:{} cancelCtx:0x10cb3a0 wroteHeader:false wroteContinue:false wants10KeepAlive:false wantsClose:false canWriteContinue:0 writeContinueMu:{state:0 sema:0} w:0xc00009a100 cw:{res:0xc0000bc000 header:map[] wroteHeader:false chunking:false} handlerHeader:map[] calledHeader:false written:0 contentLength:-1 status:0 closeAfterReply:false requestBodyLimitHit:false trailers:[] handlerDone:0 dateBuf:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] clenBuf:[0 0 0 0 0 0 0 0 0 0] statusBuf:[0 0 0] closeNotifyCh:0xc0000ba000 didCloseNotify:0}
//res, _ := w.Write([]byte("sdasdasdasdsda")) 
//fmt.Println(res) 14
//fmt.Printf("%+v", req) &{Method:GET URL:/test/ob767 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Accept:[*/*] User-Agent:[curl/7.64.1]] Body:{} GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:127.0.0.1:7670 Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:127.0.0.1:58419 RequestURI:/test/ob767 TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc00009a0c0}

        通过测试发现,req能获取到http请求相关数据,那我们针对这些数据就可以进行路由方式的自定义,而w是一个回显结构体实例,通过w可以实现数据的回显。该方法可以结合反射可以实现自动设置路由,是比较优雅的路由加载方式。