Hybrid APP基礎篇(四)->JSBridge的原理

来源:http://www.cnblogs.com/dailc/archive/2016/10/04/5931324.html
-Advertisement-
Play Games

說明 JSBridge實現原理 目錄 前言 參考來源 前置技術要求 楔子 原理概述 簡介 url scheme介紹 實現流程 實現思路 第一步:設計出一個Native與JS交互的全局橋對象 第二步:JS如何調用Native 第三步:Native如何得知api被調用 第四步:分析url-參數和回調的格 ...


說明

JSBridge實現原理

目錄

前言

參考來源

前人栽樹,後臺乘涼,本文參考了以下來源

前置技術要求

閱讀本文前,建議先閱讀以下文章

楔子

上文中簡單的介紹了JSBridge,以及為什麼要用JSBridge,本文詳細介紹它的實現原理

原理概述

簡介

JSBridge是Native代碼與JS代碼的通信橋梁。目前的一種統一方案是:H5觸發url scheme->Native捕獲url scheme->原生分析,執行->原生調用h5。如下圖

url scheme介紹

上圖中有提到url scheme這個概念,那這到底是什麼呢?

  • url scheme是一種類似於url的鏈接,是為了方便app直接互相調用設計的

    具體為,可以用系統的OpenURI打開一個類似於url的鏈接(可拼入參數),然後系統會進行判斷,如果是系統的url scheme,則打開系統應用,否則找看是否有app註冊這種scheme,打開對應app

    需要註意的是,這種scheme必須原生app註冊後才會生效,如微信的scheme為(weixin://)

  • 而本文JSBridge中的url scheme則是仿照上述的形式的一種方式

    具體為,app不會註冊對應的scheme,而是由前端頁面通過某種方式觸發scheme(如用iframe.src),然後Native用某種方法捕獲對應的url觸發事件,然後拿到當前的觸發url,根據定義好的協議,分析當前觸發了那種方法,然後根據定義來執行等

實現流程

基於上述的基本原理,現在開始設計一種JSBridge的實現

實現思路

要實現JSBridge,我們可以進行關鍵步驟分析

  • 第一步:設計出一個Native與JS交互的全局橋對象
  • 第二步:JS如何調用Native
  • 第三步:Native如何得知api被調用
  • 第四步:分析url-參數和回調的格式
  • 第五步:Native如何調用JS
  • 第六步:H5中api方法的註冊以及格式

如下圖:

第一步:設計出一個Native與JS交互的全局橋對象

我們規定,JS和Native之間的通信必須通過一個H5全局對象JSbridge來實現,該對象有如下特點

  • 該對象名為"JSBridge",是H5頁面中全局對象window的一個屬性
    var JSBridge = window.JSBridge || (window.JSBridge = {});
    					
  • 該對象有如下方法
    • registerHandler( String,Function )H5調用 註冊本地JS方法,註冊後Native可通過JSBridge調用。調用後會將方法註冊到本地變數messageHandlers
    • callHandler( String,JSON,Function )H5調用 調用原生開放的api,調用後實際上還是本地通過url scheme觸發。調用時會將回調id存放到本地變數responseCallbacks
    • _handleMessageFromNative( JSON )Native調用 原生調用H5頁面註冊的方法,或者通知H5頁面執行回調方法
  • 如圖

第二步:JS如何調用Native

在第一步中,我們定義好了全局橋對象,可以我們是通過它的callHandler方法來調用原生的,那麼它內部經歷了一個怎麼樣的過程呢?如下

callHandler函數內部實現過程

在執行callHandler時,內部經歷了以下步驟:

  • (1)判斷是否有回調函數,如果有,生成一個回調函數id,並將id和對應回調添加進入回調函數集合responseCallbacks
  • (2)通過特定的參數轉換方法,將傳入的數據,方法名一起,拼接成一個url scheme
    //url scheme的格式如
    //基本有用信息就是後面的callbackId,handlerName與data
    //原生捕獲到這個scheme後會進行分析
    var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data
    					
  • (3)使用內部早就創建好的一個隱藏iframe來觸發scheme
    //創建隱藏iframe過程
    var messagingIframe = document.createElement('iframe');
    messagingIframe.style.display = 'none';
    document.documentElement.appendChild(messagingIframe);
    
    //觸發scheme
    messagingIframe.src = uri;
    					

    註意,正常來說是可以通過window.location.href達到發起網路請求的效果的,但是有一個很嚴重的問題,就是如果我們連續多次修改window.location.href的值,在Native層只能接收到最後一次請求,前面的請求都會被忽略掉。所以JS端發起網路請求的時候,需要使用iframe,這樣就可以避免這個問題。---引自參考來源

第三步:Native如何得知api被調用

在上一步中,我們已經成功在H5頁面中觸發scheme,那麼Native如何捕獲scheme被觸發呢?

根據系統不同,Android和iOS分別有自己的處理方式

Android捕獲url scheme

在Android中(WebViewClient里),通過shouldoverrideurlloading可以捕獲到url scheme的觸發

public boolean shouldOverrideUrlLoading(WebView view, String url){
	//讀取到url後自行進行分析處理
	
	//如果返回false,則WebView處理鏈接url,如果返回true,代表WebView根據程式來執行url
	return true;
}
			

另外,Android中也可以不通過iframe.src來觸發scheme,android中可以通過window.prompt(uri, "");來觸發scheme,然後Native中通過重寫WebViewClient的onJsPrompt來獲取uri

iOS捕獲url scheme

iOS中,UIWebView有個特性:在UIWebView內發起的所有網路請求,都可以通過delegate函數在Native層得到通知。這樣,我們可以在webview中捕獲url scheme的觸發(原理是利用 shouldStartLoadWithRequest)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = [request URL];
     
    NSString *requestString = [[request URL] absoluteString];
    //獲取利潤url scheme後自行進行處理
			

之後Native捕獲到了JS調用的url scheme,接下來就該到下一步分析url了

第四步:分析url-參數和回調的格式

在前面的步驟中,Native已經接收到了JS調用的方法,那麼接下來,原生就應該按照定義好的數據格式來解析數據了

url scheme的格式 前面已經提到。Native接收到Url後,可以按照這種格式將回調參數id、api名、參數提取出來,然後按如下步驟進行

  • (1)根據api名,在本地找尋對應的api方法,並且記錄該方法執行完後的回調函數id
  • (2)根據提取出來的參數,根據定義好的參數進行轉化

    如果是JSON格式需要手動轉換,如果是String格式直接可以使用

  • (3)原生本地執行對應的api功能方法
  • (4)功能執行完畢後,找到這次api調用對應的回調函數id,然後連同需要傳遞的參數信息,組裝成一個JSON格式的參數

    回調的JSON格式為:{responseId:回調id,responseData:回調數據}

    • responseId String型 H5頁面中對應需要執行的回調函數的id,在H5中生成url scheme時就已經產生
    • responseData JSON型 Native需要傳遞給H5的回調數據,是一個JSON格式: {code:(整型,調用是否成功,1成功,0失敗),result:具體需要傳遞的結果信息,可以為任意類型,msg:一些其它信息,如調用錯誤時的錯誤信息}
  • (5)通過JSBridge通知H5頁面回調

    參考 Native如何調用JS

第五步:Native如何調用JS

到了這一步,就該Native通過JSBridge調用H5的JS方法或者通知H5進行回調了,具體如下

//將回調信息傳給H5
JSBridge._handleMessageFromNative(messageJSON);						
			

如上,實際上是通過JSBridge的_handleMessageFromNative傳遞數據給H5,其中的messageJSON數據格式根據兩種不同的類型,有所區別,如下

Native通知H5頁面進行回調

數據格式為: Native通知H5回調的JSON格式

Native主動調用H5方法

Native主動調用H5方法時,數據格式是:{handlerName:api名,data:數據,callbackId:回調id}

  • handlerName String型 需要調用的,h5中開放的api的名稱
  • data JSON型 需要傳遞的數據,固定為JSON格式(因為我們固定H5中註冊的方法接收的第一個參數必須是JSON,第二個是回調函數)
  • callbackId String型 原生生成的回調函數id,h5執行完畢後通過url scheme通知原生api成功執行,並傳遞參數

註意,這一步中,如果Native調用的api是h5沒有註冊的,h5頁面上會有對應的錯誤提示。

另外,H5調用Native時,Native處理完畢後一定要及時通知H5進行回調,要不然這個回調函數不會自動銷毀,多了後會引發記憶體泄漏。

第六步:H5中api方法的註冊以及格式

前面有提到Native主動調用H5中註冊的api方法,那麼h5中怎麼註冊供原生調用的api方法呢?格式又是什麼呢?如下

H5中註冊供原生調用的API

//註冊一個測試函數
JSBridge.registerHandler('testH5Func',function(data,callback){
	alert('測試函數接收到數據:'+JSON.stringify(data));
	callback&&callback('測試回傳數據...');
});				
			

如上述代碼為註冊一個供原生調用的api

H5中註冊的API格式註意

如上代碼,註冊的api參數是(data,callback)

其中第一個data即原生傳過來的數據,第二個callback是內部封裝過一次的,執行callback後會觸發url scheme,通知原生獲取回調信息

進一步完善JSBridge方案

在前文中,已經完成了一套JSBridge方案,這裡,在介紹下如何完善這套方案

思路

github上有一個開源項目,它裡面的JSBridge做法在iOS上進一步優化了,所以參考他的做法,這裡進一步進行了完善。地址marcuswestin/WebViewJavascriptBridge

大致思路就是

  • h5調用Native的關鍵步驟進行拆分,由以前的直接傳遞url scheme變為傳遞一個統一的url scheme,然後Native主動獲取傳遞的參數

    完善以前: H5調用Native->將所有參數組裝成為url scheme->原生捕獲scheme,進行分析

    完善以後: H5調用Native->將所有參數存入本地數組->觸發一個固定的url scheme->原生捕獲scheme->原生通過JSBridge主動獲取參數->進行分析

實現

這種完善後的流程和以前有所區別,如下

JSBridge對象圖解

JSBridge實現完整流程

註意

由於這次完善的核心是:Native主動調用JS函數,並獲取返回值。而在Android4.4以前,Android是沒有這個功能的,所以並不完全適用於Android

所以一般會進行一個相容處理,Android中採用以前的scheme傳法,iOS使用完善後的方案(也便於4.4普及後後續的完善)

完整的JSBridge

上述分析了JSBridge的實現流程,那麼實際項目中,我們就應該結合上述兩種,針對Android和iOS的不同情況,統一齣一種完整的方案,如下

完整調用流程圖

如上圖,結合上述方案後有了一套統一JSBridge方案

另外實現:不採用url scheme方式

前面提到的JSBridge都是基於url scheme的,但其實如果不考慮Android4.2以下,iOS7以下,其實也可以用另一套方案的,如下

  • Native調用JS的方法不變
  • JS調用Native是不再通過觸發url scheme,而是採用自帶的交互,比如

    Android中,原生通過 addJavascriptInterface開放一個統一的api給JS調用,然後將觸發url scheme步驟變為調用這個api,其餘步驟不變(相當於以前是url接收參數,現在變為api函數接收參數)

    iOS中,原生通過JavaScriptCore裡面的方法來註冊一個統一api,其餘和Android中一樣(這裡就不需要主動獲取參數了,因為參數可以直接由這個函數統一接收)

當然了,這隻是一種可行的方案,多一種選擇而已,具體實現流程請參考前面系列文章,本文不再贅述

實現示例

示例說明

本文中包括兩個示例,一個是基礎版本的JSBridge實現,一個是完整版本的JSBridge實現(包括JS,Android,iOS實現等)

實現源碼

基礎版本的JSBridge

這裡只介紹JS的實現,具體Android,iOS實現請參考完整版本,實現如下

(function() {
	(function() {
		var hasOwnProperty = Object.prototype.hasOwnProperty;
		var JSBridge = window.JSBridge || (window.JSBridge = {});
		//jsbridge協議定義的名稱
		var CUSTOM_PROTOCOL_SCHEME = 'CustomJSBridge';
		//最外層的api名稱
		var API_Name = 'namespace_bridge';
		//進行url scheme傳值的iframe
		var messagingIframe = document.createElement('iframe');
		messagingIframe.style.display = 'none';
		messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
		document.documentElement.appendChild(messagingIframe);

		//定義的回調函數集合,在原生調用完對應的方法後,會執行對應的回調函數id
		var responseCallbacks = {};
		//唯一id,用來確保每一個回調函數的唯一性
		var uniqueId = 1;
		//本地註冊的方法集合,原生只能調用本地註冊的方法,否則會提示錯誤
		var messageHandlers = {};

		//實際暴露給原生調用的對象
		var Inner = {
			/**
			 * @description 註冊本地JS方法通過JSBridge給原生調用
			 * 我們規定,原生必須通過JSBridge來調用H5的方法
			 * 註意,這裡一般對本地函數有一些要求,要求第一個參數是data,第二個參數是callback
			 * @param {String} handlerName 方法名
			 * @param {Function} handler 對應的方法
			 */
			registerHandler: function(handlerName, handler) {
				messageHandlers[handlerName] = handler;
			},
			/**
			 * @description 調用原生開放的方法
			 * @param {String} handlerName 方法名
			 * @param {JSON} data 參數
			 * @param {Function} callback 回調函數
			 */
			callHandler: function(handlerName, data, callback) {
				//如果沒有 data
				if(arguments.length == 3 && typeof data == 'function') {
					callback = data;
					data = null;
				}
				_doSend({
					handlerName: handlerName,
					data: data
				}, callback);
			},
			/**
			 * @description 原生調用H5頁面註冊的方法,或者調用回調方法
			 * @param {String} messageJSON 對應的方法的詳情,需要手動轉為json
			 */
			_handleMessageFromNative: function(messageJSON) {
				setTimeout(_doDispatchMessageFromNative);
				/**
				 * @description 處理原生過來的方法
				 */
				function _doDispatchMessageFromNative() {
					var message;
					try {
						if(typeof messageJSON === 'string'){
							message = JSON.parse(messageJSON);
						}else{
							message = messageJSON;
						}	
					} catch(e) {
						//TODO handle the exception
						console.error("原生調用H5方法出錯,傳入參數錯誤");
						return;
					}

					//回調函數
					var responseCallback;
					if(message.responseId) {
						//這裡規定,原生執行方法完畢後準備通知h5執行回調時,回調函數id是responseId
						responseCallback = responseCallbacks[message.responseId];
						if(!responseCallback) {
							return;
						}
						//執行本地的回調函數
						responseCallback(message.responseData);
						delete responseCallbacks[message.responseId];
					} else {
						//否則,代表原生主動執行h5本地的函數
						if(message.callbackId) {
							//先判斷是否需要本地H5執行回調函數
							//如果需要本地函數執行回調通知原生,那麼在本地註冊回調函數,然後再調用原生
							//回調數據有h5函數執行完畢後傳入
							var callbackResponseId = message.callbackId;
							responseCallback = function(responseData) {
								//預設是調用EJS api上面的函數
								//然後接下來原生知道scheme被調用後主動獲取這個信息
								//所以原生這時候應該會進行判斷,判斷對於函數是否成功執行,並接收數據
								//這時候通訊完畢(由於h5不會對回調添加回調,所以接下來沒有通信了)
								_doSend({
									handlerName: message.handlerName,
									responseId: callbackResponseId,
									responseData: responseData
								});
							};
						}

						//從本地註冊的函數中獲取
						var handler = messageHandlers[message.handlerName];
						if(!handler) {
							//本地沒有註冊這個函數
						} else {
							//執行本地函數,按照要求傳入數據和回調
							handler(message.data, responseCallback);
						}
					}
				}
			}

		};
		/**
		 * @description JS調用原生方法前,會先send到這裡進行處理
		 * @param {JSON} message 調用的方法詳情,包括方法名,參數
		 * @param {Function} responseCallback 調用完方法後的回調
		 */
		function _doSend(message, responseCallback) {
			if(responseCallback) {
				//取到一個唯一的callbackid
				var callbackId = Util.getCallbackId();
				//回調函數添加到集合中
				responseCallbacks[callbackId] = responseCallback;
				//方法的詳情添加回調函數的關鍵標識
				message['callbackId'] = callbackId;
			}

			//獲取 觸發方法的url scheme
			var uri = Util.getUri(message);
			//採用iframe跳轉scheme的方法
			messagingIframe.src = uri;
		}

		var Util = {
			getCallbackId: function() {
				//如果無法解析埠,可以換為Math.floor(Math.random() * (1 << 30));
				return 'cb_' + (uniqueId++) + '_' + new Date().getTime();
			},
			//獲取url scheme
			//第二個參數是相容android中的做法
			//android中由於原生不能獲取JS函數的返回值,所以得通過協議傳輸
			getUri: function(message) {
				var uri = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
				if(message) {
					//回調id作為埠存在
					var callbackId, method, params;
					if(message.callbackId) {
						//第一種:h5主動調用原生
						callbackId = message.callbackId;
						method = message.handlerName;
						params = message.data;
					} else if(message.responseId) {
						//第二種:原生調用h5後,h5回調
						//這種情況下需要原生自行分析傳過去的port是否是它定義的回調
						callbackId = message.responseId;
						method = message.handlerName;
						params = message.responseData;
					}
					//參數轉為字元串
					params = this.getParam(params);
					//uri 補充
					uri += ':' + callbackId + '/' + method + '?' + params;
				}

				return uri;
			},
			getParam: function(obj) {
				if(obj && typeof obj === 'object') {
					return JSON.stringify(obj);
				} else {
					return obj || '';
				}
			}
		};
		for(var key in Inner) {
			if(!hasOwnProperty.call(JSBridge, key)) {
				JSBridge[key] = Inner[key];
			}
		}

	})();

	//註冊一個測試函數
	JSBridge.registerHandler('testH5Func', function(data, callback) {
		alert('測試函數接收到數據:' + JSON.stringify(data));
		callback && callback('測試回傳數據...');
	});
	/*
	 ***************************API********************************************
	 * 開放給外界調用的api
	 * */
	window.jsapi = {};
	/**
	 ***app 模塊 
	 * 一些特殊操作
	 */
	jsapi.app = {
		/**
		 * @description 測試函數
		 */
		testNativeFunc: function() {
			//調用一個測試函數
			JSBridge.callHandler('testNativeFunc', {}, function(res) {
				callback && callback(res);
			});
		}
	};
})();				
			

完整版本的JSBridge

由於內容較多,已經單獨提取成一個模塊,參考 Hybrid APP基礎篇(五)->JSBridge實現示例


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

-Advertisement-
Play Games
更多相關文章
  • iOS多線程技術方案 === 目錄 "一、多線程簡介" "1、多線程的由來" "2、耗時操作的模擬試驗" "3、進程和線程" "4、多線程的概念及原理" "5、多線程的優缺點和一個Tip" "6、主線程" "7、技術方案" "二、Pthread" "1、函數" "2、參數和返回值" "3、使用" " ...
  • 眾所周知,viewPager是能夠滑動的,但有時候我們需要禁止它的滑動(微笑地面對*—……—*)。 情況是這樣的: activity中有一個viewPager,viewPager中加入3個Fragment,第三個Fragment中又使用了一個viewPager,這個viewPager中又加入了幾個F ...
  • 【框架】: 公共部分:左側菜單、TitleBar、RadioGroup(3個RadioButton:X、Y、Z) 選擇X頁面:指示器+ViewPager 【要達成的效果】: (1)左側選擇A,進入X頁面,X1聯網刷新頁面,此時禁止X2預載入—>滑動到X2頁面,X2才聯網刷新—>X3—>X4; (2) ...
  • Activty啟動提供了四種啟動模式。launchMode: standard:每次啟動新的活動視窗(new操作) singleTop:如果在棧頂是目標活動,則直接打開.否則開啟新的活動視窗(new). singleTask和singleInstance基本上相同.差別在於若根活動設置為single ...
  • 當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷毀的,例如當用戶按BACK鍵的時候。 註意上面的雙引號,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有 ...
  • 本章節主要為之前項目 JXHomepwner 添加照片功能(項目地址)。具體任務就是顯示一個 UIImagePickerController 對象,使用戶能夠為 JXItem 對象拍照並保存。拍攝的照片會和相應的 JXItem 對象建立關聯,當用戶進入某個 JXItem 對象的詳細視圖的時候,可以看 ...
  • 說明 JSBridge實現示例 目錄 前言 參考來源 楔子 JS實現部分 說明 實現 Android實現部分 說明 JSBridge類 實現 Callback類 實現 Webview容器關鍵代碼 實現 API 類實現 iOS實現部分 說明 WebViewJavascriptBridgeBase 實現 ...
  • 一直想弄清楚onTouchEvent,onInterceptTouchEvent,dispatchTouchEvent的執行順序,以及內部使用switch (event.getAction())中的執行順序。趁這次機會趕緊弄清楚。 重寫上面幾個方法後。我們在LogCat中看看列印的結果。 一.isO ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...