開源 , KoobooJson一款高性能且輕量的JSON框架

来源:https://www.cnblogs.com/1996V/archive/2019/03/27/10607916.html
-Advertisement-
Play Games

KoobooJson - 更小更快的C# JSON序列化工具(基於表達式樹構建) 在C#領域,有很多成熟的開源JSON框架,其中最著名且使用最多的是 Newtonsoft.Json ,然而因為版本迭代,其代碼要相容從net2.0到現在的最新的net框架,並且要支持.net平臺下的其它語言,所以最新發 ...


KoobooJson - 更小更快的C# JSON序列化工具(基於表達式樹構建)

 

  在C#領域,有很多成熟的開源JSON框架,其中最著名且使用最多的是 Newtonsoft.Json ,然而因為版本迭代,其代碼要相容從net2.0到現在的最新的net框架,並且要支持.net平臺下的其它語言,所以最新發佈版本的Newtonsoft.Json其dll大小接近700k,另一方面,因為其複雜的迭代歷史導致它的代碼為了維護向下擴展性和向上相容性而捨棄一些性能。

  如果你不太在乎體積和性能的話,那麼 Newtonsoft.Json 無疑是一款很好的選擇。但是如果你在意性能的話,在github上仍然有一些出名的以速度為稱的c# JSON框架,其中最為人知的應該是 JIL , JIL有著出色的性能是因為它採用了大量的加速技術,但這也帶來了一些局限性,它不夠靈活,對object類型的解析必須得調用它的另一個API,並且因為出於性能考慮其採用的是Emit技術,不易維護,在我的測試中有很多類型它不支持。但是JIL的地位是顯而易見的,因為它的出現,github上有著很多相仿思路的以速度為稱的JSON框架,幾乎每個都稱自己是最快的,但實際上很少有超越JIL的,並且它們中的大部分沒有一個良好的文檔,這導致我在做性能測試時,我想改個配置都得對源碼全局搜索花費一定時間。

  在說回程式集大小,JIL的最新發佈版本是500k,並且其依賴一個庫,加起來是800k大小。

  那麼,我講這些,大家應該知道我想要表達什麼!

  是的,考慮到前面種種,這些都不是在某種場景最理想化的那種JSON庫,所以我寫了一款以體積更小,速度更快,類型覆蓋更廣的開源C# JSON框架,它叫:KoobooJson

在我正式介紹KoobooJson之前,我要介紹一下什麼是Kooboo

Kooboo是我們老闆用C#編寫的一個開源的非常神奇的網站開發工具,它是一個類CMS生成器,但其從資料庫,前端引擎,到各種網路協議伺服器都是用c#自主創造的,幾乎很少使用到第三方庫,它編譯後的發佈版本僅有幾M,而正是因為僅僅只有幾M,為了Json框架不要太影響主程式的大小,這才有了KoobooJson此次的誕生!

Kooboo是開源的:https://github.com/Kooboo/Kooboo

KoobooJson自然也是開源的:https://github.com/Kooboo/Json

在NuGet包中可以直接搜索 KoobooJson 下載使用即可

什麼是KoobooJson?

  KoobooJson是一款C#的JSON工具,其主要通過表達式技術構建,最低支持.NET4.5(可以支持.NET4.0,但考慮到一些因素,最終沒有支持,有需要支持的可以自行源碼分支更改。另外,幾乎每個以性能號稱的JSON框架都最低支持.NET4.5),最低支持.NET Core 2.0,體積小巧,性能出色,類型覆蓋廣是KoobooJson的優點!

KoobooJson的優點

1. 小巧

目前KoobooJson只有130k, 並且沒有任何額外依賴項, KoobooJson當前支持框架版本.NET4.5 .NET Core2+ .NET Standard 2

2. 快速

KoobooJson 遵循JSON RFC8259規範, 是一款適用於C#的快速的Json文本序列化器

它基於表達式樹構建, 在運行時會動態的為每個類型生成高效的解析代碼, 這過程包括: 利用靜態泛型模板進行緩存, 避免字典查詢開銷, 避免裝箱拆箱消耗, 緩衝池復用, 加速位元組複製...

