本節中,我們將通過介紹如何完成枚舉資源功能來介紹MapGuide Web擴展的部分源代碼。
在瀏覽器端的地址欄輸入類似如下字符串,就會發送一個枚舉資源的HTTP請求。
http://hostname/mapGuide/mapagent.fcig?OPERATION=EnumerateResources&VERSION=1.0.0& RESOURCEID=Library:// &TYPE=FeatureSource&DEPTH=3 |
下面我們來看看MapGuide Web擴展如何處理這個請求。
在介紹MapGuide Web擴展如何處理枚舉資源HTTP請求之前,讓我們首先來看看MapGuide Web擴展用于處理HTTP請求和響應的類,這些類的類圖如圖19?4所示。
圖 19?5 HTTP請求和響應類的類圖
類MgHttpRequest和類MgHttpResponse分別是對HTTP請求和響應結果的抽象,這兩個類可以用于任何類型的Web應用服務器,也就是說它們的代碼不依賴于任何Web應用服務器API。在文件夾“\MgDev\Web\src\HttpHandler”下可以找到所有前綴為“MgHttp”類的源代碼。
類MgHttpRequest用于處理HTTP請求,它包含了一個HTTP請求頭MgHttpHeader的實例、一個HTTP請求參數HttpRequestParam的實例和一個HTTP請求元數據HttpRequestMetadata的實例。調用方法MgHttpRequest::Execute()會將HTTP請求轉發給MapGuide服務器,當MapGuide服務器處理完這個請求,將請求結果返回給Web擴展之后,這個方法會返回一個MgHttpResponse對象,它表示HTTP請求的響應結果。
接下來讓我們看看MapGuide Web擴展如何處理枚舉資源的HTTP請求。大多數MapGuide用戶都是使用IIS作為Web應用服務器,并且使用Web擴展模塊isapi_MapAgent.dll來處理HTTP請求,所以本節側重于isapi_MapAgent.dll模塊處理HTTP請求的流程,這個處理流程的時序圖如圖19?5所示。
圖?19?6 Web擴展處理枚舉資源請求的時序圖
1) 加載Web應用服務器擴展模塊
如果使用的是IIS Web應用服務,那么在接收到后綴為“fcgi”的HTTP請求后,IIS會加載Web應用服務器擴展模塊isapi_MapAgent.dll或MapAgent.exe。其中,isapi_MapAgent.dll基于IIS API,具有更好的性能;MapAgent.exe基于CGI技術,性能要稍微差一些。如果使用的是Apache Web應用服務器,那么在Windows平臺上Apache會加載Web應用服務器擴展模塊MapAgent.exe,在Linux平臺上會加載模塊mod_mgmapagent.so。
這三個Web服務器擴展模塊實現了類似的功能,它們的源代碼位置如表19?2所示。
服務器擴展模塊 |
源代碼位置 |
isapi_MapAgent.dll |
\Web\src\IsapiAgent |
MapAgent.exe |
\Web\src\CgiAgent |
mod_mgmapagent.so |
\Web\src\ApacheAgent |
表?19?2 Web服務器擴展模塊的源代碼位置
從圖19?6中可以看到這三個Web服務器擴展模塊提供了類似的類,這些類提供了相同的接口,類XXXPostParser用于解析HTTP請求中傳入的參數及參數值,類XXXResponseHandler用于將HTTP請求的響應結果發送給客戶端。其中,“XXX”表示“Cgi”、“Isapi”或“Apache”。
圖?19?7 Web擴展的請求處理模塊
對于模塊isapi_MapAgent.dll,每個“fcgi”類型的HTTP請求會調用此模塊中的方法HttpExtensionProc,這個方法的代碼如下所示,為了便于理解我們只保留了一些核心代碼。
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB) { Initialize(pECB); // 調用GetServerVariable讀取諸如服務器名稱、端口號等服務器變量信息 ...... // 創建MgHttpRequest的實例,用于處理HTTP請求 Ptr request = new MgHttpRequest(wUrl); // 解析HTTP請求中的參數,并且將這些參數置入MgHttpRequest對象中 Ptr params = request->GetRequestParam(); ...... IsapiPostParser postParser(pECB); postParser.Parse(params); ...... // 創建IsapiResponseHandler的實例,用于將響應請求返回給客戶端 IsapiResponseHandler responseHandler(pECB); ...... // 將HTTP請求轉發給MapGuide服務器,當MapGuide服務器處理完這個請求, // 將請求結果返回給Web擴展之后,這個方法會返回一個MgHttpResponse對象, // 它表示HTTP請求的響應結果。 Ptr response = request->Execute(); // 將響應結果返回給客戶端 responseHandler.SendResponse(response); ...... } |
2) 創建MgHttpRequest的實例,用于處理HTTP請求
在加載Web應用服務器擴展模塊后,需要創建一個MgHttpRequest的實例用于處理HTTP請求。類MgHttpRequest并不關聯與任何類型的Web應用服務器,所以Web應用服務器擴展模塊會使用類XXXPostParser解析HTTP請求中傳入的參數及參數值,將這些參數設置到MgHttpRequest對象中。
3) 調用方法MgHttpRequest::Execute()處理HTTP請求
方法MgHttpRequest::Execute()的源代碼如下所示。為了便于理解,我們同樣只保留了一些核心代碼。
MgHttpResponse* MgHttpRequest::Execute() { Ptr hResponse; Ptr result; // 創建MgHttpResponse對象,用于返回HTTP請求響應結果 hResponse = new MgHttpResponse(); result = hResponse->GetResult(); // 獲得HTTP請求中參數“OPERATION”的值 STRING sParamValue = m_requestParam->GetParameterValue(MgHttpResourceStrings::reqOperation); ...... // 根據參數“OPERATION”的值取得對應操作的請求響應處理器 Ptr rrHandler = CreateRequestResponseHandler(sParamValue, result); ....... // 處理HTTP請求 if(rrHandler != NULL) rrHandler->Execute(*hResponse); ...... // 返回響應結果 return SAFE_ADDREF((MgHttpResponse*)hResponse); } |
每個HTTP請求中包含一個參數“OPERATION”,它用于代表操作的類型。對于不同的操作,MapGuide定義了不同的請求響應處理器類,這個類會調用MapGuide服務中對應的方法處理這個操作,這些請求響應處理器類的類圖如圖19?7所示。從圖19?7可以看到,所有請求響應處理器類都繼承自類MgHttpRequestResponseHandler,每一種操作都有一個對應的子類,例如枚舉資源操作使用了類MgHttpEnumerateResources,描述模式操作使用了類MgHttpDescribeSchema。
圖?19?8 HTTP請求響應處理器的類圖
MapGuide定義了一個全局map對象用于存放每種操作對應的請求響應處理器類對象,它的鍵是操作的名稱,值是請求響應處理器類對象。
// 定義一個全局map對象,用于存放每種操作對應的請求響應處理器類對象 static map httpClassCreators; bool InitializeStaticData() { httpClassCreators[MgHttpResourceStrings::opGetMap] = MgHttpGetMap::CreateObject; httpClassCreators[MgHttpResourceStrings::opGetMapUpdate] = MgHttpGetMapUpdate::CreateObject; httpClassCreators[MgHttpResourceStrings::opGetDrawing] = MgHttpGetDrawing::CreateObject; ...... httpClassCreators[MgHttpResourceStrings::opDescribeDrawing] = MgHttpDescribeDrawing::CreateObject; httpClassCreators[MgHttpResourceStrings::opEnumerateResources] = MgHttpEnumerateResources::CreateObject; ...... } |
調用方法MgHttpRequest::CreateRequestResponseHandler(...)可以根據操作的名稱獲得對應的請求響應處理器類對象。對于枚舉資源操作,它會返回一個類MgHttpEnumerateResources的對象。
調用這些請求響應處理器類的Execute(…)方法,會創建一個代理服務類對象,然后調用代理服務中對應的方法。為什么說創建的是一個代理服務類對象呢?我想看完圖19?8之后大家會有所明白。從圖19?8中可以看到,所有的MapGuide服務有兩個子類,一個是名稱為MgProxyXXXService的代理服務類,它是MapGuide Web擴展端的一個類,另一個是名稱為MgServerXXXService的服務器服務類,它是MapGuide服務器端的一個類。MgProxyXXXService最終會將服務請求轉發給服務器端類MgServerXXXService,所以說真正處理服務請求的類是MgServerXXXService,MgProxyXXXService僅僅起到一個轉發的作用,這也就是為什么稱類MgProxyXXXService為代理服務類的原因。
圖?19?9 代理服務和服務器服務類的類圖
對于枚舉資源操作,MgHttpEnumerateResources::Execute(...)的源代碼如下所示。調用這個方法會創建一個代理資源服務類MgProxyResourceService的對象,然后調用MgProxyResourceService::EnumerateResources(...)枚舉資源,最后將操作的執行結果放入MgHttpResponse對象中。
void MgHttpEnumerateResources::Execute(MgHttpResponse& hResponse) { Ptr hResult; hResult = hResponse.GetResult(); // 檢查HTTP請求中的參數 ValidateCommonParameters(); // 創建MgProxyResourceService實例 Ptr mgprService = (MgResourceService*)(CreateService(MgServiceType::ResourceService)); ...... // 執行枚舉資源操作 Ptr byteReader = mgprService-> EnumerateResources(&mgrIdentifier, m_depth, m_type, m_computeChildren); // 如果需要,轉換響應請求的格式 ProcessFormatConversion(byteReader); // 將操作結果放入響應請求中的MgHttpResult對象 hResult->SetResultObject(byteReader, byteReader->GetMimeType()); } MgByteReader* MgProxyResourceService::EnumerateResources( MgResourceIdentifier* resource, INT32 depth, CREFSTRING type, INT32 properties, CREFSTRING fromDate, CREFSTRING toDate, bool computeChildren) { MgCommand cmd; cmd.ExecuteCommand(m_connProp, MgCommand::knObject, MgResourceService::opIdEnumerateResources, 7, Resource_Service, BUILD_VERSION(1,0,0), MgCommand::knObject, resource, MgCommand::knInt32, depth, MgCommand::knString, &type, MgCommand::knInt32, properties, MgCommand::knString, &fromDate, MgCommand::knString, &toDate, MgCommand::knInt8, (int)computeChildren, MgCommand::knNone); SetWarning(cmd.GetWarningObject()); return (MgByteReader*)cmd.GetReturnValue().val.m_obj; } |
4) 將HTTP請求的響應結果返回給客戶端
調用MgHttpRequest::Execute()會返回一個MgHttpResponse對象,這個對象包含了HTTP請求的響應結果。不過,還需要調用方法XXXResponseHandler::SendResponse(...),這樣Web應用服務器才會將這個響應結果返回給客戶端。