ISAPI扩展中获取GET和POST数据的方法

ISAPI扩展中获取表单的GET和POST数据是一个常见的需求。本文将介绍如何在ISAPI扩展中实现这一功能,包括MFC和非MFC两种版本的实现方法。由于非MFC版本的ISAPI扩展在速度和同时连接数方面更可靠,因此本文也将包含非MFC版本的实现。

功能介绍

无论是MFC还是非MFC版本,都使用了基于STL的Vector、Map和String类。在MFC版本中,字符串集合基于_bstr_t类型,而在非MFC版本中,字符串集合基于STL String类型。MFC版本使用ISAPIMFC宏来检索服务器变量和参数数据,而非MFC版本则使用WriteClient和ServerSupportFunction HTTP函数。

默认方法会在浏览器中写入一个包含复选框、编辑框、单选按钮、文本区域甚至文件类型编辑框的复杂表单。目的是将所有POST参数接收到同一个DLL扩展中。表单是从HTML字符串资源中加载的,使用的是LoadLongResource辅助函数。这减少了构建页面所需的时间,因为字符串在扩展的第一次调用时就加载到内存中。如何在Visual Studio项目中使用HTML字符串资源,可以参考“ADO Data access from ISAPI”文章。

GET数据的接收非常简单,因为这种类型的数据是通过URL从浏览器发送到服务器的。POST数据变量对用户是透明的,可以传输大量数据。有关GET/POST数据的更多信息,请参阅“HTTP GET-POST utility”文章。

在MFC版本中,用于存储数据集合参数的C++类是Twin和TwinVector。ISAPIMFC版本的默认方法会将IDR_HTML_FORM资源写入客户端浏览器。FormRequest是点击“Submit Query”按钮后接收控制的方法。

void CPostDataExtension::FormRequest(CHttpServerContext* pCtxt, void* pVoid, DWORD dwBytes) { // 从服务器变量和POST数据构建STL集合 TwinVector vecServerCtx(pCtxt, (LPTSTR)pVoid); // 将服务器上下文变量写入浏览器流 WriteServerData(pCtxt); // 将服务器上下文变量和POST数据的STL集合写入浏览器流 WriteServerVar(pCtxt, vecServerCtx); }

从DLL入口点获取控制到FormRequest方法有点小问题。在MFC下,这很容易做到,使用宏:

DEFAULT_PARSE_COMMAND(FormRequest, CPostDataExtension) ON_PARSE_COMMAND(FormRequest, CPostDataExtension, ITS_RAW)

为了正确知道哪个方法将接收POST数据,MFC包装器必须从HTML表单中接收一个隐藏参数,该参数必须在FORM HTML标签之后的第一个位置:

在WriteServerVar辅助函数中,服务器上下文变量集合被写入HTTP流。可以直接获取所需参数的值:

bstrValue = vecServerCtx.Find(L"Filename"); for (itVec = vecServerCtx.begin(); itVec != vecServerCtx.end(); itVec++) *pCtxt << itVec->GetName() << " = " << itVec->GetValue() << "";

在同一个WriteServerVar辅助函数中,POST数据集合以这种方式写入HTTP流。

bstrToken = L"DATA"; index = vecServerCtx.Find(bstrToken); if (index > -1) { map = vecServerCtx[index].GetValueAsMap(); if (!map.empty()) // 有值 for (itMap = map.begin(); itMap != map.end(); itMap++) *pCtxt << (*itMap).first << " = " << (*itMap).second << ""; }

可以直接获取所需参数的值:

*pCtxt << "Filename = " << map[L"Filename"] << "";

TwinVector类提供了VARIANT GetVariant()方法和TwinVector(VARIANT varSafe)构造函数,以便在网络之间轻松传输集合。

非MFC版本基于MSDN关于ISAPI扩展中GET-POST数据的文章。C++扩展在DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pECB)方法中接收入口点。在这里,它启动CWriteFormExtension类的Run方法 - ISAPI扩展中只有一个运行对象。

用于在数据集合中保持参数的C++类是MultipartParser和MultipartEntry。继承Map STL类型的cParser变量接收整个POST数据集合。这是在Initialize方法中完成的。GetParam是一个辅助方法,返回所需参数的值。

String CWriteFormExtension::GetParam(MultipartParser& cParser, String sName) { String sValue; // 输出字符串 MultipartEntry* pEntry = cParser[sName.c_str()]; if (pEntry != NULL) // 有数据 { sValue = (LPCTSTR)pEntry->Data(); // 从集合中获取值 int nLen = sValue.size(); if (sValue[nLen - 1] == '\n' && sValue[nLen - 2] == '\r') sValue = sValue.substr(0, nLen - 2); } return sValue; }

这是Run方法的结果:

LoadLongResource私有函数与MFC版本相比略有修改。输入/输出str字符串参数是STL string类型。在szPath char变量中,必须放入DLL文件的名称,以便加载正确的资源库。

BOOL CWriteLayoutExtension::LoadLongResource(String& str, UINT nID) { HRSRC hRes; BOOL bResult = FALSE; CHAR szPath[MAX_PATH]; strcpy(szPath, "WriteForm.dll"); HINSTANCE hInst = LoadLibrary(szPath); // 如果想要标准的html类型 hRes = FindResource(hInst, MAKEINTRESOURCE(nID), RT_HTML); if (hRes == NULL) { // 跟踪错误 str = "Error: Resource could not be found\r\n"; } else { DWORD dwSize = SizeofResource(hInst, hRes); if (dwSize == 0) { str.empty(); bResult = TRUE; } else { HGLOBAL hGlob = LoadResource(hInst, hRes); if (hGlob != NULL) { LPVOID lpData = LockResource(hGlob); if (lpData != NULL) { str = (LPCTSTR)lpData; bResult = TRUE; } FreeResource(hGlob); } } } if (!bResult) str = "Error: Resource could not be load\r\n"; return bResult; }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485