ISAPI(Internet Server API)最初是微軟為IIS伺服器所提供的一種CGI應用開發介面,其主要的目的也是為CGI開發提供好的開發介面,如果不負責的講ISAPI也可以認為是類似WinCGI的一種開發模式,只不過ISAPI通過映射宏來取得使用者表單傳送的參數。這一點上和MFC訊息映射宏很相似。 當然ISAPI的推出同時還具備了很多其他的特性:
- ISAPI以DLL動態串連庫的方式實現,所以載入較普通的EXE運行程式快,而且系統不會在使用完後馬上清除掉DLL在記憶體中的空間,所以再次使用時會獲得更快的速度。
- ISAPI在調用者的內部以線程方式運行,所以和CGI進程相比較需要的Runspace也更小。
- 在同一個DLL中可以集中多個處理函數,並且通過映射宏來指明不同的函數分別對什麼樣的請求來進行處理。
- 由於與IIS的整合,所以可以利用ISAPI開發ISAPI過濾器。通過過濾器可以完成例如使用者權限檢測,資料加密,壓縮,日誌等功能,IIS伺服器的功能也可以通過ISAPI過濾器來得到增強。
到目前為止,很多非M$的WEB伺服器也都添加了對ISAPI的支援。 在同一個DLL中可以集中多個處理函數,所以執行ISAPI的請求和執行普通CGI的請求有一點區別,在URL中需要填寫如下形式http://.../cgi-bin/test.dll?function_name&name=xxx&email=yyy,function_name表示的就是功能名稱,為了能夠正確處理你必須將其映射到DLL中的一個處理函數上。對與未映射的功能都將由一個預設函數處理。 VC4.2以上版本為建立一個ISAPI程式提供了嚮導程式,在建立工程時選擇ISAPI Extension Wizard並在以後的對話方塊中選擇建立伺服器擴充和輸入相關的資訊就可以了。 一個最簡單的ISAPI程式至少包含了一個由CHttpServer類所派生的新類,並且在該類中進行了基本的映射,重載了BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer)函數和提供了形式如void Default(CHttpServerContext* pCtxt)的成員函數。幸運的是嚮導程式已經為我們做好了這一切並建立了最基本的代碼。 Default函數用於處理沒有帶參數首先我們修改Default函數中的代碼,首先我們該寫其中的相關代碼成為如下: void CTestisaExtension::Default(CHttpServerContext* pCtxt){//Print the <HTML> <BODY> tags.StartContent(pCtxt);//Print the title.WriteTitle(pCtxt);*pCtxt << _T("<p>示範</p>");*pCtxt << _T("<p>目前是Default成員函數其作用</p>");//Print </HTML> </BODY> tags.EndContent(pCtxt);} 然後用以下方式調用:http://.../cgi-bin/test.dll?Default或http://.../cgi-bin/test.dll?。你就會看到輸出的HTML頁面了。 <FORM ACTION="test.dll?Add" METHOD=POST><INPUT NAME="name"><INPUT NAME="id"><INPUT TYPE=SUBMIT></FORM> 上面表單要求使用者輸入了使用者名稱和ID號,所以傳送的資料形式形式應該如同:http://.../test.dll?Add&name=xxx&id=yyy,我們可以通過處理命令映射宏來指明處理命令的函數,然後通過參數映射宏對資料進行分解,分解後的資料將會存放入指定的變數。使用方法為: ON_PARSE_COMMAND(Add, CTestExtension, ITS_PSTR ITS_I4)ON_PARSE_COMMAND_PARAMS("name id") ON_PARSE_COMMAND宏中需要指明函數名,類名和參數類型列表,ON_PARSE_COMMAND_PARAMS宏中需要根據前一個宏中所列出的參數列表來指明表單中的變數名稱列表。最後一步就是定義一個名稱與請求URL中?後命令名稱相同的成員函數,參數的設定要與ON_PARSE_COMMAND宏中的參數列表定義相符合,在這個例子中為: void CTestExtension::Add(CHttpServerContext* pCtxt,LPTSTR pszName,int iID)。參數類型列表中可以使用下面的類型後面為該類型的說明:
- ITS_EMPTY 無資料
- ITS_PSTR 字串LPCSTR
- ITS_I2 short
- ITS_I4 long
- ITS_R4 float
- ITS_R8 double
下面我們看一個更複雜的表單,表單定義如下: <FORM ACTION="test.dll?Delete" METHOD=POST><INPUT NAME="name"><INPUT NAME="month"><SELECT NAME="mode"><OPTION VALUE=1>All<OPTION VALUE=2>Before<OPTION VALUE=3>After<INPUT TYPE=HIDDEN NAME=pwd VALUE=xxx><INPUT TYPE=SUBMIT></FORM> 定義映射和處理函數如下: ON_PARSE_COMMAND(Add, CTestExtension, ITS_PSTR ITS_I4 ITS_I4 ITS_PSTR)ON_PARSE_COMMAND_PARAMS("name month mode pwd")void CTestExtension::Add(CHttpServerContext* pCtxt,LPTSTR pszName,int iMonth,int iMode,LPTSTR pszPwd)。 ISAPI程式的輸出:在所有的命令處理函數中第一個參數都是CHttpServerContext*指標,我們所有的輸出都要通過它進行,在CHttpServerContext類中定義了<<操作符,通過該操作符我們可以方便的輸出字串,數字,位元據。輸出的方法為: *pCtxt<<"this is a string";*pCtxt<<'c';*pCtxt<<10; 可以看得出輸出是很簡單的,一個完整的輸出過程應該是如同下面的形式: StartContent(pCtxt);//開始輸出WriteTitle(pCtxt);//輸出頭部資訊,相當於輸出<TITLE>*pCtxt << "your string";EndContent(pCtxt);//結束輸出 我們所使用的ISAPI擴充類是由CHttpServer所派生,可以調用CHttpServer::AddHeader來指明返回資料的類型,例如下面的代碼示範了輸出純文字: void CTestisaExtension::Default(CHttpServerContext* pCtxt){AddHeader(pCtxt, "Content-type = text/plain/r/n");(*pCtxt) << "Hello world!/r/n"; } 我們可以重載某些函數來達到加強控制的目的,可重載的函數有以下這些:
- virtual LPCTSTR CHttpServer::GetTitle( ) const;返回<TITLE>部分資訊
- virtual BOOL CHttpServer::OnParseError( CHttpServerContext* pCtxt, int nCause );錯誤處理
- virtual void CHttpServer::StartContent( CHttpServerContext* pCtxt ) const;輸出<HTML><BODY>部分資訊
- virtual void CHttpServer::EndContent( CHttpServerContext* pCtxt ) const;輸出</BODY></HTML>部分資訊
- virtual void CHttpServer::WriteTitle( CHttpServerContext* pCtxt ) const;輸出<TITLE></TITLE>部分資訊
此外我們可以在命令處理函數中利用CHttpServerContext指標來得到CGI中的相關環境變數,在CHttpServerContext中有一個成員變數m_pECB為下面的結構指標。 typedef struct _EXTENSION_CONTROL_BLOCK { DWORD cbSize; //IN 該結構長度 DWORD dwVersion //IN 版本 HCONN ConnID; //IN 串連上下文 DWORD dwHttpStatusCode; //OUT 狀態代碼 CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; //OUT LPSTR lpszMethod; //IN 環境變數REQUEST_METHOD LPSTR lpszQueryString; //IN QUERY_STRING LPSTR lpszPathInfo; //IN PATH_INFO LPSTR lpszPathTranslated; //IN PATH_TRANSLATED DWORD cbTotalBytes; //IN CONTENT_LENGTH DWORD cbAvailable; //IN LPBYTE lpbData; //IN LPSTR lpszContentType; //IN CONTENT_TYPE...其他資訊在此忽略} EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK; 返回 |