KoobooJson生成代碼的方式並沒有採用Emit, 而是採用ExpressionTree. ExpressionTree相比Emit而言, 它不能像Emit直接寫出最優的IL代碼, 它依賴於下層的編譯器, 在某些時候它會多生成一些不必要的IL代碼路徑, 故而性能上有所遜色. 但相較於幾乎沒有類型檢查的Emit而言, ExpressionTree不會出現各種莫名其妙的錯誤, 它更加安全, 也更加容易擴展維護.

雖然ExpressionTree與Emit相比在性能方面可能會有所差異, 但是KoobooJson的表現卻相當亮眼!

 

上圖是使用BenchmarkDotNet在Net Core2.1上做的Json序列化和反序列化的性能測試,隨機生成大量的測試數據,迭代100次後產生的結果,基準報告在這裡

BenchmarkDotNet=v0.11.4, OS=Windows 10.0.17763.316 (1809/October2018Update/Redstone5) Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=2.1.505 [Host] : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT Job-XEQPPS : .NET Core 2.1.9 (CoreCLR 4.6.27414.06, CoreFX 4.6.27415.01), 64bit RyuJIT

IterationCount=100 LaunchCount=1 WarmupCount=1

3. 覆蓋類型廣

在類型定義上, KoobooJson並沒有單獨實現每個集合或鍵值對類型, 而是對這些FCL類型進行劃分成不同的模板

a. KoobooJson將序列化分為5種類型:

  • 原始類型
    它包括 Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.

  • 所有擁有鍵值對行為的類型
    任何能夠實現IDictionary<>或能夠實現IDictionary且能夠通過構造函數註入鍵值對的類型, 都將以鍵值對方式進行解析

  • 所有擁有集合行為的類型
    任何能夠實現IEnumable並且滿足IColloction的Add行為或擁有自己獨特的集合行為且能夠通過構造函數註入集合的類型, 都將以集合方式進行解析

  • 特殊類型
    如Nullable<>, Lazy<>, Guid, Datatable, DateTime, Type, Task, Thread, Timespan...等等這些特定的類型實現

  • 常規Model的鍵值對類型
    在KoobooJson中, 如果當類型不滿足上述4種時, 將會以鍵值對的形式來對其解析, KoobooJson會對Model中公開的所有元素進行序列化, 在這個環節, 幾乎配置器中所有的配置都是有關Model的. 諸如別名, 忽略特性, 指定構造函數, 忽略堆棧迴圈引用, 首字母大小寫, 格式化器... 值得一提的是, 在對介面類型進行反序列化時, KoobooJson預設會自動創建並返回一個實現於該介面的對象.

b. 在對類型的解析上, 其中浮點型,日期時間類型, GUID的解析是參照了JIL的代碼, 在此表示感謝.

作為一款活躍的Json庫, KoobooJson會不斷支持更多的類型, 這其中, 因為對FCL中的鍵值對和集合的行為進行歸納, 所以對於這兩種類型, KoobooJson並不像其它框架一樣去特定的為每種類型單獨實現, 實際上, 第2和3所定義的規則可以容納FCL中的大多數鍵值對或集合類型.

目前KoobooJson所覆蓋的類型包括 : Hashtable, SortedList, ArrayList, IDictionary, Dictionary<,>, IList,List<>, IEnumerable<>, IEnumerable, ICollection, ICollection<>, Stack<>, Queue<>, ConcurrentBag<>, ConcurrentQueue<>, ConcurrentStack<>, SortedDictionary<,>, ConcurrentDictionary<,>, SortedList<,>, IReadOnlyDictionary<,>, ReadOnlyDictionary<,>, ObservableCollection<>, HashSet<>, SortedSet<>, LinkedList<>, ReadOnlyCollection<>, ArraySegment<>, Stack, Queue, IReadOnlyList<>, IReadOnlyCollection<>, ReadOnlyCollection<>, ISet<>, BitArray, URI, NameValueCollection, StringDictionary, ExpandoObject, StringBuilder, Nullable<>, Lazy<>, Guid, Datatable, DateTime, Type, Task, Thread, Timespan, Enum, Exception, Array[], Array[,,,,,]...

KoobooJson的實現

序列化

class UserModel
{
   public object Obj;
   public string Name;
   public int Age;
}
string json = JsonSerializer.ToJson(new UserModel());

在對類型第一次序列化時, KoobooJson會為這個類型生成大致是這樣的解析代碼.

