很多人聽過 MessagePack,但真正用在 Web API 上的機會其實不多,尤其是完整從 Client 到 Server 都走 binary 傳輸的情境
這篇文章用 .NET 10 示範一個最小可行的案例,實作一個只接受 MessagePack 的 Web API,並搭配一個 Client 呼叫範例
讓整個傳輸流程從頭到尾都清楚可以快速複製貼上了解
1. 我測試環境是 .NET 10 ,一樣沒有內建,請先到 nuget 下面去下載 https://www.nuget.org/packages/messagepack
2. 建立模型,記得 Server and Client 的模型要一模一樣,包括 Key(index) 不然會出錯,這點要特別注意
在設計實務上,建議使用另一個專統一描述模型不要用兩邊各自為政的資料模型,這樣比較安全,也不用重複撰寫
[MessagePackObject]
public sealed class User
{
[Key(0)] public int Id { get; set; }
[Key(1)] public string Name { get; set; } = "";
[Key(2)] public List Friends { get; set; } = new();
[Key(3)] public int Age { get; set; }
[Key(4)] public string Alias { get; set; } = "";
[Key(5)] public decimal Salary { get; set; }
[Key(6)] public DateTime Birth { get; set; }
}
3. Server side 的程式碼,這邊我直接開一個 Web API 專案然後直接加上一個 Controller ,這樣你也不用去調整其他設定
如果你是自己開一個空白的專案,記得在 Program.cs 要加入 app.MapControllers();
這邊我很常忘記,我提醒自己一下,這邊我就修改進來的MessagePack 還原成物件,並且修改一些資料
C# Code
[ApiController]
[Route("api/user")]
public class UserController : ControllerBase
{
[HttpPost("edit")]
public async Task EditUser()
{
// 1. 讀取 request body binary data
using var ms = new MemoryStream();
await Request.Body.CopyToAsync(ms);
var requestBytes = ms.ToArray();
// 2. MessagePack 反序列化
var user = MessagePackSerializer.Deserialize(
requestBytes,
MessagePackSerializerOptions.Standard
);
// 3. 模擬處理
user.Name = user.Name + " --Server 改過了";
user.Age += 1;
// 4. 序列化回傳
var responseBytes = MessagePackSerializer.Serialize(
user,
MessagePackSerializerOptions.Standard
);
return File(
responseBytes,
"application/x-msgpack"
);
}
}
4. 接下來是 Client 呼叫的部分,這邊的部分我就製作一個 User 資料序列化成 MessagePack 後就傳給 Server
並且拿到回應後用 JSON 印出看看是不是得到預期答案
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var client = new HttpClient(handler);
var user = new User
{
Id = 1,
Name = "許當麻測試",
Age = 42,
Birth=new DateTime(1981,1,1),
Alias="測試"
};
// 1. Serialize to MessagePack
byte[] requestBytes = MessagePackSerializer.Serialize(
user,
MessagePackSerializerOptions.Standard
);
// 2. 建立 HttpContent
var content = new ByteArrayContent(requestBytes);
content.Headers.ContentType =
new MediaTypeHeaderValue("application/x-msgpack");
// 3. 呼叫 Server
var response = await client.PostAsync(
"http://localhost:5260/api/user/edit",
content
);
response.EnsureSuccessStatusCode();
// 4. 讀取 binary response
var responseBytes = await response.Content.ReadAsByteArrayAsync();
// 5. Deserialize
var responseUser = MessagePackSerializer.Deserialize(
responseBytes,
MessagePackSerializerOptions.Standard
);
Console.WriteLine(
"Result:" + JsonConvert.SerializeObject(responseUser)
);
執行結果:
Result:{"Id":1,"Name":"許當麻測試 --Server 改過了","Friends":[],"Age":43,"Alias":"測試","Salary":0.0,"Birth":"1981-01-01T00:00:00Z"}
來個結論,如果你是對外的專案當然就是以 JSON 為主,如果你是在 server 跟 server 之間的溝通
都是在自己可以控制的範圍,MessagePack 的確是一個好選擇,中間傳遞資料小了,還原速度也可以更快速
我個人在兩台自己的機器溝通的時候的確是採用這方案, 畢竟都是自己專案的機器一切都是好說話。
--
本文原文首發於個人部落格:Server 與 Client 全程走 MessagePack:一個 .NET 10 Web API 的實作紀錄
--
---
The bug existed in all possible states.
Until I ran the code.