目录
目录README.md

Gnet-Mux

一个基于 gnet/v2 的高性能 Web 框架,提供类似 Gin 的 API 风格。

功能特性

  • 🚀 高性能: 基于 gnet/v2 的事件驱动架构
  • 🎯 Gin风格API: 熟悉的路由和中间件设计
  • 🛡️ 内置中间件: Logger、Recovery、CORS等
  • 📝 丰富的响应方法: JSON、HTML、String、Redirect等
  • 🔧 灵活的上下文: 支持参数获取、数据存储等
  • 🛣️ 路径参数: 支持动态路由参数(如 /user/:id
  • 🔄 自定义JSON: 支持第三方JSON序列化库(如 goccy/go-jsonjson-iterator
  • 🌐 WebSocket支持: 基于 gorilla/websocket 的 WebSocket 连接支持

快速开始

安装

go mod init your-project
go get -u gitlink.org.cn/nanakura/gnet-mux

基本用法

package main

import (
    "gitlink.org.cn/nanakura/gnet-mux"
)

func main() {
    // 创建默认引擎(包含日志和恢复中间件)
    r := mux.Default()

    // 定义路由
    r.GET("/", func(c *mux.Context) {
        c.JSON(map[string]string{
            "message": "Hello, World!",
        })
    })

    // 启动服务器
    r.Run(":8080")
}

API 文档

创建引擎

// 创建空引擎
r := mux.New()

// 创建带默认中间件的引擎
r := mux.Default() // 包含 Logger() 和 Recovery()

路由注册

// 基本路由
r.GET("/", func(c *mux.Context) {
    c.String("Hello, World!")
})

r.POST("/users", func(c *mux.Context) {
    c.JSON(map[string]string{"message": "User created"})
})

// 路径参数路由
r.GET("/user/:id", func(c *mux.Context) {
    id := c.Param("id")
    c.JSON(map[string]interface{}{
        "user_id": id,
        "message": "User found",
    })
})

// 多个路径参数
r.GET("/user/:id/post/:postId", func(c *mux.Context) {
    userID := c.Param("id")
    postID := c.Param("postId")
    c.JSON(map[string]interface{}{
        "user_id": userID,
        "post_id": postID,
        "message": "User post found",
    })
})

// 混合静态路径和参数
r.GET("/api/v1/user/:id/profile", func(c *mux.Context) {
    id := c.Param("id")
    c.JSON(map[string]interface{}{
        "user_id": id,
        "profile": map[string]string{
            "name":  "John Doe",
            "email": "john@example.com",
        },
    })
})

r.PUT("/users/:id", func(c *mux.Context) {
    id := c.Param("id")
    c.JSON(map[string]string{"id": id, "message": "User updated"})
})

r.DELETE("/users/:id", func(c *mux.Context) {
    id := c.Param("id")
    c.Status(204) // No Content
})

中间件

// 使用内置中间件
r.Use(mux.Logger())
r.Use(mux.Recovery())
r.Use(mux.CORS())

// 自定义中间件
r.Use(func(next mux.HandlerFunc) mux.HandlerFunc {
    return func(c *mux.Context) {
        // 前置处理
        next(c)
        // 后置处理
    }
})

自定义JSON配置

框架支持使用第三方JSON序列化库,提供更好的性能或特定功能:

import (
    mux "gnet-mux"
    "github.com/goccy/go-json"  // 高性能JSON库
    // 或者使用 json-iterator
    // jsoniter "github.com/json-iterator/go"
)

// 使用默认的 encoding/json
r1 := mux.New()

// 使用 goccy/go-json
r2 := mux.NewWithConfig(mux.Config{
    JSONEncoder: json.Marshal,
    JSONDecoder: json.Unmarshal,
})

// 使用 json-iterator
var jsonAPI = jsoniter.ConfigCompatibleWithStandardLibrary
r3 := mux.NewWithConfig(mux.Config{
    JSONEncoder: jsonAPI.Marshal,
    JSONDecoder: jsonAPI.Unmarshal,
})

配置选项:

  • JSONEncoder: 自定义JSON编码函数,用于 c.JSON() 响应
  • JSONDecoder: 自定义JSON解码函数,用于 c.ShouldBindJSON()c.BindJSON()
  • XMLEncoder: 自定义XML编码函数,用于 c.XML() 响应
  • YAMLEncoder: 自定义YAML编码函数,用于 c.YAML() 响应
  • ProtoBufEncoder: 自定义ProtoBuf编码函数,用于 c.ProtoBuf() 响应
  • 如果不设置,将使用对应的标准库

自定义编码器配置

框架支持自定义各种格式的编码器:

import (
    mux "gnet-mux"
    "encoding/xml"
    "gopkg.in/yaml.v3"
    "google.golang.org/protobuf/proto"
)

// 使用自定义编码器
r := mux.NewWithConfig(mux.Config{
    // 自定义JSON编码器
    JSONEncoder: customJSONEncoder,

    // 自定义XML编码器
    XMLEncoder: xml.Marshal,

    // 自定义YAML编码器
    YAMLEncoder: yaml.Marshal,

    // 自定义ProtoBuf编码器
    ProtoBufEncoder: proto.Marshal,
})

// 示例:自定义JSON编码器,添加额外字段
func customJSONEncoder(v interface{}) ([]byte, error) {
    wrapper := map[string]interface{}{
        "data": v,
        "timestamp": time.Now().Unix(),
        "version": "1.0",
    }
    return json.Marshal(wrapper)
}

路径参数

框架支持动态路由参数,使用 : 前缀定义参数:

// 单个参数
r.GET("/user/:id", func(c *mux.Context) {
    id := c.Param("id")  // 获取路径参数
    c.JSON(map[string]string{"user_id": id})
})

// 多个参数
r.GET("/user/:userId/post/:postId", func(c *mux.Context) {
    userID := c.Param("userId")
    postID := c.Param("postId")
    c.JSON(map[string]string{
        "user_id": userID,
        "post_id": postID,
    })
})

// 混合静态路径和参数
r.GET("/api/v1/user/:id/profile", func(c *mux.Context) {
    id := c.Param("id")
    // 处理用户资料请求
})

路由匹配规则:

  • 静态路由优先于参数路由
  • 参数名区分大小写
  • 支持多级路径参数
  • 路径参数不能为空

路由分组

框架支持路由分组,可以为一组路由设置共同的前缀和中间件:

// 基本分组
{
    v1 := router.Group("/v1")
    v1.POST("/login", loginEndpoint)
    v1.POST("/submit", submitEndpoint)
    v1.POST("/read", readEndpoint)
}

// 带中间件的分组
{
    v2 := router.Group("/v2")
    v2.Use(AuthMiddleware())
    v2.POST("/login", loginEndpoint)
    v2.POST("/submit", submitEndpoint)
    v2.POST("/read", readEndpoint)
}

// 嵌套分组
api := router.Group("/api")
api.Use(APIMiddleware())
{
    v1 := api.Group("/v1")
    v1.Use(V1Middleware())
    v1.GET("/users/:id", getUserHandler)
    v1.POST("/users", createUserHandler)
}

// 分组中间件链式调用
admin := router.Group("/admin")
admin.Use(AuthMiddleware()).Use(AdminMiddleware())
admin.GET("/stats", adminStatsHandler)

分组特性:

  • 支持路径前缀自动拼接
  • 支持分组级别的中间件
  • 支持嵌套分组
  • 支持中间件链式调用
  • 与 Gin 框架 API 完全兼容

模糊路由(通配符路由)

框架支持模糊路由(通配符路由),与 Gin 框架 API 完全兼容:

func InitRouter() *mux.Engine {
    r := mux.New()
    r.Use(mux.Logger())
    r.Use(mux.Recovery())

    r.GET("/foo/*any", someHandler)

    return r
}

基本用法

// 通配符路由示例
router.GET("/foo/*any", func(c *mux.Context) {
    any := c.Param("any")
    c.JSON(mux.H{"any": any})
})

// API 通配符
router.GET("/api/*path", func(c *mux.Context) {
    path := c.Param("path")
    c.JSON(mux.H{"path": path})
})

// 静态文件服务
router.GET("/static/*filepath", func(c *mux.Context) {
    filepath := c.Param("filepath")
    // 处理静态文件请求
})

匹配示例

// 路由: /foo/*any
"/foo/bar"           → any="bar"
"/foo/bar/baz"       → any="bar/baz"
"/foo/bar/baz/qux"   → any="bar/baz/qux"
"/foo/"              → any=""
"/foo"               → 不匹配(需要 /foo/ 或 /foo/something)

// 路由: /api/*path
"/api/users"         → path="users"
"/api/users/123"     → path="users/123"
"/api/posts/456/comments" → path="posts/456/comments"

路由优先级

通配符路由具有最低优先级,具体路由和参数路由优先匹配:

// 注册路由
router.GET("/api/*path", wildcardHandler)    // 通配符路由
router.GET("/api/health", healthHandler)     // 具体路由
router.GET("/api/users/:id", userHandler)    // 参数路由

// 匹配结果
"/api/health"    → 匹配 healthHandler(具体路由优先)
"/api/users/123" → 匹配 userHandler(参数路由优先)
"/api/other"     → 匹配 wildcardHandler(通配符兜底)

模糊路由特性:

  • 支持任意深度的路径匹配
  • 自动处理空路径(trailing slash)
  • 具体路由优先于通配符路由
  • 参数路由优先于通配符路由
  • 与 Gin 框架行为完全一致
  • 适用于静态文件服务、API 兜底等场景

框架提供了完整的 Cookie 管理功能,与 Gin 框架 API 完全兼容:

// 获取 Cookie
cookie, err := c.Cookie("session_id")
if err != nil {
    // Cookie 不存在
    cookie = "default_value"
}

// 设置 Cookie
c.SetCookie(
    "session_id",    // name: Cookie 名称
    "abc123",        // value: Cookie 值
    3600,            // maxAge: 过期时间(秒)
    "/",             // path: 路径
    "localhost",     // domain: 域名
    false,           // secure: 是否仅 HTTPS
    true,            // httpOnly: 是否仅 HTTP(防止 XSS)
)

// Cookie 会话示例
router.GET("/login", func(c *mux.Context) {
    username := c.Query("username")
    c.SetCookie("user", username, 3600, "/", "", false, true)
    c.JSON(mux.H{"message": "登录成功"})
})

router.GET("/profile", func(c *mux.Context) {
    user, err := c.Cookie("user")
    if err != nil {
        c.JSON(mux.H{"error": "未登录"})
        return
    }
    c.JSON(mux.H{"user": user})
})

// 删除 Cookie(设置过期时间为 -1)
c.SetCookie("session_id", "", -1, "/", "", false, true)

Cookie 特性:

  • 支持获取和设置 Cookie
  • 支持所有标准 Cookie 属性
  • 自动处理路径默认值
  • 与 Gin 框架 API 完全兼容
  • 支持安全 Cookie 设置

静态文件服务

框架提供了完整的静态文件服务功能,与 Gin 框架 API 完全兼容:

// 静态目录服务
router.Static("/assets", "./assets")

// 自定义文件系统服务
router.StaticFS("/more_static", http.Dir("my_file_system"))

// 单个文件服务
router.StaticFile("/favicon.ico", "./resources/favicon.ico")

// 在处理器中发送文件
router.GET("/download", func(c *mux.Context) {
    c.File("./files/document.pdf")
})

静态文件特性:

  • 支持目录和单文件服务
  • 自动 MIME 类型检测
  • 安全路径检查(防止目录遍历攻击)
  • 自动缓存头设置
  • 支持自定义文件系统
  • 目录索引文件支持(index.html)
  • 与 Gin 框架 API 完全兼容

使用示例:

func main() {
    router := mux.Default()

    // 静态文件服务(完全遵循 Gin 示例)
    router.Static("/assets", "./assets")
    router.StaticFS("/more_static", http.Dir("my_file_system"))
    router.StaticFile("/favicon.ico", "./resources/favicon.ico")

    router.Run(":8080")
}

文件上传

框架提供了完整的文件上传功能,与 Gin 框架 API 完全兼容:

单文件上传

func main() {
    router := mux.Default()
    // 设置多部分表单的内存限制(默认为 32 MiB)
    router.MaxMultipartMemory = 8 << 20  // 8 MiB

    router.POST("/upload", func(c *mux.Context) {
        // 单文件
        file, _ := c.FormFile("file")
        log.Println(file.Filename)

        // 上传文件到指定目录
        c.SaveUploadedFile(file, "./files/" + file.Filename)

        c.Status(http.StatusOK).String(fmt.Sprintf("'%s' uploaded!", file.Filename))
    })

    router.Run(":8080")
}

多文件上传

router.POST("/upload/multiple", func(c *mux.Context) {
    // 多部分表单
    form, _ := c.MultipartForm()
    files := form.File["files"]

    for _, file := range files {
        log.Println(file.Filename)

        // 上传文件到指定目录
        c.SaveUploadedFile(file, "./files/" + file.Filename)
    }
    c.Status(http.StatusOK).String(fmt.Sprintf("%d files uploaded!", len(files)))
})

文件上传特性:

  • 支持单文件和多文件上传
  • 可配置内存限制(MaxMultipartMemory)
  • 自动创建目录结构
  • 安全的文件保存
  • 与 Gin 框架 API 完全兼容
  • 支持大文件上传(超过内存限制时自动使用临时文件)

重要提示: file.Filename 不应该被信任。参考 MDN Content-Disposition 文档

上下文方法

获取请求数据

// URL 参数
id := c.Param("id")

// 查询参数
name := c.Query("name")
age := c.DefaultQuery("age", "18")

// 表单数据
username := c.PostForm("username")
password := c.DefaultPostForm("password", "")

// 文件上传
file, err := c.FormFile("file")                    // 单文件上传
form, err := c.MultipartForm()                     // 多文件上传
err := c.SaveUploadedFile(file, "./uploads/file")  // 保存上传的文件

// 请求头
token := c.GetHeader("Authorization")

设置响应

// JSON 响应
c.JSON(map[string]interface{}{
    "status": "success",
    "data": data,
})

// XML 响应
c.XML(map[string]interface{}{
    "status": "success",
    "data": data,
})

// YAML 响应
c.YAML(map[string]interface{}{
    "status": "success",
    "data": data,
})

// Protocol Buffer 响应
c.ProtoBuf(protoMessage)

// 字符串响应
c.String("Hello, %s!", name)

// HTML 响应
c.HTML("<h1>Welcome</h1>")

// 原始数据响应
c.Data("application/octet-stream", []byte("binary data"))

// 从 Reader 读取数据响应
c.DataFromReader(http.StatusOK, contentLength, "text/plain", reader, extraHeaders)

// 重定向
c.Redirect(302, "/login")

// 设置状态码
c.Status(404).String("Not Found")

// 设置响应头
c.Header("X-Custom-Header", "value")

// Cookie 操作
cookie, err := c.Cookie("session_id")  // 获取 Cookie
c.SetCookie("session_id", "abc123", 3600, "/", "localhost", false, true)  // 设置 Cookie

JSON数据绑定

// JSON数据绑定
type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

var user User
// 绑定JSON数据(使用配置的JSON解码器)
if err := c.ShouldBindJSON(&user); err != nil {
    c.Status(400).JSON(map[string]string{"error": err.Error()})
    return
}

// 绑定JSON数据并自动处理错误
if err := c.BindJSON(&user); err != nil {
    return // 自动返回400错误
}

上下文存储

// 存储值
c.Set("user", user)

// 获取值
user, exists := c.Get("user")
user := c.MustGet("user") // 不存在时会 panic

中间件控制

// 执行下一个处理器
c.Next()

// 中止执行
c.Abort()
c.AbortWithStatus(403)
c.AbortWithStatusJSON(400, map[string]string{"error": "Bad Request"})

示例

完整示例

package main

import (
    "net/http"
    "gitlink.org.cn/nanakura/gnet-mux"
)

func main() {
    r := mux.Default()

    // 添加 CORS 中间件
    r.Use(mux.CORS())

    // 基本路由
    r.GET("/", func(c *mux.Context) {
        c.JSON(map[string]string{"message": "Hello, World!"})
    })

    // 带参数的路由
    r.GET("/user/:id", func(c *mux.Context) {
        id := c.Param("id")
        c.JSON(map[string]string{"user_id": id})
    })

    // 查询参数
    r.GET("/search", func(c *mux.Context) {
        query := c.DefaultQuery("q", "")
        page := c.DefaultQuery("page", "1")
        c.JSON(map[string]string{
            "query": query,
            "page":  page,
        })
    })

    // POST 请求
    r.POST("/user", func(c *mux.Context) {
        name := c.PostForm("name")
        email := c.PostForm("email")

        if name == "" {
            c.AbortWithStatusJSON(http.StatusBadRequest,
                map[string]string{"error": "Name is required"})
            return
        }

        c.JSON(map[string]interface{}{
            "message": "User created",
            "user": map[string]string{
                "name":  name,
                "email": email,
            },
        })
    })

    // 启动服务器
    r.Run(":8080")
}

自定义中间件示例

// 认证中间件
func AuthMiddleware() mux.Middleware {
    return func(next mux.HandlerFunc) mux.HandlerFunc {
        return func(c *mux.Context) {
            token := c.GetHeader("Authorization")
            if token == "" {
                c.AbortWithStatusJSON(401, map[string]string{
                    "error": "Authorization header required",
                })
                return
            }

            // 验证 token 逻辑...

            c.Set("user_id", "123")
            next(c)
        }
    }
}

// 使用中间件
r.Use(AuthMiddleware())

性能

基于 gnet/v2 的高性能网络引擎,相比传统的 net/http 具有更好的性能表现:

  • 更低的内存占用
  • 更高的并发处理能力
  • 更少的 GC 压力
  • 支持多核并行处理

与 Gin 的对比

特性 Gnet-Mux Gin
网络引擎 gnet/v2 net/http
性能 更高 标准
API 风格 类似 Gin 原生
中间件 支持 支持
生态系统 新兴 成熟

WebSocket 支持

框架支持 WebSocket 连接,使用 gorilla/websocket 库:

import (
    "github.com/gorilla/websocket"
    mux "gitlink.org.cn/nanakura/gnet-mux"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}

func handleWebSocket(c *mux.Context) {
    // 升级 HTTP 连接为 WebSocket 连接
    conn, err := c.UpgradeWebSocket(&upgrader)
    if err != nil {
        return
    }
    defer conn.Close()

    // 处理 WebSocket 消息
    for {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            break
        }
        // 回显消息
        err = conn.WriteMessage(messageType, message)
        if err != nil {
            break
        }
    }
}

// 注册 WebSocket 路由
r.GET("/ws", handleWebSocket)

WebSocket 特性:

  • 支持 gorilla/websocket
  • 提供 c.Writer()c.Request() 方法用于 WebSocket 升级
  • 提供 c.UpgradeWebSocket() 便捷方法
  • 与 Gin 框架 API 完全兼容
  • 支持自定义 WebSocket 升级器配置

注意事项

  1. 这是一个基于 gnet/v2 的实验性框架
  2. API 可能会在后续版本中发生变化
  3. 建议在生产环境使用前进行充分测试

贡献

欢迎提交 Issue 和 Pull Request!

许可证

MIT License

关于
330.0 KB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

©Copyright 2023 CCF 开源发展委员会
Powered by Trustie& IntelliDE 京ICP备13000930号