架構搭建----基於DDD領域驅動設計的WCF+EF+WPF分層框架(2)

来源:http://www.cnblogs.com/acssoft/archive/2016/04/09/5370388.html
-Advertisement-
Play Games

寫在最前面:轉載請註明出處 目錄置頂: 關於項目 基於DDD領域驅動設計的WCF+EF+WPF分層框架(1) 架構搭建 基於DDD領域驅動設計的WCF+EF+WPF分層框架(2) WCF服務端具體實現 基於DDD領域驅動設計的WCF+EF+WPF分層框架(3) WCF客戶端配置以及代理 基於DDD領 ...


寫在最前面:轉載請註明出處

目錄置頂:

架構搭建

架構是基於DDD領域驅動模型的,DDD的思想我就不介紹了,園裡有挺多的介紹了,另外在下麵的架構圖裡面也會有體現,架構圖本應該用UML圖展示一下的,我覺得麻煩,我直接把我的項目截一個圖介紹一下:

項目我分了四個解決方案文件夾:ACS.OA.Common、ACS.OA.UIClient、ACS.OA.WCFServer、ACS.OA.WebSite(這個本次博客不打算介紹,直接忽略)。

基礎設施層 ACS.OA.Common

這個文件夾裡面我建了Base、Global、Model、WCFContract四個項目,這四個項目大家看名稱就知道作用了。

Base的內部結構我也截一張圖吧

這個重點在CS文件夾裡面,Cache緩存類,Communication通訊類,CustomException 異常處理類,Lib幫助集合(比較多,我把截圖放旁邊了),Log日誌類(Log4Net)

我重點講一下通訊類Pactet數據包

 1    /// <summary>
 2    /// 打包、解包
 3    /// </summary>
 4    /// <typeparam name="T">數據類型 object</typeparam>
 5     public abstract class Packet<T>
 6     {        
 7         /// <summary>
 8         /// 打包數據
 9         /// </summary>
10         /// <param name="t"></param>
11         /// <param name="strKey">加密Key</param>
12         /// <returns></returns>
13         public static byte[] PacketData(T t, string strKey)
14         {
15             var jSetting = new JsonSerializerSettings();
16             jSetting.NullValueHandling = NullValueHandling.Ignore;      //忽略為NULL的值
17 
18             byte[] bytContent;
19             string strJson = JsonConvert.SerializeObject(t, jSetting);  //T轉Json
20             strJson = DESHelper.Encrypt(strJson, strKey);               //加密
21             bytContent = SerializeHelper.Serialize(strJson);            //壓縮轉byte[]
22 
23             Init(bytContent);
24             return bytContent;
25         }
26 
27         /// <summary>
28         /// 解包數據
29         /// </summary>
30         /// <param name="bytContent"></param>
31         /// <param name="strKey">解密Key</param>
32         /// <returns></returns>
33         public static T DePacketData(byte[] bytContent, string strKey)
34         {
35             var jSetting = new JsonSerializerSettings();
36             jSetting.NullValueHandling = NullValueHandling.Ignore;                       //忽略為NULL的值
37 
38             T t;
39             string strJson = SerializeHelper.Deserialize(bytContent).ToString();         //解壓縮轉string
40             strJson = DESHelper.Decrypt(strJson, strKey);                                //解密
41             t = (T)JsonConvert.DeserializeObject(strJson, typeof(T), jSetting);           //Json轉T
42             return t;
43         }
44 
45     }
View Code

裡面的DESHelper的加密解密方法用的是TripleDESCryptoServiceProvider,需要的話自己引用一下就可以自己寫了,這個實例比較多,我就不貼代碼了。

我說一下我為何這麼做的原因

打包步驟:

我先把實體類轉為Json字元串目的為統一傳輸規則,這樣做不用考慮發送方和接收方是什麼語言開發的,接收方收到json解析就行了。

我又把Json串加密,這是為了安全考慮,數據傳輸用明文不安全吧。

轉byte[] 壓縮一下,文件會小很多,考慮的是傳輸效率。

