Golang中如何使用net/http实现动态URL或正则表达式路由

Golang中如何使用net/http实现动态URL或正则表达式路由 我不想使用 Gorilla/mux,因为有人说它的性能较慢,而这正是我想要避免的。 那么,如何仅使用 net/http 包(不使用 gorilla mux)来实现 "page/{pageTitle}/{pageId}/HelloWorld" 这样的动态或正则表达式 URL 呢?

4 回复

我想我会采纳你另一个回答中提到的 HttpRouter,它实际上比内置的多路复用器更快。非常感谢!

更多关于Golang中如何使用net/http实现动态URL或正则表达式路由的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用正则表达式(regexp)的路由器性能较慢,开源的主流路由器有 httprouter、chi、mux。

性能耗时:httprouter 3,chi 14,mux 100+。

在一般项目中,不必过分关注路由器的性能。与其他操作相比,路由器消耗的资源较少。

在路由器的实现中,有基于基数树(radix)和基于 for 循环的两种实现方式。与 for 循环系列相比,基数树系列天然具有高性能。此外,正则表达式(regexp)操作在验证过程中会消耗大量资源。

我的路由器示例:https://github.com/eudore/erouter

中文实现文档:https://github.com/eudore/eudore/wiki/4.5.1-eudore-router-radix

基准测试:https://github.com/eudore/web-framework-benchmark

在标准库 net/http 中实现动态路由,可以通过解析请求路径并手动匹配模式来实现。以下是一个示例,展示如何实现类似 "page/{pageTitle}/{pageId}/HelloWorld" 的路由:

package main

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

// 定义路由处理函数类型
type routeHandler func(http.ResponseWriter, *http.Request, map[string]string)

// 路由结构体
type Route struct {
    pattern string
    handler routeHandler
}

// 路由器
type Router struct {
    routes []Route
}

// 注册路由
func (r *Router) HandleFunc(pattern string, handler routeHandler) {
    r.routes = append(r.routes, Route{pattern: pattern, handler: handler})
}

// 实现 http.Handler 接口
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    for _, route := range r.routes {
        if params, ok := r.match(route.pattern, req.URL.Path); ok {
            route.handler(w, req, params)
            return
        }
    }
    http.NotFound(w, req)
}

// 路径匹配函数
func (r *Router) match(pattern, path string) (map[string]string, bool) {
    patternParts := strings.Split(pattern, "/")
    pathParts := strings.Split(path, "/")

    if len(patternParts) != len(pathParts) {
        return nil, false
    }

    params := make(map[string]string)
    for i, patternPart := range patternParts {
        if strings.HasPrefix(patternPart, "{") && strings.HasSuffix(patternPart, "}") {
            // 提取参数名
            paramName := patternPart[1 : len(patternPart)-1]
            params[paramName] = pathParts[i]
        } else if patternPart != pathParts[i] {
            return nil, false
        }
    }
    return params, true
}

// 示例处理函数
func pageHandler(w http.ResponseWriter, req *http.Request, params map[string]string) {
    pageTitle := params["pageTitle"]
    pageId := params["pageId"]
    fmt.Fprintf(w, "Page Title: %s, Page ID: %s", pageTitle, pageId)
}

func main() {
    router := &Router{}
    
    // 注册动态路由
    router.HandleFunc("page/{pageTitle}/{pageId}/HelloWorld", pageHandler)
    
    // 启动服务器
    http.ListenAndServe(":8080", router)
}

这个实现支持基本的动态参数提取。对于更复杂的正则表达式匹配,可以修改 match 函数来支持正则模式。例如:

import "regexp"

type Route struct {
    pattern *regexp.Regexp
    handler routeHandler
}

func (r *Router) HandleRegex(pattern string, handler routeHandler) {
    compiled := regexp.MustCompile(pattern)
    r.routes = append(r.routes, Route{pattern: compiled, handler: handler})
}

func (r *Router) matchRegex(pattern *regexp.Regexp, path string) (map[string]string, bool) {
    matches := pattern.FindStringSubmatch(path)
    if matches == nil {
        return nil, false
    }
    
    params := make(map[string]string)
    for i, name := range pattern.SubexpNames() {
        if i != 0 && name != "" {
            params[name] = matches[i]
        }
    }
    return params, true
}

使用正则表达式路由:

router.HandleRegex(`^/page/(?P<pageTitle>[^/]+)/(?P<pageId>[^/]+)/HelloWorld$`, pageHandler)

这种方法避免了第三方依赖,同时提供了动态路由功能。性能方面,由于减少了抽象层,通常比使用完整路由库更快。

回到顶部