MetaMessage,開啟了API革命!

举报
大漠孤煙 发表于 2026/05/26 00:40:41 2026/05/26
【摘要】 傳統api調用存在很多問題。很多應用,api是沒有文檔的,調用者根本無法得知api的請求參數和返回數據是什麼樣的,規範是什麼。發佈一個api,可能還得發佈文檔,比如Swagger。大多數開發者很難寫好文檔,文檔質量參差不齊。文檔發佈地址就是一個問題。即使有文檔,也很難找。還有一些文檔受限於網絡、權限等,看不了。文檔可能和api脫節,更新不及時。甚至造成各種歧義。文檔很難管理,丟失、遺忘等問題...

傳統api調用存在很多問題。

  1. 很多應用,api是沒有文檔的,調用者根本無法得知api的請求參數和返回數據是什麼樣的,規範是什麼。
  2. 發佈一個api,可能還得發佈文檔,比如Swagger。大多數開發者很難寫好文檔,文檔質量參差不齊。
  3. 文檔發佈地址就是一個問題。即使有文檔,也很難找。還有一些文檔受限於網絡、權限等,看不了。
  4. 文檔可能和api脫節,更新不及時。甚至造成各種歧義。
  5. 文檔很難管理,丟失、遺忘等問題很常見。
  6. 開發者、調用者之間協作困難,甚至相互誤解。

問題太多了,這只是冰山一角。而MetaMessage的出現,可能真正改變了這種局面。

MetaMessage,自描述、自規範、自示例。比如,輸出為一個文本格式jsonc:

{
  // mm: type=datetime; desc=創建時間
  "create_time": "2026-01-01 00:00:00",
}

看起來很簡單,type表示了數據格式,各種語言實現一致,不會出現某個語言解釋為字符串的問題。desc可以描述字段含義、使用方法等。
當然還有更多標籤,這些標籤共同描述了一種精確、完整的數據。

開發者可以直接從各語言的數據對象直接生成mm,也可以通過比如jsonc字符串生成mm。代表的數據是完全一致的。
說到這裡,對於多語言間的數據對比,實際很困難,甚至說還沒有一個好的方法真正能做到,對於測試來說就是一個很大的問題。
而都轉成mm,對比文本非常方便。

還有一些其他的優點,比如可以生成緊湊的二進制,可以生成更多文本格式等等。未來,一個mm可以生成jsonc、toml、yaml等格式,作為配置文件的轉換非常方便。

遠了。

我們在調用一個api的時候,能不能直接獲取它的請求方法?我想到一個方法,我們可以利用resful的options方法。options方法設計之初就是資源探測,但是幾十年來,大家僅僅是用來表示cors,描述跨域問題。太浪費了。而且瀏覽器默認會在post等請求前,調用下options方法,我們是沒辦法取消的,有點可笑了。我們可以把請求參數方法放在options返回的body中,這樣調用方不就能獲得實時的、準確的請求方法了嗎?

OPTIONS /api/v1/users/1:

// mm: example
{

        // mm: nullable; desc="用戶名稱"
        "name": "",

        // mm: type=email; nullable; desc="電子郵箱"
        "email": "",

        // mm: type=u8; nullable; desc="年齡"
        "age": 0,

        // mm: nullable; desc="是否激活"
        "is_active": false,
}

這時一個修改用戶的api,當我們看到這個結果,我們就能知道name可以為null,age必須是uint8.當然整體上還能得出這是個example示例數據。

我們請求的時候,只要完全符合,那麼就是安全合法的。

實踐

我們來做一個golang gin的中間件,實現這樣的功能:

  1. 對於post、put、patch方法,把請求參數自動包裝、暴露到options
  2. 自動以mm對請求進行編碼
  3. 對返回結果自動解嗎

為了方便使用和演示,我們還可以做個客戶端:

  1. 請求的時候,自動探測schema,也就是請求options
  2. 自動驗證請求合法性

當然服務端和客戶端都會自動驗證。

server:

...

type CreateUserRequest struct {
	Name  string `mm:"desc=用戶名稱; min=1; max=50"`
	Email string `mm:"type=email; desc=電子郵箱"`
	Age   uint8  `mm:"desc=年齡; min=0; max=150"`
}

mmgin.POST("/users", createUser)

func createUser(c *gin.Context, req *CreateUserRequest) {
	newUser := User{
		ID:       int64(len(users) + 1),
		Name:     req.Name,
		Email:    req.Email,
		Age:      req.Age,
		IsActive: true,
	}
	users = append(users, newUser)

	mmgin.RespondWithStatus(c, http.StatusCreated, APIResponse{
		Code:    0,
		Message: "user created",
		Data:    &newUser,
	}, "")
}
...

client:

...

type CreateUserRequest2 struct {
	Name  string `mm:"desc=用戶名稱; min=1; max=50"`
	Email string `mm:"type=email; desc=電子郵箱"`
	Age   uint8  `mm:"desc=年齡; min=0; max=150"`
}

createReq := &CreateUserRequest2{
    Name:  "David",
    Email: "david@example.com",
    Age:   28,
}
resp3, err := client.POST[CreateUserRequest2, APIResponse]("/api/v1/users", createReq)
if err != nil {
    fmt.Printf("  [Error] %v\n", err)
    return
}
fmt.Printf("  [OK] Message: %s\n", resp3.Message)
fmt.Printf("     New User: %+v\n", resp3.Data)
...

以上可以看到,使用非常簡單,甚至無感。
完整示例可參見"metamessage/mm-gin"項目

可測試的結果是,只要請求數據不合法,在options階段就會失敗。

注意這是強一致的,是實時驗證的。

未來

api不用寫文檔,代碼就是文檔。

每個api接口,自動生成一個schema

陌生的api,沒關係,自動獲取請求參數;api更新,沒關係,自動獲得更新後的請求參數。

特別是眾多瀏覽器,怎麼也不會想到options還能這麼用,如果內置這種用法,對各種api更是幫助巨大。

MetaMessage作為基礎協議,會在各種場景有更多重要應用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。