解包步驟 就是打包的逆向了,不解釋了啊。

 Lib幫助類集合裡面,我講一下WCFHandler類,先貼代碼

  1     public class WCFHandler
  2     {
  3         public static T CreateHttpServiceClient<T>(string webAddress, string serviceName)
  4         {
  5             T t = default(T);
  6             object obj = Activator.CreateInstance(typeof(T), new object[]
  7             {
  8                 GetHttpBinding(),
  9                 GetEndpoint(webAddress, serviceName)
 10             });
 11             t = (T)(obj);
 12             return t;
 13         }
 14 
 15         public static T CreateTcpServiceClient<T>(string webAddress, string serviceName, bool isStream=false)
 16         {
 17             T t = default(T);
 18             object obj;
 19             if (isStream) //流傳輸
 20             {
 21                 obj = Activator.CreateInstance(typeof(T), new object[]
 22                 {
 23                     GetFileTcpBinding(),
 24                     GetEndpoint(webAddress, serviceName)
 25                 });
 26             }
 27             else         //緩存傳輸
 28             {
 29                 obj = Activator.CreateInstance(typeof(T), new object[]
 30                 {
 31                     GetTcpBinding(),
 32                     GetEndpoint(webAddress, serviceName)
 33                 });
 34             }
 35             t = (T)(obj);
 36             return t;
 37         }
 38         
 39         public static NetTcpBinding GetTcpBinding()
 40         {
 41             return new NetTcpBinding
 42             {
 43                 MaxBufferPoolSize = 2147483646L,
 44                 MaxReceivedMessageSize = 2147483646L,
 45                 CloseTimeout = new TimeSpan(0, 0, 10),
 46                 OpenTimeout = new TimeSpan(0, 0, 10),
 47                 ReceiveTimeout = TimeSpan.MaxValue,
 48                 SendTimeout = new TimeSpan(0, 20, 0),
 49                 ReaderQuotas =
 50                 {
 51                     MaxDepth=64,
 52                     MaxStringContentLength=2147483646,
 53                     MaxArrayLength=2147483646,
 54                     MaxBytesPerRead=2147483646,
 55                     MaxNameTableCharCount=2147483646
 56                 },
 57                 ReliableSession =
 58                 {
 59                     Enabled = true,
 60                     Ordered = true,
 61                     InactivityTimeout = new TimeSpan(0, 0, 10)
 62                 },
 63                 Security =
 64                 {
 65                     Mode=SecurityMode.None,
 66                     Message =
 67                     {
 68                         ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows
 69                     },
 70                     Transport =
 71                     {
 72                         ClientCredentialType =  TcpClientCredentialType.Windows
 73                     }
 74                 },
 75 
 76             };
 77         }
 78 
 79 
 80         /// <summary>
 81         /// TCP大文件斷點續傳
 82         /// </summary>
 83         /// <returns></returns>
 84         public static NetTcpBinding GetFileTcpBinding()
 85         {
 86             return new NetTcpBinding
 87             {
 88                 MaxBufferPoolSize = 2147483646L,
 89                 MaxReceivedMessageSize = 2147483646L,
 90                 CloseTimeout = new TimeSpan(0, 0, 10),
 91                 OpenTimeout = new TimeSpan(0, 0, 10),
 92                 ReceiveTimeout = TimeSpan.MaxValue,
 93                 SendTimeout = new TimeSpan(0, 20, 0),
 94                 TransferMode=TransferMode.Streamed,
 95                 ReaderQuotas =
 96                 {
 97                     MaxDepth=64,
 98                     MaxStringContentLength=2147483646,
 99                     MaxArrayLength=2147483646,
100                     MaxBytesPerRead=2147483646,
101                     MaxNameTableCharCount=2147483646
102                 },
103                 //ReliableSession =
104                 //{
105                 //    Enabled = true,
106                 //    Ordered = true,
107                 //    InactivityTimeout = new TimeSpan(1, 0, 0)
108                 //},
109                 //Security =
110                 //{
111                 //    Mode=SecurityMode.None,
112                 //    Message =
113                 //    {
114                 //        ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows
115                 //    },
116                 //    Transport =
117                 //    {
118                 //        ClientCredentialType =  TcpClientCredentialType.Windows
119                 //    }
120                 //},
121 
122             };
123         }
124 
125         public static WSHttpBinding GetHttpBinding()
126         {
127             return new WSHttpBinding
128             {
129                 MaxBufferPoolSize = 2147483646L,
130                 MaxReceivedMessageSize = 2147483646L,
131                 CloseTimeout = new TimeSpan(0, 0, 10),
132                 OpenTimeout = new TimeSpan(0, 0, 10),
133                 ReceiveTimeout = new TimeSpan(0, 20, 0),
134                 SendTimeout = new TimeSpan(0, 20, 0),
135                 ReliableSession =
136                 {
137                     Enabled = true,
138                     Ordered = true,
139                     InactivityTimeout = new TimeSpan(0, 0, 10)
140                 },
141                 UseDefaultWebProxy = false,
142                 Security =
143                 {
144                     Mode = SecurityMode.None,
145                     Message =
146                     {
147                         ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows
148                     },
149                     Transport =
150                     {
151                         ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows
152                     }
153                 },
154             };
155         }
156         public static EndpointAddress GetEndpoint(string webAddress, string serviceName)
157         {
158             Uri uri = new Uri(webAddress + "/" + serviceName + ".svc");
159             return new EndpointAddress(uri, new AddressHeader[0]);
160         }
161 
162         public static EndpointAddress GetIMEndpoint(string webAddress, string serviceName)
163         {
164             Uri uri = new Uri(webAddress + "/" + serviceName + ".svc");
165             return new EndpointAddress(uri, new AddressHeader[0]);
166         }
167     }
View Code

