45

Golang web路由实现方式整理总结

 4 years ago
source link: https://www.tuicool.com/articles/qM7jIjN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Golang web路由实现方式整理总结

一、参考博客文章整理

1.0 链接整理集合

  1. 博客文章代码下载地址
  2. Go的http包详解
  3. Go语言经典库使用分析(七)| 高性能可扩展 HTTP 路由 httprouter
  4. golang自定义路由控制实现(一)
  5. golang自定义路由控制实现(二)-流式注册接口以及支持RESTFUL
  6. Golang学习笔记 - 标准库'net/http'的简析及自制简单路由框架
  7. go web框架的路由分析
  8. golang实现的简单http路由器,用于学习理解http.

1.1 链接文章整理

Go的http包详解

通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程。

  • 首先调用Http.HandleFunc

按顺序做了几件事:

  1. 调用了DefaultServeMux的HandleFunc
  2. 调用了DefaultServeMux的Handle
  3. 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
  • 其次调用http.ListenAndServe(":9090", nil)

按顺序做了几件事情:

  1. 实例化Server
  2. 调用Server的ListenAndServe()
  3. 调用net.Listen("tcp", addr)监听端口
  4. 启动一个for循环,在循环体中Accept请求
  5. 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
  6. 读取每个请求的内容w, err := c.readRequest()
  7. 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
  8. 调用handler的ServeHttp
  9. 在这个例子中,下面就进入到DefaultServeMux.ServeHttp
  10. 根据request选择handler,并且进入到这个handler的ServeHTTP

mux.handler(r).ServeHTTP(w, r)

  1. 选择handler:
  • A 判断是否有路由能满足这个request(循环遍历ServeMux的muxEntry)
  • B 如果有路由满足,调用这个路由handler的ServeHTTP
  • C 如果没有路由满足,调用NotFoundHandler的ServeHTTP

二、Golang Web路由组件实现方式

2.0 实现方式分类

  1. 原生方式

(1)调用http.HandleFunc(2)调用http.ListenAndServe(":8080", nil)

  1. 路由封装重写ServeHTTP方式
  • 路由存储格式:

(1)map[string]http.HandlerFunc格式,其中的string由method和传入参数拼接字符串组成(2)map[string]map[string]http.HandlerFunc,,其中一维的键String表示请求method比如post, get 等。二维的键string表示要匹配的URL地址, http.HandlerFunc当然就是处理URL请求的具体方法。

  • 重写步骤:

2.1 原生方式实现

//http.go
package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", SayhelloName)
    http.ListenAndServe(":8080", nil)
}

func SayhelloName(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "原生HTTP路由测试页面")
}

/*
package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", SayhelloName)
    log.Fatal(http.ListenAndServe(":8080", nil))
    //http.ListenAndServe(":8080", nil)
}

func SayhelloName(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "原生HTTP路由测试页面")
}
*/

原生方式主要是2个操作,第1步使用http.HandleFunc函数导入对应的方法,第二步使用http.ListenAndServe监听对应的端口

2.2 路由封装重写ServeHTTP方式实现

第1种方式 不存储对应路由规则、直接在ServeHTTP判断对应的路由规则方式

你可以直接使用&结构体方式new一个空对象,来实现:

//第一种方式
package main

import (
    "fmt"
    "net/http"
)

type MyMux struct {
}

func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
        sayhelloName(w, r)
        return
    }
    http.NotFound(w, r)
    return
}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello MyMuxRoute!")
}

func main() {
    mux := &MyMux{}                   //&结构体方式
    http.ListenAndServe(":8080", mux) //mux是核心操作代码
}

第2种方式 你也可以通过通过一个NewMyMux方法来实现这个功能,然后通过调用当前NewMyMux方法来实现,主流Web框架都使用这种方法实现对应的路由功能,NewMyMux方法代码:

func NewMyMux() *MyMux {
    return new(MyMux)
}

完整代码

//第二种方式
package main

import (
    "fmt"
    "net/http"
)

type MyMux struct {
}

func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path == "/" {
        sayhelloName(w, r)
        return
    }
    http.NotFound(w, r)
    return
}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello MyMuxRoute!")
}

//新建的&结构体方法,框架中十分常用
func NewMyMux() *MyMux {
    return &MyMux{} //等同于  return new(MyMux)
}

func main() {
    mux := NewMyMux()                 //通过调用一个方法来实现
    http.ListenAndServe(":8080", mux) //mux是核心代码
}

2.3 路由封装重写ServeHTTP方式继续封装代码

我们继续封装代码,将NewMyMux方法和type MyMux struct以及ServeHTTP封装到一个Router包里面,其他的还是在main包里面,03http.go详细代码:

//第二种方式代码封装
//03http.go
package main

import (
    "GoHTTP/01httpBase/03httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改
    "net/http"
)

func main() {
    mux := route.NewMyMux()           //通过调用一个方法来实现
    http.ListenAndServe(":8080", mux) //mux是核心代码
}

封装的路由route.go代码:

//route包函数封装
//route.go
package route

import (
    "fmt"
    "net/http"
)

type MyMux struct {
}

//新建的&结构体方法,框架中十分常用
func NewMyMux() *MyMux {
    return &MyMux{} //等同于  return new(MyMux)
}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello MyMuxRoute!")
}

func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    //下面是路由处理,一般web框架会使用一个封装函数统一处理
    if r.URL.Path == "/" {
        sayhelloName(w, r) //这是所有web框架路由的核心代码
        return
    }
    http.NotFound(w, r)
    return
}

我们增加MyMux结构体来存储http.HandleFunc("/", SayhelloName)和重写NewMyMux方法,增加路由的多样性和灵活性,04http.go代码:

//第二种方式代码封装
//增加NewMyMux结构体属性,方便存储http.HandleFunc("/", SayhelloName)路由规则
//04http.go
package main

