go笔记[2]--路由

本文深入探讨了Go语言中默认ServeMux路由的工作原理,并基于此实现了一个支持正则匹配和多种HTTP方法的自定义路由。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.ServeMux

上一章讲到go接收请求之后的工作流程,但未提到将访问路径与对应的方法匹配

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
}

在ServeMux中,有一个map,装载了muxEntry
muxEntry中有pattern记录路径,h记录对应处理请求的Handler

ServeMux实现了ServeHTTP接口,其中调用了Handler方法,根据请求取到对应的handler然后再调用该handler的ServeHTTP方法

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)//根据request获得对应的handler
    h.ServeHTTP(w, r)
}

Handler方法调用了handler,handler再调用match,通过遍历mux实体map获取与路径匹配的handler

func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    var n = 0
    for k, v := range mux.m {
        if !pathMatch(k, path) {
            continue
        }
        if h == nil || len(k) > n {
            n = len(k)
            h = v.h
            pattern = v.pattern
        }
    }
    return
}

go就是通过这种方式匹配路径与处理方法的

二.支持正则匹配路径的路由

根据上节的分析可以看到go自带的路由是不支持正则匹配的,且不支持GET,POST等不同请求方法的匹配
现在试着DIY一个支持正则匹配的路由

定义一个controllerInfo类型,用于存放正则表达式以及对应的controller的类型


type controllerInfo struct {
    regex          *regexp.Regexp
    controllerType reflect.Type
}

type ControllerRegister struct {
    routers []*controllerInfo
}

定义Controller并使Controller类实现ControllerInterface
ControllerInterface定义了一些常用的方法


type Controller struct {
    RW        http.ResponseWriter
    Request   *http.Request
    Name string
}
type ControllerInterface interface {
    Init(rw http.ResponseWriter, r *http.Request, controllerName string) 
    Prepare()
    Get()
    Post()
    Delete()
    Put()
    Head() 
    Patch()
    Options()
    Finish()
    Render() error 
}

前面定义的ControllerRegister增加两个方法
1.将正则字符串和对应的Controller注册

func (p *ControllerRegister) Add(pattern string, c ControllerInterface) {
    regex, regexErr := regexp.Compile(pattern)
    if regexErr != nil {
        //TODO add error handling here to avoid panic
        panic(regexErr)
        return
    }

    //now create the Route
    t := reflect.Indirect(reflect.ValueOf(c)).Type()
    route := &controllerInfo{}
    route.regex = regex
    route.controllerType = t

    p.routers = append(p.routers, ro.......
te)

}

2.ServeHTTP用于分发请求,通过正则匹配路径,调用匹配的controller对应的方法

func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
    //TODO dispatch request to controller
    url := r.URL
    for _, router := range p.routers {
        reg := router.regex
        if m := reg.MatchString(url.Path); m {
            t := router.controllerType
            v := reflect.New(t)
            ci, ok := v.Interface().(ControllerInterface)
            if !ok {
                panic("controller is not ControllerInterface")
            }

            ci.Init(rw, r, t.Name())
            ci.Prepare()
            switch r.Method {
            case http.MethodGet:
                ci.Get()
            case http.MethodPost:
                ci.Post()
            case http.MethodDelete:
                ci.Delete()
            case http.MethodPut:
                ci.Put()
            case http.MethodHead:
                ci.Head()
            case http.MethodPatch:
                ci.Patch()
            case http.MethodOptions:
                ci.Options()
            default:
                ci.Get()
            }

            ci.Finish()

            break
        }
    }
}

定义App,用于存放ContrllerRegister,之后我们还会用它存放Session管理器等工具

type App struct {
    Handlers *ControllerRegister
    Server   *http.Server
}

var (
    LinApp *App
)

func init() {
    LinApp = NewApp()
}

func NewApp() *App {
    app := &App{Handlers: &ControllerRegister{}, Server: &http.Server{}}
    return app
}

func Router(rootpath string, c ControllerInterface) *App {
    LinApp.Handlers.Add(rootpath, c)
    return LinApp
}

func Run() {
    http.HandleFunc("/", LinApp.Handlers.ServeHTTP)
    http.ListenAndServe(":1234", nil) //设置监听的端口
}

现在只需创建一个Controller的子类,并复写相关的方法
例如:增加一个MainController处理Get方法,打印请求的表单,并返回123到客户端


type MainController struct {
    lin.Controller
}

func (this *MainController) Get() {
    fmt.Println("----------")
    this.Request.ParseForm()
    fmt.Println(this.Request.Form)
    fmt.Fprintf(this.RW, "123")
}

最后在main方法中添加路由,并启动服务
我们DIY的路由就工作起来了

func main(){
    lin.Router("/",&controllers.MainController{})
    lin.Run();  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值