這個裡面是NET.TCP 和WSHttp 的WCF配置,這個類主要用於客戶端的 需要傳入兩個參數“webAddress”,“serviceName”

webaddress我的做法是配置在客戶端的App.Config讀取。

serviceName我是根據每一個不同方法手工寫入的

我再貼一下用法實例

 1        /// <summary>
 2         /// 艾克仕網路雲OA企業名片
 3         /// </summary>
 4         public FirmCardPacket GetFirmCard(FirmCardPacket packet)
 5         {
 6             using (SettingServiceClient client = CreateTcpServiceClient<SettingServiceClient>(Caches.GolbalCache.WCFAddressCache, "MainClient/SettingService"))
 7             {
 8                 client.Open();
 9                 byte[] bt = null;
10                 bt = Packet<FirmCardPacket>.PacketData(packet, strKey);
11                 bt = client.GetFirmCard(bt);
12                 packet = Packet<FirmCardPacket>.DePacketData(bt, strKey);
13                 client.Close();
14             }
15             return packet;
16         }

這個現在就展示一下用法,到我把WCF代理講完,在UI裡面我詳細講。


Base主要的是這些了,下麵講Global

先貼圖

 Global項目就是它的字面意思,全局的信息或者配置就放在這裡。這個不用多講了,大家如果使用我這套架構的話,如何劃分全局還得靠自己,每個項目一般都不一樣的。


下麵是Model

 

Model我的文件夾分類規則是一個客戶端項目對應一個Model文件夾,多個客戶端公用的在Common文件夾,Base文件夾是基類舉一個例子說明基類的用法

WPF需要實時刷新界面數據的話,我的實體如IMinfo就會繼承Notify。IMpacket是具體的數據包需要繼承PacketModel。

PacketModel裡面是一些公共屬性,LoginId,FirmId,ResultCode,ResultMsg 這些信息。

我貼一下Notify的代碼

 1 public abstract class Notify : INotifyPropertyChanged
 2     {
 3         public event PropertyChangedEventHandler PropertyChanged;
 4 
 5         protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
 6         {
 7             if (object.Equals(storage, value)) return false;
 8 
 9             storage = value;
10             this.OnPropertyChanged(propertyName);
11 
12             return true;
13         }
14 
15         protected void OnPropertyChanged(string propertyName)
16         {
17             var eventHandler = this.PropertyChanged;
18             if (eventHandler != null)
19             {
20                 eventHandler(this, new PropertyChangedEventArgs(propertyName));
21             }
22         }        
23     }

這個代碼不解釋了,做WPF的都知道。


ACS.OA.WCFContract

 WCF契約,我為何要寫在這裡呢?是因為我是使用SvcUtil生成的WCF代理類,這個代理類和WCF服務庫都公用這個契約,代理類和服務庫的代碼就會很簡潔了,而且可以保證代理類和服務庫的方法一致。