import (
    "GoHTTP/01httpBase/03httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改
    "net/http"
)

func main() {
    mux := route.NewMyMux()           //通过调用一个方法来实现
    http.ListenAndServe(":8080", mux) //mux是核心代码
}

route.go代码:

//route包函数封装
//route.go
package route

import (
    "fmt"
    "net/http"
)

/*【对比前代码】
type MyMux struct {
}
*/

type MyMux struct {
    handlers map[string][]*Handler //用于存储http.HandleFunc("/", SayhelloName) 格式的路由规则
}

type Handler struct {
    path string
    http.HandlerFunc
}

/*【对比前代码】
//新建的&结构体方法,框架中十分常用
func NewMyMux() *MyMux {
    return &MyMux{} //等同于  return new(MyMux)
}
*/

func NewMyMux() *MyMux {
    return &MyMux{make(map[string][]*Handler)}
}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello MyMuxRoute!")
}

func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    //下面是路由处理,一般web框架会使用一个封装函数统一处理
    if r.URL.Path == "/" {
        sayhelloName(w, r) //这是所有web框架路由的核心代码
        return
    }
    http.NotFound(w, r)
    return
}

我们继续封装http.ListenAndServe(":8080", mux)功能,提升代码的简洁性,也方便后期其他封装操作

//第二种方式代码封装
//封装http.ListenAndServe(":8080", mux)功能
//05http.go
package main

import (
    "GoHTTP/01httpBase/05httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改
    //"fmt"
    //"net/http"
)

func main() {
    mux := route.NewMyMux() //通过调用一个方法来实现
    mux.Listen(":8080")     //http.ListenAndServe(":8080", mux)
}

route.go代码详情:

//route包函数封装
//route.go
package route

import (
    "fmt"
    "log"
    "net/http"
)

type MyMux struct {
    handlers map[string][]*Handler //用于存储http.HandleFunc("/", SayhelloName) 格式的路由规则
}

type Handler struct {
    path string
    http.HandlerFunc
}

//开启http服务
func (m *MyMux) Listen(port string) {
    err := http.ListenAndServe(port, m)
    if err != nil {
        log.Fatal("开启http服务错误!")
    }
}

func NewMyMux() *MyMux {
    return &MyMux{make(map[string][]*Handler)}
}

func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    //下面是路由处理,一般web框架会使用一个封装函数统一处理
    if r.URL.Path == "/" {
        sayhelloName(w, r) //这是所有web框架路由的核心代码
        return
    }
    http.NotFound(w, r)
    return
}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello MyMuxRoute!")
}

我们继续封装存储路由功能和重写ServeHTTP()功能,增加路由多样性和灵活性,方便后期存储Rest格式接口

//第二种方式代码封装
//封装AddRoute添加路由功能、重写ServeHTTP功能
//06http.go
package main

import (
    "GoHTTP/01httpBase/06httpBase/route" //你复制过去的代码,前面的HTTP/01httpBase/03httpBase/根据需要修改
    "fmt"
    "net/http"
)

func main() {
    r := route.NewMyMux() //通过调用一个方法来实现
    r.AddRoute("GET", "/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Hello Get!")
    })
    //mux.Listen(":8080")
    http.ListenAndServe(":8080", r)
}
//route包函数封装
//route.go
package route

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

type MyMux struct {
    handlers map[string][]*Handler //用于存储http.HandleFunc("/", SayhelloName) 格式的路由规则
}

type Handler struct {
    path string
    http.HandlerFunc
}

//开启http服务
func (m *MyMux) Listen(port string) {
    err := http.ListenAndServe(port, m)
    if err != nil {
        log.Fatal("开启http服务错误!")
    }
}

func NewMyMux() *MyMux {
    return &MyMux{make(map[string][]*Handler)}
}

//添加路由
func (m *MyMux) AddRoute(mode string, path string, fun http.HandlerFunc) {
    m.add(mode, path, fun)
}

//添加路由
/*mode  Post|Get|Put|Delete
 *path  前缀
 *fun    方法
 */
func (m *MyMux) add(mode, path string, fun http.HandlerFunc) {
    h := &Handler{
        strings.ToLower(path),
        fun}

    //fmt调试代码可以删除
    fmt.Println("h ::", &h)
    fmt.Println("strings.ToLower(path) ::", strings.ToLower(path))
    fmt.Println("strings.ToLower(mode) ::", strings.ToLower(mode))

    //下面是存储路由的核心代码,这里的路由m.handlers存储的格式是Get|Post|Put|Delete:String:http.HandlerFunc
    m.handlers[strings.ToLower(mode)] = append(
        m.handlers[strings.ToLower(mode)],
        h,
    )
    fmt.Println("m.handlers", m.handlers[strings.ToLower(mode)])
}

//优化前代码
/*
func (p *MyMux) ServeHTTP2(w http.ResponseWriter, r *http.Request) {
    //下面是路由处理,一般web框架会使用一个封装函数统一处理
    if r.URL.Path == "/" {
        sayhelloName(w, r) //这是所有web框架路由的核心代码
        return
    }
    http.NotFound(w, r)
    return
}
*/

//进行路由分配

func (m *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    //处理静态文件
    url := strings.Split(strings.TrimLeft(r.URL.Path, "/"), "/")

    //调试代码,可以直接删除
    fmt.Println("r", fmt.Sprintf("%+v", r))
    fmt.Println("w", fmt.Sprintf("%+v", w))

    for _, handler := range m.handlers[strings.ToLower(r.Method)] {
        if handler.path == "/"+strings.ToLower(url[0]) {
            handler.ServeHTTP(w, r) //调用的是func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
            return
        }
    }
    http.NotFound(w, r)
    return
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK