MetaMessage 改變 Web 生態

MetaMessage 现已支持 C、C++、C#、Go、Python、Rust、PHP、Swift、Kotlin、Java、TypeScript、JavaScript 等主流编程语言,这些语言间的数据结构、语义与数值完全一致,展现出成为统一结构化数据序列化/反序列化协议的强大潜力,堪称 AI 时代的“终极协议”。接下来,我们将深入剖析 MetaMessage 在 Go 语言 Web 开发中的实际应用,进行一场真枪实弹的实战演练。
项目地址:github.com/metamessage/mm-web-go
该库为 Gin、Echo、Fiber、Chi 以及 net/http 原生框架均提供了完善支持,使开发者能够无缝集成 MetaMessage 进行编解码,无需自行构建轮子。
Schema 發現
服务端泛型路由(GET/POST/PUT/DELETE/PATCH)会自动注册一个 OPTIONS 端点用于 Schema 发现。当客户端发送 OPTIONS 请求时,服务端返回一个 MetaMessage 编码的结构体实例,其中包含完整的类型约束、验证规则以及描述元数据。客户端在实际业务请求之前会自动利用此机制进行请求验证——类似于预检请求,但完全由框架自动完成:
客户端 服务端
│ │
├── OPTIONS /api/v1/users ──────►
│◄──── MetaMessage Schema ──────┤
│ (struct definition) │
│ │
├── POST /api/v1/users ────────►
│◄──── MetaMessage Response ────┤
服務端
服务端的实现极为简洁:只需返回一个结构体,路由即自动完成注册。所有框架均提供高度一致的泛型路由注册 API,handler 签名统一为 func(ctx, *T) (any, string, error)。无论你使用 Gin 还是 Echo,编写方式几乎完全一致。
net/http 原生
import server "github.com/metamessage/mm-web-go/mmvanilla"
mux := http.NewServeMux()
server.Init(mux, "/api/v1")
server.GET("/users", func(r *http.Request, req *any) (any, string, error) {
return ListUsersResponse{...}, "", nil
})
server.POST("/users", func(r *http.Request, req *CreateUserRequest) (any, string, error) {
return UserResponse{...}, "", nil
})
Gin
import "github.com/gin-gonic/gin"
import server "github.com/metamessage/mm-web-go/mmgin"
r := gin.Default()
server.Init(r, "/api/v1")
// 泛型路由:自动绑定 + OPTIONS Schema 发现
server.GET("/users", func(c *gin.Context, req *any) (any, string, error) {
return ListUsersResponse{...}, "", nil
})
server.GET("/users/:id", func(c *gin.Context, req *any) (any, string, error) {
return APIResponse{...}, "", nil
})
server.POST("/users", func(c *gin.Context, req *CreateUserRequest) (any, string, error) {
return UserResponse{...}, "", nil
})
server.PUT("/users/:id", func(c *gin.Context, req *UpdateUserRequest) (any, string, error) {
return APIResponse{...}, "", nil
})
server.DELETE("/users/:id", func(c *gin.Context, req *any) (any, string, error) {
return APIResponse{...}, "", nil
})
Echo
import "github.com/labstack/echo/v4"
import server "github.com/metamessage/mm-web-go/mmecho"
e := echo.New()
server.Init(e, "/api/v1")
server.GET("/users", func(c echo.Context, req *any) (any, string, error) {
return ListUsersResponse{...}, "", nil
})
server.POST("/users", func(c echo.Context, req *CreateUserRequest) (any, string, error) {
return UserResponse{...}, "", nil
})
Fiber
import "github.com/gofiber/fiber/v2"
import server "github.com/metamessage/mm-web-go/mmfiber"
app := fiber.New()
server.Init(app, "/api/v1")
server.GET("/users", func(c *fiber.Ctx, req *any) (any, string, error) {
return ListUsersResponse{...}, "", nil
})
server.POST("/users", func(c *fiber.Ctx, req *CreateUserRequest) (any, string, error) {
return UserResponse{...}, "", nil
})
Chi
import "github.com/go-chi/chi/v5"
import server "github.com/metamessage/mm-web-go/mmchi"
r := chi.NewRouter()
server.Init(r, "/api/v1")
server.GET("/users", func(r *http.Request, req *any) (any, string, error) {
return ListUsersResponse{...}, "", nil
})
server.POST("/users", func(r *http.Request, req *CreateUserRequest) (any, string, error) {
return UserResponse{...}, "", nil
})
數據綁定
对于 POST/PUT/PATCH 这类使用请求体(body)的 HTTP 方法,推荐直接携带二进制数据,以获得更高的传输性能。而对于 GET/DELETE 等仅支持 URL 传递参数的请求,也提供了兼容方案——通过 ?data= 方式携带 MetaMessage 编码的数据。
Bind
将请求体绑定到结构体,自动检测数据格式:
var user User
if err := mmgin.Bind(c, &user); err != nil {
// 处理错误
}
BindQuery
从查询参数 ?data= 中读取 MetaMessage 编码数据并解析到结构体:
var filter Filter
if err := mmgin.BindQuery(c, &filter); err != nil {
// 处理错误
}
客戶端
客户端发送请求时,能够实现完全类型安全的泛型请求。它会自动先发送一个 OPTIONS 预检请求来验证 Schema,确保请求数据与服务端期望的结构完全一致。当然,这一流程已由框架自动封装,开发者无需手动编写任何额外代码。
resp, err := client.DoRequest[CreateUserRequest, UserResponse](
c, "POST", "/api/v1/users", req,
)
便捷函數
如果不想显式创建客户端对象,可以直接使用包级别的便捷函数,默认调用全局客户端实例:
client.GET[any, ListUsersResponse]("/api/v1/users", nil)
client.POST[CreateUserRequest, UserResponse]("/api/v1/users", req)
client.PUT[UpdateUserRequest, APIResponse]("/api/v1/users/1", req)
client.DELETE[any, APIResponse]("/api/v1/users/1", nil)
client.PATCH[UpdateUserRequest, APIResponse]("/api/v1/users/1", req)