契約不用貼圖,WCF有明確規定的,等到即時通訊需要雙工的地方我會貼特定的契約介面。

 

到此,ACS.OA.Common解決方案文件夾講完,下麵講ACS.OA.WCFServer,不要走開,精彩還在後面

DDD WCF 服務層 ACS.OA.WCFServer

 這個文件夾裡面我建了ACS.OA.Application、Domain、Repositories、WCFService、WCFServiceLib 五個項目。

這部分我將DDD和WCF整合統一放在WCFServer解決方案文件夾下。

我講一下數據處理過程:UI層通過WCF服務通信到WCFService由WCFServiceLib-->Application-->Domain-->Repositories。

WCFService 項目裡面只有SVC和Web.Config

契約如前面寫的我已經單獨新建項目放在ACS.OA.Common文件夾中,這裡引用就可以了。svc.cs文件我也刪掉放在WCFServiceLib中,這裡也是引用。

我貼一下Web.config配置給大家做一個參考

 1   <system.serviceModel>
 2     <bindings>
 3       <netTcpBinding>
 4         <binding name="OATcpBinding" closeTimeout="00:00:10" openTimeout="00:00:10"
 5           receiveTimeout="00:30:00" sendTimeout="00:30:00" transactionFlow="true"
 6           transferMode="Buffered" maxBufferPoolSize="2147483646" maxReceivedMessageSize="2147483646"
 7           portSharingEnabled="true">
 8           <readerQuotas maxDepth="64" maxStringContentLength="2147483646"
 9             maxArrayLength="2147483646" maxBytesPerRead="2147483646" maxNameTableCharCount="2147483646" />
10           <reliableSession ordered="true" inactivityTimeout="00:00:10"
11             enabled="true" />
12           <security mode="None" />
13         </binding>        
14       </netTcpBinding>
15 
16       <wsHttpBinding>
17         <binding name="OAHttpBinding" closeTimeout="00:05:00" openTimeout="00:05:00"
18           receiveTimeout="00:05:00" sendTimeout="00:05:00" transactionFlow="true"
19           maxBufferPoolSize="2147483646" maxReceivedMessageSize="2147483646"
20           messageEncoding="Text">
21           <readerQuotas maxDepth="64" maxStringContentLength="2147483646"
22             maxArrayLength="2147483646" maxBytesPerRead="2147483646" maxNameTableCharCount="2147483646" />
23           <reliableSession ordered="true" inactivityTimeout="01:00:00"
24             enabled="true" />
25           <security mode="None" />
26         </binding>
27       </wsHttpBinding>
28     </bindings>
29     <services>
30       
31       <!--即時通訊Begin-->
32       <service behaviorConfiguration="OATcpBehavior" name="ACS.OA.WCFServiceLib.MainClient.IMService">
33         <endpoint binding="netTcpBinding" bindingConfiguration="OATcpBinding" name="IMService" contract="ACS.OA.WCFContract.MainClient.IIMService">
34           <identity>
35             <dns value="OAMainClient"/>
36           </identity>
37         </endpoint>
38         <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
39       </service>
40       <!--即時通訊End-->
41 
42       <!--文件斷點續傳Begin-->
43       <service behaviorConfiguration="OATcpBehavior" name="ACS.OA.WCFServiceLib.Common.ACSFileService">
44         <endpoint binding="netTcpBinding" bindingConfiguration="OATcpBinding" name="ACSFileService" contract="ACS.OA.WCFContract.Common.IACSFileService">
45           <identity>
46             <dns value="OAMainClient"/>
47           </identity>
48         </endpoint>
49         <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
50       </service>
51       <!--文件斷點續傳End-->
52 
53     </services>
54     <behaviors>
55       <serviceBehaviors>
56         <behavior>
57           <!-- 為避免泄漏元數據信息,請在部署前將以下值設置為 false -->
58           <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
59           <!-- 要接收故障異常詳細信息以進行調試,請將以下值設置為 true。在部署前設置為 false 以避免泄漏異常信息 -->
60           <serviceDebug includeExceptionDetailInFaults="false"/>
61         </behavior>
62         <behavior name="OATcpBehavior">
63           <serviceMetadata httpGetEnabled="false"/>
64           <serviceDebug includeExceptionDetailInFaults="true"/>
65           <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
66           <serviceTimeouts transactionTimeout="00:05:00"/>
67           <!--會話最大數量-->
68           <serviceThrottling maxConcurrentSessions="10000" maxConcurrentCalls="10000" maxConcurrentInstances="10000"/>
69         </behavior>
70 
71         <behavior name="OAHttpBehavior">
72           <serviceMetadata httpGetEnabled="true"/>
73           <serviceDebug includeExceptionDetailInFaults="true"/>
74           <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
75           <serviceTimeouts transactionTimeout="00:05:00"/>
76           <!--會話最大數量-->
77           <serviceThrottling maxConcurrentSessions="10000" maxConcurrentCalls="10000" maxConcurrentInstances="10000"/>
78         </behavior>
79       </serviceBehaviors>
80       <endpointBehaviors>
81         <behavior name="DuplexendpointBehaviors">
82           <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
83         </behavior>
84       </endpointBehaviors>
85     </behaviors>
86     <protocolMapping>
87       <add binding="basicHttpsBinding" scheme="https"/>
88     </protocolMapping>
89     <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
90   </system.serviceModel>
Web.config View Code

 


ACS.OA.WCFServiceLib

 

這裡我貼一下WCFServiceLibBase和其中一個服務的代碼

public class WCFServiceLibBase
    {

        public string HandleException(Exception ex, bool isThrowOut = false)
        {
            var myExceptionManager = ExceptionManager.GetInstance();

            string returnValue = myExceptionManager.HandleException(ex, th

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

-Advertisement-
Play Games
更多相關文章
  • 1.單例設計模式核心作用: 保證一個類只有一個實例,並且提供了訪問該實例的全局訪問點 2.常見應用場景: window的任務管理器 項目中讀取配置文件一般也是一個單例模式 資料庫連接池的設計也是採用單例模式,因為資料庫連接是一種資料庫資源 操作系統的文件管理系統,也是單例模式,一個操作系統只能有一個 ...
  • 線程安全與鎖優化 1.線程安全 (1)當多個線程訪問一個對象時,如果不考慮這些線程在執行時環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其他的協調操作,調用這個對象的行為都可以獲得正確的結果,那麼這個對象時是線程安全的。 (2)Java語言中的線程安全 a)可以將Java語言中 ...
  • #登堂入室——JAVA流——文章出自PeterYe,不得私自轉載###我所知道的>java.io裡面的[流],就仿佛太平洋裡面的水一樣,浩浩蕩蕩,橫無際涯。。。——天黑請閉眼,明日再續。。。 ...
  • 獲取【下載地址】 QQ: 313596790 【免費支持更新】三大資料庫 mysql oracle sqlsever 更專業、更強悍、適合不同用戶群體【新錄針對本系統的視頻教程,手把手教開發一個模塊,快速掌握本系統】A 集成代碼生成器(開發利器); 技術:313596790 增刪改查的處理類,ser ...
  • 培訓大數據架構開發! 從零基礎到高級,一對一培訓![技術QQ:2937765541] 課程體系: 獲取視頻資料和培訓解答技術支持地址 課程展示(大數據技術很廣,一直線上為你培訓解答!): 獲取視頻資料和培訓解答技術支持地址 ...
  • Atiti。流量提升軟體設計大綱規劃 v1 q45 1. 通用數據管理1 2. 網頁Url管理模塊1 3. 網站domain管理1 4. ad廣告管理2 5. Task任務管理2 6. 任務執行功能::進入網站,隨機瀏覽網頁2 7. 系統設置2 8. 界面跨平臺h52 9. 開發語言java+h53 ...
  • atitit.userService 用戶系統設計 v5 q330 1. 新特性1 2. Admin login1 3. 用戶註冊登錄2 3.1. <!-- 會員註冊使用 --> 商家註冊2 3.2. <!-- 會員登錄使用 -->3 3.3. <!-- 會員退出登錄 -->3 3.4. <!-- ...
  • 在所有的設計模式開篇中,總是說一個好的架構,或多或少都會有設計模式的出現。當然或多或少也會使用設計模式的相關原則: SOLID+迪米爾原則 1.優化代碼的第一步:單一職責原則 S:單一職責鏈原則:英文名稱為Single Responsibility Principle(SRP) 定義:就一個類而言, ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...