void WriteUserModel(UserModel model,JsonSerializerHandler handler)
{
  ...配置選項處理...格式化器處理...堆棧無限引用處理...
  handler.sb.Write("Obj:")
  WriteObject(model.Obj);//在序列化時將為Object類型做二次真實類型查找
​
  handler.sb.Write("Name:")
  WriteString(model.Name);
​
  handler.sb.Write("Age:")
  WriteInt(model.Age);
  
}

如果是List<UserModel>的話, 那麼將生成這樣的代碼

handler.sb.Write("[")
foreach(var user in users)
{
    WriteUserModel(user);
    WriteComma()
}
handler.sb.Write("]")

在當前版本中, KoobooJson序列化使用的容器為StringBuilder, 與直接ref char[]相比, 多了一些額外的調用. 將考慮在下個版本中構建一個輕便的char容器, 並會區分對象大小, 考慮棧數組和通過預掃描大小來減少對記憶體的開銷,這將顯著提升序列化速度.

 

反序列化

在對類型進行第一次反序列化時, KoobooJson會為這個類型生成大致是這樣的解析代碼.

UserModel model = JsonSerializer.ToObject<UserModel>("{\"Obj\":3,\"Name\":\"Tom\",\"Age\":18}");
void ReadUserModel(string json,JsonDeserializeHandler handler)
{
    ...Null處理...
    ReadObjLeft()
    空元素處理...構造函數處理...配置項處理...格式化器處理...
    while(i-->0){
        switch(gechar())
        {
            case 'O':
                switch(getchar())
                    case 'b':
                        switch(getchar())
                            case 'j':
                                ReadQuote();
                                ReadObject();
                                if(getchar()==',')
                                    i++;
        }
    }
    ReadObjRight()
}

KoobooJson生成反序列化代碼, KoobooJson會假設json格式完全正確, 沒有預先讀取Json結構部分, 而是直接使用代碼來描述結構, 所以KoobooJson少了一次對json結構的掃描, 執行過程中如果json結構發生錯誤, 會直接拋出異常.

而對於key的匹配, KoobooJson生成的是逐個char的自動機匹配代碼, 目前KoobooJson是以字典樹為演算法, 逐個char進行類型比較, 與一次比較多個char相比, 這種方式顯然沒有達到最小的查詢路徑, 不過在jit優化下, 兩種方式實現經測試效率幾乎一樣.

在反序列化讀取字元時, 因為是對類型動態生成編碼, 提前知道每個類型中的元素的位元組長度和其類型的值長度, 所以KoobooJson出於更高的性能對反序列化採取了指針操作, 並加速位元組讀取.

case 3:
    if (*(int*)Pointer != *(int*)o) return false;
    if (*(Pointer + 2) != *(o + 2)) return false;
    goto True;
case 4:
    if (*(long*)Pointer != *(long*)o) return false;
    goto True;
case 5:
    if (*(long*)Pointer != *(long*)o) return false;
    if (*(Pointer + 4) != *(o + 4)) return false;

因為是指針操作, KoobooJson在反序列化環節幾乎不需要去維護一個char池來存放下一個需要讀取的json結構片段.

功能介紹

KoobooJson當前僅支持3個API調用

string Kooboo.Json.JsonSerializer.ToJson<T>(T value, JsonSerializerOption option=null)
​
T Kooboo.Json.JsonSerializer.ToObject<T>(string json, JsonDeserializeOption option=null)
​
object Kooboo.Json.JsonSerializer.ToObject(string json, Type type, JsonDeserializeOption option=null)

 

忽略註釋

在json字元串的讀取中KoobooJson會自動忽略註釋

string json = @"
                 /*註釋*/
                {//註釋  
                     /*註釋*/""Name"" /*註釋*/: /*註釋*/""CMS"" /*註釋*/,//註釋
                    /*註釋*/
                    ""Children"":[//註釋
                                    1/*註釋*/,
                                    2/*註釋*/
                                ]//註釋
                }//註釋
                /*此處*/
                ";
var obj = JsonSerializer.ToObject(json);
obj=>Name:CMS
obj=>Children:Array(2)

 

忽略互引用所導致的堆棧迴圈

class A
{
    public B b;
}
class B
{
    public A a;
}
A.b=B;
B.a=A;

A指向B, B指向A, 在序列化時這種情況會發生無限迴圈.可通過KoobooJson的序列化配置項中的屬性來設定這種情況下所對應的結果

