在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;
}