Let's see how this comes together. Here's an example .srf file that's part of a simple online forum[2] 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:[3]
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,[4] 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. |