JsonSerializerOption option = new JsonSerializerOption
{
    ReferenceLoopHandling = JsonReferenceHandlingEnum.Null
};
string json = JsonSerializer.ToJson(a, option);
json => {\"b\":{\"a\":null}}
------
ReferenceLoopHandling = JsonReferenceHandlingEnum.Empty
json => {\"b\":{\"a\":{}}}
-----
ReferenceLoopHandling = JsonReferenceHandlingEnum.Remove
json => {\"b\":{}}

 

忽略Null值

class A
{
    public string a;
}
A.a=null;
JsonSerializerOption option = new JsonSerializerOption { IsIgnoreValueNull = true };
var json = JsonSerializer.ToJson(A, option);
json => {}

 

排序特性

class A
{
    [JsonOrder(3)]
    public int a;
    [JsonOrder(2)]
    public int b;
    [JsonOrder(1)]
    public int c;
}

可通過[JsonOrder(int orderNum)]來排序序列化的json元素位置. 如果是正常沒有通過[JsonOrder]排序元素,那麼解析出來的Json則是預設順序:{"a":0,"b":0,"c":0} 上面樣例通過[JsonOrder]排序後是這樣的:{"c":0,"b":0,"a":0}

 

忽略序列化元素

class A
{
    [IgnoreKey]
    public int a;
    public int b;   
}

可通過[IgnoreKey]特性來標記序列化和反序列化要忽略的元素 json => {"b":0} 當然, 也可以通過配置來動態選擇忽略對象

JsonSerializerOption option = new JsonSerializerOption { IgnoreKeys = new List<string>(){"b"} };
var json = JsonSerializer.ToJson(A, option);
json => {}

 

序列化時僅包含該元素

class A
{
    [JsonOnlyInclude]
    public int a;
    public int b;  
    public int c;  
}
json => {\"a\":0}

如果一個model里包含幾十個元素, 而你僅想序列化其中一個, 那麼就沒必要對每一個元素進行[IgnoreKey]標記,只需要對想要序列化的元素標記[JsonOnlyInclude]即可

 

時間格式

JsonSerializerOption option = new JsonSerializerOption { DatetimeFormat=DatetimeFormatEnum.ISO8601 };
json => 2012-01-02T03:04:05Z

JsonSerializerOption option = new JsonSerializerOption { DatetimeFormat=DatetimeFormatEnum.RFC1123 };
json => Thu, 10 Apr 2008 13:30:00 GMT

JsonSerializerOption option = new JsonSerializerOption { DatetimeFormat=DatetimeFormatEnum.Microsoft };
json => \/Date(628318530718)\/

 

首字母大小寫

class A
 {
     public int name;  
 }
 JsonSerializerOption option = new JsonSerializerOption { JsonCharacterRead=JsonCharacterReadStateEnum.InitialUpper };
 json => {\"Name\":0}

在對model序列化時可以指定key的首字母大小寫,反序列化時也可以設置對字元串不區分大小寫.首字母大小寫屬於內嵌支持, 在解析時並不會影響性能

 

別名特性

 class A
 {
     [Alias("R01_Name")]
     public int name;  
 }
 json => {\"R01_Name\":0}

當元素被標記[Alias]後,KoobooJson無論序列化還是反序列化都會按照Alias來進行解析

 

反序列化時指定構造函數

class A
{
    public A(){}
    [JsonDeserializeCtor(3,"ss")]
    public A(int a,string b){}
}

在反序列化的時候, 我們不得不調用構造函數來以此創建對象. 在常規情況下, KoobooJson會通過優先順序自動搜索最合適的構造函數,其優先順序順序為: public noArgs => private noArgs => public Args => private Args, 這其中, 會對有參構造函數進行預設值構造.

然而你也可以顯式通過[JsonDeserializeCtor(params object[] args)]特性來指定反序列化時的構造函數, 這樣 當KoobooJson創建A實例的時候就不是通過new A(); 而是new A(3,"ss");

 

值格式化特性

 class A
 {
     [Base64ValueFormat]
     public byte[] a;  
 }

當你需要來覆寫由KoobooJson進行元素解析的行為時, 我們可以繼承一個 ValueFormatAttribute 來覆寫行為.

 class Base64ValueFormatAttribute:ValueFormatAttribute
 {
     public override string WriteValueFormat(object value,Type type, JsonSerializerHandler handler, out bool isValueFormat)
     {
         isValueFormat=true;
         if(value==null)
             return "null";
         else
             return ConvertToBase64((byte[])value);
     }

    public override object ReadValueFormat(string value,Type type, JsonDeserializeHandler handler, out bool isValueFormat)
    {
        isValueFormat=true;
        if(value=="null")
            return null;
        else
            return Base64Convert(value);
    }
}

值格式化特性也可以標記在結構體或類上, 而另一點是對於值格式化器, 也可以以全局的方式來進行配置: 以序列化為例, 可通過 JsonSerializerOption中的GlobalValueFormat委托來進行配置

JsonSerializerOption.GlobalValueFormat=(value,type,handler,isValueFormat)=>
{
     if(type==typeof(byte[]))
     {
         isValueFormat=true;
         if(value==null)
                  return "null";
               else
                  return ConvertToBase64((byte[])value);
     }
     else
     {
        isValueFormat=false;
        return null;
     }
}

值得註意的是,對於byte[]類型的base64解析行為, KoobooJson已經內嵌在配置項中, 只要設置JsonSerializerOption.IsByteArrayFormatBase64=true即可

 

全局Key格式化

對於Model中的Key處理, KoobooJson支持全局的Key格式化器.

 class R01_User
 {
     public string R01_Name;  
     public int R01_Age;
 }

如果我們想把R01這個首碼給去掉, 只需要註冊全局Key格式化器的委托即可

JsonSerializerOption.GlobalKeyFormat=(key,parentType,handler)=>
{
    if(parentType==typeof(R01_User))
    {
        return key.Substring(4);
    }
    return key;
}

這樣,出來的json是這樣的:{"Name":"","Age":""}

同樣, 對於反序列化,我們也同樣應該註冊:

JsonDeserializeOption.GlobalKeyFormat=(key,parentType)=>
{
    if(parentType==typeof(R01_User))
    {
        return "R01_"+key;
    }
    return key;
}

其它

我喜歡和我一樣的人交朋友,不被環境影響,自己是自己的老師,歡迎加群 .Net web交流群, QQ群:166843154 欲望與掙扎

作者:小曾
出處:https://www.cnblogs.com/1996V/p/10607916.html 歡迎轉載,但請保留以上完整文章,在顯要地方顯示署名以及原文鏈接。
.Net交流群, QQ群:166843154 欲望與掙扎

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • VS項目Debug|Release包含排除不同技巧,csproj技巧 ...
  • 之前花了很長的一段時間研究之後,以便把(第三方或)系統的和應用的dll分開,最後還寫了一篇文章介紹,木有想到,今天又入坑了,發現了一個bug。 ...
  • 本文將會根據情況持續更新。 作為一個 `Framework`,`ASP.NET Core` 提供了諸多的擴展點。使用內置的組件和預設的配置通常就能夠滿足部分需求,當需要擴展的時就需要先去找出這些擴展點。除了基於實際應用對 `ASP.NET Core` 進行擴展,也會故意搞一些不切實際的偽需求,解決一... ...
  • 是這樣的,大概10天前,我忽然就想造個輪子,這個輪子的主要作用是收集客戶端的日誌,通過服務端存儲到指定的持久化介質中。客戶端和服務端都可以通過簡單的配置實現快速接入 ...
  • C#訪問修飾符,無時無刻都在使用,這裡記錄一下,如果寫錯的地方,歡迎指正。 總結:類只能被定義為public或internal,嵌套類除外,因為嵌套類也具有成員的形態,同時嵌套類可以訪問所屬類的所有成員,包括私有成員!類預設的訪問修飾符是internal,也可以被修飾為public。 參考: htt ...
  • 原文地址:http://www.entityframeworktutorial.net/basics/what-is-entity-in-entityframework.aspx EF中的實體就是繼承自DbContext類的上下文類中的,DbSet類型的實體類。EF API 將這樣的每個實體映射成數 ...
  • 問題 執行若幹個任務,只需要對其中任意一個的完成進行響應。這主要用於:對一個操作進行多種獨立的嘗試,只要一個嘗試完成,任務就算完成。例如,同時向多個 Web 服務詢問股票價格,但是只關心第一個響應的。 文中舉的是向多個Web服務詢問股票價格的例子。 我曾在過往的工作中遇到另一個不太相似的例子。一個問 ...
  • private void button1_Click(object sender, EventArgs e) { string txt = this.richTextBox1.Text; txt = txt.Replace("\n", ",").Replace(" ", "!"). Replac..... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...