An Example Request
Handler
Let's see how this comes together. Here's an
example .srf file that's part of a simple online
forum system, to provide a list of forums
available:
<html>
{{handler SimpleForums.dll/ForumList}}
<head>
<title>Forums</title>
</head>
<body>
<h1>ATL Server Simple Forums</h1>
<p>There are {{NumForums}} forums on this system.</p>
{{while MoreForums}}
<h2><a href="{{LinkToForum}}">{{ForumName}}</a></h2>
<p>{{ForumDescription}}</p>
<p><a href="{{LinkToEditForum}}">Edit Forum Settings</a></p>
<br />
{{NextForum}}
{{endwhile}}
</body>
</html>
This file uses not only the {{handler}}
directive, but also textual replacements and the {{while}}
loop.
So, we need a forum list handler. The handler
class looks like this:
class ForumListHandler :
public CRequestHandlerT<ForumListHandler> {
public:
ForumListHandler(void);
public:
virtual ~ForumListHandler(void);
public:
BEGIN_REPLACEMENT_METHOD_MAP(ForumListHandler)
REPLACEMENT_METHOD_ENTRY("NumForums", OnNumForums)
REPLACEMENT_METHOD_ENTRY("MoreForums", OnMoreForums)
REPLACEMENT_METHOD_ENTRY("NextForum", OnNextForum)
REPLACEMENT_METHOD_ENTRY("ForumName", OnForumName)
REPLACEMENT_METHOD_ENTRY("ForumDescription",
OnForumDescription)
REPLACEMENT_METHOD_ENTRY("LinkToForum", OnLinkToForum)
REPLACEMENT_METHOD_ENTRY("LinkToEditForum",
OnLinkToEditForum)
END_REPLACEMENT_METHOD_MAP()
HTTP_CODE ValidateAndExchange();
private:
HTTP_CODE OnNumForums( );
HTTP_CODE OnMoreForums( );
HTTP_CODE OnNextForum( );
HTTP_CODE OnForumName( );
HTTP_CODE OnLinkToForum( );
HTTP_CODE OnLinkToEditForum( );
HTTP_CODE OnForumDescription( );
private:
ForumList m_forums;
CComPtr< _Recordset > m_forumsRecordSet;
};
The action starts for
this class in the ValidateAndExchange method, which is
called at the start of processing after the m_HttpRequest
variable has been created.
#define AS_HR(ex) { \
HRESULT_hr = ex; if(FAILED(_hr)) { return HTTP_FAIL; } }
HTTP_CODE ForumListHandler::ValidateAndExchange() {
// Set the content-type
m_HttpResponse.SetContentType("text/html");
AS_HR( m_forums.Open( ) );
AS_HR( m_forums.ReadAllForums( &m_forumsRecordSet ) );
return HTTP_SUCCESS;
}
The return value, HTTP_CODE, is used to
signal what HTTP return code to send back to the client. If this
function returns HTTP_SUCCESS, the processing continues.
On the other hand, if something is wrong, you can return a
different value (such as HTTP_FAIL) to abort the
processing and send an HTTP failure code back to the browser.
The HTTP_CODE type is actually a
typedef for a DWORD, and it packs multiple data items into
those 32 bits (much like hrESULT does). The high 16 bits
contain the HTTP status code that should be returned. The lower 16
bits specify a code to tell IIS what to do with the rest of the
request. Take a look at MSDN for the set of predefined
HTTP_CODE macros.
In this example, we use the data layer object in
the m_forums variable to go out to our forums database and
read the list of forums. Assuming that this worked, we
store the list (an ADO recordset) as a member variable.
The replacement functions
come in two varieties: textual replacement and flow control. The
OnForumName method is an example of the former. When the
{{ForumName}} token is found in the SRF file, this code is
run:
HTTP_CODE ForumListHandler::OnForumName( ) {
CComBSTR name;
AS_HR( m_forums.GetCurrentForumName( m_forumsRecordSet,
&name ) );
m_HttpResponse << CW2A( name );
return HTTP_SUCCESS;
}
Here, the m_HttpResponse member is used
like a C++ stream class to output the name of the current forum.
The CW2A conversion class is used because our data layer
is returning Unicode, but the SRF file defaults to 8-bit
characters.
The flow-control tokens use the same replacement
map but work very differently. Within the replacement method, the
return value is the important thing:
HTTP_CODE ForumListHandler::OnMoreForums( ) {
VARIANT_BOOL endOfRecordSet;
AS_HR( m_forumsRecordSet->get_adoEOF( &endOfRecordSet ) );
if( endOfRecordSet == VARIANT_TRUE ) {
return HTTP_S_FALSE;
}
return HTTP_SUCCESS;
}
Here, we're checking to see if we have any more
records in our recordset. If so, we return HTTP_SUCCESS.
If not, we return HTTP_S_FALSE. Much like S_FALSE
is the "Succeeded, but false" hrESULT,
HTTP_S_FALSE signals the stencil processor that the
Boolean expression being evaluated is false, but the processing
completed. In this case, the false return value causes the
while loop to exit.
|