
Introduction
This article presents a way to retrieve the GET-POST data from a form in an ISAPI extension. Two helpful collections class of parameters
are also provided. Because a non MFC ISAPI extension is more reliable regarding the speed and the simultaneous number of connections, a non MFC version
is included.
Functionality
Both MFC and Non MFC versions use Vector, Map and String STL based classes, with non MFC code inside.
In the MFC version the string collection are based on _bstr_t type. In Non MFC
version the string collection are based on STL String type. So, the MFC version
is using the ISAPI MFC based macros to retrieve the server variables and parameters data while the
non MFC
version uses the
WriteClient and
ServerSupportFunction HTTP functions.
The default method writes to the browser a complex form with check boxes, edit boxes, radio buttons, text
area - even a
file type edit box. The idea is to receive all the POST parameters into the same DLL extension.
The form is loaded from a HTML string resource using the LoadLongResource helper function.
This reduces the time needed to build the
page because the string is loaded into memory on the first call of the extension. How to use the HTML string resources
in your Visual Studio project it is show in my ADO Data access from ISAPI article.
The GET data is received very easy since that type of data is sent from the browser to
the server using the URL. The POST data
variables are transparent to the user and it is possible to transfer large amount of
data. For more information about GET/POST
data see my HTTP GET-POST utility article.
MFC version
The C++ classes used to store parameters from the data collection are
Twin and
TwinVector. The Default method of the ISAPI MFC version writes to the client browser the
IDR_HTML_FORM resource.
FormRequest is the method that receives the control after you click on
the
"Submit Query" button.
void CPostDataExtension::FormRequest(CHttpServerContext* pCtxt,
void* pVoid, DWORD dwBytes)
{
TwinVector vecServerCtx(pCtxt, (LPTSTR)pVoid);
WriteServerData(pCtxt);
WriteServerVar(pCtxt, vecServerCtx);
}
A little problem is to get the control from the DLL entry
point to FormRequest method. Under the MFC, that is done very easy, using the
macros:
DEFAULT_PARSE_COMMAND(FormRequest, CPostDataExtension)
ON_PARSE_COMMAND(FormRequest, CPostDataExtension, ITS_RAW)
To correctly know what method will receive the POST data, the MFC wrapper must receive from
the HTML form one hidden parameter, which must be in the first
place after the FORM HTML tag:
<form action="PostData.dll?" method=post>
<input TYPE=hidden NAME="MfcISAPICommand" VALUE="FormRequest">
In the
WriteServerVar helper function, the server context variables collection is written on the HTTP stream.
It's possible to directly obtain the value of a needed parameter:
bstrValue = vecServerCtx.Find(L"Filename").
for (itVec = vecServerCtx.begin(); itVec != vecServerCtx.end(); itVec++)
*pCtxt << <itVec->GetName() << " = " <<<itVec-> GetValue() << "br";
In the same WriteServerVar helper function, the POST data collection is written
to the HTTP stream in this way.
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 << "br";
}
It's possible to directly obtain the value of a needed parameter:
*pCtxt << "Filename = " << map[L"Filename"] << "br".
The TwinVector class offer the VARIANT GetVariant() method and
TwinVector(VARIANT varSafe) constructor, to easy transport the collection over network between COM+ components.
Non MFC version
The non MFC version is based on the MSDN article regarding at GET-POST data
in ISAPI extensions.
The C++ extension receive the entry point in the DWORD WINAPI HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK pECB ) method.
Here it launches the Run method of the CWriteFormExtension class -
there isonly one running object in our ISAPI extension.
The C++ classes used to keep the parameters in the data collection are MultipartParser
and MultipartEntry. The cParser variable of
inherited Map STL type receive the entire collection of POST data. That is done in
Initialize method. The GetParam is a helper method who return the value of a needed parameter.
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;
}
This is the result of the Run method:

The
LoadLongResource private function is a little
modified compared with the MFC version. The input/output
str string
parameter is of STL string type. In the
szPath char variable we must put the name of
the DLL file in order to load the correct resource library.
BOOL CWriteLayoutExtension::LoadLongResource(String &str, UINT nID)
{
HRSRC hRes;
BOOL bResult = FALSE;
CHAR szPath[MAX_PATH];
strcpy(szPath, "WriteForm.dll");
HINSTANCE hInst = LoadLibrary(szPath);
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;
}
History
16 July 2002 - updated MFC.zip download (was missing a class)