|
An ATL Server Web application is the ATL Server equivalent of an ASP.NET application. In ASP.NET applications, .aspx files contain HTML that uses custom tags to execute methods in page handler classes. In ATL Server Web applications, SRF files contain fixed HTML and text, and they use SRF tags to execute methods on handler classes. In both cases, the handler code is supplied in DLLs.
You can write Web applications using pure C++ code, but two attributes have been supplied that make it easier to write handler classes.
The request_handler attribute marks a class as implementing request handler methods and takes a parameter that sets the alias that SRF files will use to identify the handler class:
[request_handler("MyHandler")] classCMyhandler { ... };
If the preceding class was compiled into Handlers.dll, it would be referred to in an SRF file like this:
{{//Thehandlertagforthefile}} {{handlerHandlers.dll/MyHandler}}
Using the request_handler attribute will normally result in the addition of the ATL Server class CRequestHandlerT as a base class and in the addition of a DECLARE_REQUEST_HANDLER macro that exposes the class to IIS.
Note | I use the word normally because CRequestHandlerT will not be added as a base class if the attributed class already explicitly implements the IRequestHandler interface. |
The CRequestHandlerT class has several useful members that you might end up using in your handler code. These members are summarized in Table 7-13.
Member | Description |
---|---|
CheckValidRequest | Override this method if you need to check the validity of the request. |
ClearResponse | Clears the response object of any headers and buffered data. |
FormFlags | Returns one or more of the ATL Server form flags, which specifies how forms are processed . See below for an explanation of form flags. |
HandleRequest | You can override this method to provide your own handling for the HTTP request. For the majority of cases, the default implementation is sufficient. |
InitializeChild | Called to initialize the handler when an instance is created during processing a subhandler or include tag. This method initializes the request and response objects, and calls ValidateAndExchange . |
InitializeHandler | Called to initialize the handler when an instance is created during processing a handler tag. This method initializes the response object, calls CheckValidRequest , initializes the request object, and finally calls ValidateAndExchange . |
MaxFormSize | Returns the maximum size of form that will be accepted. You can override this function if the default value of 48 KB is not acceptable. |
ServerTransferRequest | Transfers the request to another handler. |
ValidateAndExchange | Override this method to initialize the request handler. |
m_dwRequestType | Data member holding the type of the request. The value of this member can be ATLSRV_REQUEST_STENCIL , showing that an SRF file was requested ; or ATLSRV_REQUEST_DLL , showing that a handler DLL was requested; or ATLSRV_REQUEST_UNKNOWN . |
m_HttpRequest | Returns a CHttpRequest object representing the current request. |
m_HttpResponse | Returns a CHttpResponse object used to write the response to the client. |
m_pRequestInfo | Returns a pointer to an AtlServerRequest object that holds information about the ATL Server request handling infrastructure. This data will not be used by the majority of users. |
The FormFlags method is used to return a value that defines how forms will be processed. The following flag values are supported:
ATL_FORM_FLAG_NONE (numerical value 0), which specifies that forms are processed and files will be created
ATL_FORM_FLAG_IGNORE_FILES (numerical value 1), which specifies that attempts to upload files will be ignored
ATL_FORM_FLAG_REFUSE_FILES (numerical value 2), which specifies that attempts to upload files will be treated as an error
ATL_FORM_FLAG_IGNORE_EMPTY_FILES (numerical value 4), which specifies that files of size zero bytes will be ignored
ATL_FORM_FLAG_IGNORE_EMPTY_FIELDS (numerical value 8), which specifies that fields with no content will be ignored
The default implementation provided by CRequestHandlerT returns ATL_FORM_FLAG_IGNORE_FILES . You can override this method to define other behavior, ORing together values as required.
The tag_name attribute establishes the tag name that will be used to call a handler method from within SRF files. Heres an example of a handler function that doesnt take any parameters:
[tag_name(name="DoThis")] HTTP_CODESomeFunction() { //IfallisOK... returnHTTP_SUCCESS; }
Handler methods are simply C++ class member functions. These functions can take arguments, but any arguments passed in will need to be converted because HTTP request arguments are always passed as strings. There are three situations to be considered :
The handler takes no arguments.
The handler takes one argument that can be parsed automatically.
The handler takes more than one argument, the argument is of a type that cannot be parsed automatically, or both.
The first case is trivial because there are no arguments to convert. In the second case, if the method takes a single argument that is a bool , an integer type ( char , short , int , or __int64 , and their unsigned equivalents), or a floating point type ( float or double ), then the conversion from a string will be done automatically. Such methods should be declared to take a pointer of the requisite type, for example,
//Ahandlerfunctionthattakesaboolean [tag_name(name="PassABool")] HTTP_CODEBoolHandler(bool*pArg) { //IfallisOK... returnHTTP_SUCCESS; }
This function could be invoked from an SRF file like this:
{{PassABool(true)}}
If the first four characters of the parameter passed in evaluate to true with the comparison being case-insensitivea Boolean value of true will be passed to the handler function. If the parameter cannot be converted for some reason, the conversion process will pass an HTTP error back to the client.
Note | Parameters used with replacement tags cannot contain a closing parenthesis character). Be careful not to choose a character encoding that results in one of the bytes having the same value as an ANSI closing parenthesis (decimal 41, hex 29). |
If the handler method is going to take a single parameter of a type that isnt automatically parsed, or its going to take more than one parameter of any type, youll need to write a parsing function to decode the input data and convert it to a form that can be used by the handler method. The prototype for a parsing function looks like this:
HTTP_CODEParsingFunction(IAtlMemMgr*pMemoryManager, LPCSTRszArgumentData, parameterType **ppArgument);
The first parameter is a pointer to a memory manager object that you use to allocate memory for the converted argument data. The second parameter is the string containing the data from the HTTP request, and the third parameter is a pointer of the appropriate type, through which the function passes the converted value to the handler function.
You link a parsing function to a handler by using the optional second parameter to the tag_name attribute:
[tag_name(name="DoThis",parse_func="ParsingFunction")] HTTP_CODESomeFunction(bool*pb) { //IfallisOK... returnHTTP_SUCCESS; }
The parse_func parameter specifies the name of the parsing function, and this function will automatically be called to parse the input data whenever the handler is invoked. If parse_func is used, the handler method must take an argument of the same type that will be emitted by the parsing function.
The following example shows how to write a simple ATL Server Web application, which has the following characteristics:
It has two handler classes, one of which is used as a subhandler.
It implements handler methods that do not require custom parsing.
It implements handler methods that do require custom parsing functions.
Note | To create and test Web applications, you must have access to a Web server, and have permission to install ISAPI extensions. |
Bring up the New Project dialog box, and in the Visual C++ project types folder select ATL Server Project , choosing an appropriate location and name, as shown in Figure 7-2.
You can see in the ATL Server Project Wizard dialog box shown in Figure 7-3, that there are a number of project settings that can be used to customize the code and the way it is deployed.
The default settings will generate attributed code and will automatically deploy the project to a virtual root on the Web server with the same name as the project. The Project Settings page, shown in Figure 7-4, lets you choose how many DLLs are generated. Remember that an ATL Server application consists of one ISAPI extension DLL and one or more ATL Server handler DLLs. You can choose to build these as two separate DLLs, or to merge them into one. You might want to merge them if you have only one Web application DLL.
When you press Finish to exit from the wizard, youll find that two projects have been generated for you, one for each of the ISAPI and Web application DLLs. The interesting code is in the AtlWebApp.h file, which contains the definition of the handler class and its methods:
[request_handler("Default")] classCAtlWebAppHandler { private: //Putprivatemembershere protected: //Putprotectedmembershere public: //Putpublicmembershere HTTP_CODEValidateAndExchange() { //TODO:Putallinitializationandvalidationcodehere //Setthecontent-type m_HttpResponse.SetContentType("text/html"); returnHTTP_SUCCESS; } protected: //Hereisanexampleofhowtouseareplacement //tagwiththestencilprocessor. [tag_name(name="Hello")] HTTP_CODEOnHello(void) { m_HttpResponse<< "HelloWorld!"; returnHTTP_SUCCESS; } };//classCAtlWebAppHandler
The handler class is decorated with the request_handler attribute and given a name of "Default" . This is the name given to the default handler class within a DLL, and it enables writers of SRF files to specify the default handler in a DLL without having to know a specific handler name. The class defines the ValidateAndExchange method, which simply sets the response type to HTML. It also defines a simple replacement tag method named "Hello" , which echoes a string to the HTTP response stream. Replacement tag method names often start with On , although this is only a convention, and not a requirement.
The project also contains a sample SRF file, which uses the test Hello tag:
{{//useMSDN's "ATLServerResponseFileReference" to learnaboutSRFfiles.}} {{handlerAtlWebApp.dll/Default}} Thisisatest:{{Hello}}
You should test the Web application at this point by building the project. When the build has finished, you should find that a new directory has been created under the Web servers wwwroot directory, and that this new directory contains three files: the two DLLs and the sample SRF file.
Now open a browser window and navigate to the URL of the SRF file. You should see the output displayed in Figure 7-5.
The following method shows how to implement a handler function that takes a single argument of a type that can be automatically parsed:
//Amethodthatusesbuilt-inparsing [tag_name(name="Square")] HTTP_CODEOnSquare(short*pVal) { longresult=(*pVal)*(*pVal); m_HttpResponse<< "Squareof " <<(*pVal)<< " is " <<result; returnHTTP_SUCCESS; }
The method takes an int* as an argument; because int is one of the types that can be automatically parsed, the string parameter passed in with the HTTP request will be converted before the method is called. You can test this method by editing the test SRF file as follows :
Testingthesquaretag:{{Square(3)}}
Note | You dont enclose arguments in quotesconversion to strings is automatic. |
If you experiment, youll find that the Square method isnt very satisfactory. It uses the atoi C Runtime Library function to convert the argument, and one of the properties of this function is that it returns zero if it cant perform the conversion. This means that if you give an invalid argument to the Square tag such as {{Square(Tuesday)}} the OnSquare method will be called with an argument of zero. This means that if zero is a valid input value, you cant tell when an invalid argument has been used.
To get around this problem, you can implement a custom parsing function instead of relying on the built-in parsing. The following pair of functions show how to do this:
//Parsingfunctiontoparseoneshortvalue HTTP_CODEParseSquareData(IAtlMemMgr*pMemMgr,LPCSTRszParams, short**ppDest) { //Ifanyoftheparametersarenull,fail if(pMemMgr==0szParams==0ppDest==0) { returnHTTP_FAIL; } //Verysimplemindedcheck... for(inti=0;i<strlen(szParams);i++) { boolbOK=false; switch(szParams[i]) { case'-': if(i==0)bOK=true; elsebOK=false; break; case'+': if(i==0)bOK=true; elsebOK=false; break; case'0':case'1':case'2':case'3': case'4':case'5':case'6':case'7': case'8':case'9': bOK=true; break; default: bOK=false; break; } if(bOK==false) { //Causes500-servererror returnAtlsHttpError(500,ISE_SUBERR_STENCIL_PARSE_FAIL); } } //Allocatememoryforashort *ppDest=(short*)pMemMgr->Allocate(sizeof(short)); //ConvertusingatoibecauseweknowtheparameterisOK **ppDest=atoi(szParams); returnHTTP_SUCCESS; } //Amethodthatusescustomparsing [tag_name(name="PSquare",parse_func="ParseSquareData")] HTTP_CODEOnSquare2(short*pVal) { longresult=(*pVal)*(*pVal); m_HttpResponse<< "(P)Squareof " <<(*pVal)<< " is " <<result; returnHTTP_SUCCESS; }
The ParseSquareData function takes the three parameters that all parsing functions must take: a pointer to a memory manager object, a string containing the data from the HTTP request, and a pointer to receive the converted value. If any of those values is null, the function cannot proceed and a fail status code is returned.
The input string is checked to see that it contains only digits, with an optional leading plus or minus sign. If any other characters are found, the function returns an error status code. The AtlsHttpError function builds an HTTP status code out of major and minor parts ; in this case, the minor status code shows that parsing a stencil has failed.
If the input string passes the check, enough memory is allocated to hold a short . The atoi function can be used to convert the string because we know that now a return value of zero represents a real value, rather than an error. The parsing function is then associated with the handler method using the parse_func parameter on the tag_name attribute.
Note | You must allocate memory for the converted argument using the IAtlMemMgr pointer because the memory manager will take care of deallocating the memory after use. |
If you edit the SRF to invoke the PSquare tag and pass an invalid argument, you should see a Server error page displayed in the browser.
|