|
|
|
Tech Docs 004
|
Primer |
I recently had the opportunity to mentor to a bunch of people who were not programmers, were not going to be programmers and weren't really very interested in programming. They were account managers. They needed to have an idea of the sheer power of the technology the in-house programmers were using so as to enable them to do feasibility and costing on client requests for technical project work. (We don't use Extreme Programming methodologies - bah!).
Not being one to piss about I thought they could write their own ISAPIs with a quick and dirty Paradox table hanging off the back. They had an hour each (I think) and all of em finished.
It's just taken me about 3 hours to find my notes, but here they are.
|
|
Objective |
We're gonna write an ISAPI which takes in someone's name and returns a predetermined string in a response page. The string will be derived from a pre-populated database. This isn't great but if you can nail this you can do just about anything. |
|
Step |
Description | | |
Okay, first we need some HTML, we are going to need a page that collects the name to be tested, and then we are going to need the page that is shown to that person afterwards. 2 pages - easy. We are going to use three bits of kit here, Microsoft FrontPage, CSE HTML Validator and Advanced HTML Optimiser | |
1 - Create the Default page, this will mostly be a text entry form to gather the persons name and submit it up to the server |
Open FrontPage and make sure you are creating a new blank html page
Insert->Form->One Line Text Box (this terminology denotes a menu track)
Delete the Reset button Right-Click on the text entry box Form File Properties NAME 40 Characters
Put 'Name:' before the box
Now, in the HTML view Delete out the grey FrontPage Agent lines Change action to be 'http://www.<url>.com/cgi-bin/namefind.dll/response
Now, save this page as 'Default.htm' in a place where you know where it is.
Notice on this url we have '/response' at the end - we have predefined a PathInfo we are going to use later, effectively daisy-chaining things together | | | | |
2 - Create the Response page, this html will have a dynamic entity, macro tag replacements, bits that the server can change as the page is issued. |
Open FrontPage and make sure you are creating a new blank html page
Go straight into the HTML view and between the <body> and </body> tags place the line :
<#NAME><#MESSAGE>
These are macro tags and will be replaced with 'dynamic' text by the dll.
Now, save this page as 'Response.htm' in a place where you know where it is. | | | | |
3 - Validate the HTML, yeah I know that this HTML is minimal in the extreme but it's good practice to use something to validate it so at least there's a chance that it's going to run on a bloody WinTV or some equally kooky device |
Run the CSE HTML Validator
File->Open, and highlight both the Default.htm and Response.htm, click on Open
Now, on each of the files click the validate button ('H' at the top)
Check and fix any errors and then save the changes
This Validator complains bitterly on the response page about the macro tags, thats only because it doesn't understand them. Don't change the <#NAME> and the <#MESSAGE> however hard it screams at you.
(No this isn't me wimping out of helping you with your HTML - I dont know what you typed, I know what you should have typed. With HTML, in my opinion, the biggest single screw up surrounds badly formed html, always make sure your html tags are perfectly nested!) | | | | |
4 - Optimise the HTML, The idea of this is to try and get the HTML down to the user as fast as possible we dont want any bloaty spaces in it really. Optimising the HTML mostly turns the HTML into one long line so that it can be streamed to the user at maximum efficiency, not so important in a small page - but a good idea.
CAVEAT: Make sure that your optimiser doesn't do any tag replacements, this has caused me grief with Macro tag names in the past. |
Run the Advanced HTML Optimiser
Files->Select Tree, and point it at the directory where Default.htm and Response.htm are.
Click the Okay button
Then go for Actions->Optimise and Okay
The Optimiser will then do it's thing. Remember, in the real world it's probably best to only optimise the html once it has been finalised and okayed and tested because it is next to unreadable afterwards.
Right. There is a big screw-up that can happen here. Mostly to get round it you need to make sure that there is no line in the HTML which is longer than about 500 characters, so in a text editor whizz down and put a newline at a convenient place at about the 500 character mark | | | | | |
So now you've got two pages of HTML, the data collection and the return page, yes their simplistic but you get the idea.
With the 500 character malarky, this is because Page Producers (used later) dont seem to be able to cope with immensely long single lines of HTML. You gotta keep your wits about you a bit because If you were putting a long bit of text in as a replacement you should wiether insert a '\n' in the text programmatically or at least make sure the macro tag is on a line by itself. |
|
Step |
Description | | |
So, the next thing we need is a database. There are hundreds and hundreds of different ways of storing data in a computer. Familiarity means that the easiest way I use for running a database is with a Paradox table and the Borland Database Engine. One of the reasons for this is that I have been using Paradox since version 2 (meet FRED) and it is so so simple to understand. | |
5 - Make the actual data structure that will hold the data |
Okay, run Borland's Database Desktop
File->New->Table, the table type should be set to Paradox 7, and click okay
You will now have the 'Create Paradox 7 Table (Untitled) box, this is where you define the column of the database. Define these columns:
KeyNo, which is type '+' (AutoInc), and has no size Name, which is type 'A' (AlphaNumeric) and has a size of 40 Message, which is type 'A' (AlphaNumeric) and has a size of 200 | | | | |
6 - Define a secondary index So that the database knows that it will be using the Name column for searching... so we can find someone's name, we are going to make it a secondary index..... a searchy column |
Drop down the 'Table Properties' list and select 'Secondary Indexes'.
Click on 'Define' and then, in the left hand box select 'Name' from the fields list, and then click on the arrow (->) to move it over to the 'Indexed Fields list'
Now, click on the 'Ok' button and it will ask you what name to keep this index as in a funny little popup box. Call the index 'SE-NAME'. | | | | |
7 - Right, this is certainly where I am going to wimp out a bit for the moment. I've got some notes on aliases and will put them up ASAP |
Now you need to save the database table you have just created so click on the 'Save As' button and enter a sensible file name, like 'names' or something. Then select the Alias where to save it. I will use the Alias 'NamesBase' for the moment.
Make sure the 'display table' option is ticked and then click 'Save' | | | | |
8 - Lets slap some data into the table so the ISAPI.dll can send back some messages |
You should now be looking at a blank table with the columns you have just defined labelled. At the top of database desktop is a toolbar with some speed buttons on, you need to click the one on the right, it has a tool-tip of 'Edit Data' (It says 'Edit Data' when you hover the cursor over it).
When you click this you will be given a blank line to play with.
The database will put the KeyNo in (probably numbered '1') and place the cursor in the 'Name' column. Type a name hare, one you will remember. Then press the right arrow to move over to the 'Response' column. Type in a message you want to give back if this name is typed in.
Do this for a couple of different names, so you've got a library to search through, use the down arrow to do the next line after you have filled in a response. | | | | | |
That was fun, huh. There are a couple of interesting things that we skated over in there, mostly to do with unique indexing and case sensitivity. Also we didnt make a Key field but this is more about ISAPI's than it is about databases.
Apologies about the 'Alias' flunk out - I'm working on an explanation page at the moment. |
| Groundwork Done | So that's cool! - We've got everything we need that the program is going to use, all ready and close to hand. Now we can jump in and do some cooool programming. With the aid of the Genius of Borland. K, trust me here - If you are using a Visual Compiler at the moment, put down all that 'my compiler is better than yours' baggage and find a copy of a Borland compiler to play with - you are allowed to use both you know (Microsoft owns a 10% stake in Borland I think). |
|
Step |
Description | | |
So this is great. We've got everything we need and we can jump in and do the deal. | |
9 - Get all set up to do some coding |
Okay, run Borland's C++ Builder Professional
File->New..., and you should have a box pop up of all the different types of Items that Borland can do - there's a lot of them huh.
Find the 'Web Server Application' Item and click on it, then in the popup choice box go for 'ISAPI/NSAPI DLL'. NSAPI's are slightly different than ISAPI's but it don't matter 'cos Borland have sorted all that out | | | | |
10 - Play with the VCL - If you cant find the bits I am talking about you may not have a high enough version of Borland C++ Builder - I suspect you mite at least need the Pro version |
You should have a whole load of toys on the component palette at the top of the page, find the 'Internet' Tab and you should have a page producer.
Drop a page producer into the WebModule and call that page producer 'PP_Default' in it's Name property in the object inspector.
Now we need the HTML for this page producer so find the Default.htm file, open it in notepad or such and copy the html from the file into the HTMLDoc property of PP_Default.
Sorry... A page producers job is exactly that, to produce a (web) page- all you need to do is to give it the HTML of the page you want it to produce. This can be done in two ways.
1). You can copy the raw HTML into the HTMLDoc property of the page producer - this is great because it is just so bloody quick at delivering the page from the dll, or
2). You can give the page producer a link to a .htm file that it will use. This second option is actually quite a bitch as the address has to be a drive address and not a URL address - ie, the page must be on the same server and it must be accessible by the dll - ouch. This is quite slow, and the dislocation of the HTML can lead to problems.
Aaaanyway, when you click on the HTMLDoc property in the Object Inspector you should get a little box popup - paste the HTML in there. | | | | |
11 - Set the ISAPI Default action. Remember the page you just pasted into the PP_Default page producer is the one that asks for someones name to be entered into t a text box - well that needs to be sent as the default action of the dll - here's how. |
On the left hand side of WebModule is an object or class tree, in here you should see 'Actions' and 'PP_Default'. You need to right-click on 'Actions' and 'Add Item'. Because of a small bug in the object inspector - although the 'WebActionItem1' you just created looks selected, it isnt really - so click on it and you will get the associated Properties and Events for the 'WebActionItem1' you have just created.
In the Properties for this Action (WebActionItem1) you need to set/change the following :
Default : True Name : default PathInfo : /default Producer : PP_Default
Lets go over those :
Default becomes true, we are setting this action to be the default action of the dll - if nothing else, this action is performed.
Name becomes default, getting confusing here with everything being called default - no, shouldn't be.. we are calling the default action of the dll 'default' much better than leaving it called 'WebActionItem1'.
PathInfo becomes /default, okay, because the default action is selected because no other actions are called we dont really need to set a PathInfo but I like to because then it means you can call it explicitly during testing if needed, Curiously we touched on a PathInfo in step 1.
Producer becomes PP_Default, because the action is simplistic - we just want it to deliver the default page, we can directly wire the page producer into the action.
That's it... If we were to compile that and put it on a capable server and run it - we could get a page that asked for a name - trouble is it wouldn't do much else - lets give it some functionality next. | | | | |
11 - We are going to do almost exactly the same thing again but with the results or second or return or name page. |
From the Internet tab on the component palette you need to drop another page producer onto the WebModule, the same as in step 10.
Set this new page producers Name property to be PP_Response and into it's HTMLDoc Property paste the contents of the Repsonse.htm you created in step 2. | | | | |
12 - We need a second action adding to the dll, this action is the one called by the html form action in step 1 |
So, on the left hand of the WebModule, right click on Actions again and 'Add Item'. You will get a new WebActionItem1, click on it and set it's properties :
Default : false Name : Response PathInfo : /response Producer : PP_Response
So, because this action hasn't got it's Default property set to true, the only way it can be called is by submitting a PathInfo that matches the PathInfo property, look back at step 1 and see how your first page of HTML calls this action using the action of the html form. EASY!! | | | | | |
So things are going well and we are nearly done with the getting and returning data but we've gotta leave building the HTML and the action for a minute to weave the database we created in steps 5,6,7 & 8. Remember that by this point you have created a dll that will issue a page asking for a name, receive that information and return a page with... well with nothing on at the moment because we haven't written the code that replaces the macro tag's you put in on step 2 with the information from the database |
|
Step |
Description | | |
Lets add the Paradox Data-table we created into the program now - this shouldn't be too hard. | |
13- (Unlucky for some) Drop in a Table Component, a DataSource Component, a Database Session component, wire em up and move on. This is as heavy as it gets. |
In the Component Palette at the top again go for the Data Access Tab, locate the 'Table' component, and while you are there, locate the 'Data Source' component. Drop one of each of these into the WebModule.
These two components are great friends and like to hold hands whenever possible.
This is how you can get them to do that.
First you gotta give them a sensible name each.
Set the Name property of the Table to be 'T_Name' then go to the DataSource properties and set it's name to be DS_Name, now, while you are in the DataSource properties set the DataSet properties to be T_Name which should be the only entry in the drop down list. The two components are now wired together.
Next we need to drop in a database session, this looks after cursor sharing in the database and a load of other malarky. So on the Data Access Component palette tab drop a session onto the WebModule.
In the Sessions Properties set these up
AutoSessionName: True Active: True
Then go back to the T_Name Table object and finally set these properties
Database Name : NamesBase for the moment TableName : names
(both of the above should be as used for the alias and the name of the table in step 7, in fact if you get the Database Name property correct then the names data-table will be in the drop down list for easy selection.)
IndexName : SE-Name (as set in step 6) | | | | | |
Really Really Really sorry about that bit - it's a bitch to start with. In brief you drop 2 components on to look after the database kinda directly and another which assists. They all gotta be wired together. Nail this and life's peachy. So with the database woven in we can get back to doing some proper 'type it in' coding..... |
|
Step |
Description | | |
So the database is in our program now, as is much of the structure. The only thing left is to replace those pesky macro tags in the response page, if you remember from step 2 the <#NAME> and the <#MESSAGE> that we want replacing with the name that has been entered from the first page, and the message, if any, lifted out of the database. sooooooo..... | |
14 - Tell the Macro tags in the response page what they should really be.
|
Okay, so really everything should be about rite in the whole dll apart from the macro tags in the response page need replacing. So in the WebModule you need to select the PP_Response component you placed in step 12, and in the object inspector go to it's events.
The PageProducer object (that you know as PP_Response (and PP_Default)), has only one event, and that event is 'fired' when the stream of html being issued by the PageProducer object encounters a macro tag, in our case it will 'fire' once for <#NAME> and again for <#MESSAGE>.
So, double-click in the white bit next to where it says 'OnHTMLTag' in the PP_Response Events in the Object Inspector and Borland will do some magic and present you with an empty function for you to work with.
void __fastcall TWebModule1::PageProducer1HTMLTag(TObject *Sender, TTag Tag, const AnsiString TagString, TStrings *TagParams, AnsiString &ReplaceText) {
}
niiiiiice.
This is mostly all stuff and nonsense apart from two bits the TagString and the ReplaceText. | | | | |
15 - TagString, so important it got it's own step |
TagString is an incoming indication of the macro tag that needs replacing, so the first time this function is called it will be NAME, and the second time it is called it will be MESSAGE, notice how the <# and the > have been stripped. | | | | |
16 - ReplaceText, as important as 15 |
ReplaceText is what you decide to put back into the html instead of the macro tag that has just been removed | | | |
17 - So, stuff all that, type this in (or cut and paste it -CHEEEEAAAAT), mostly whats happening is that there is an outside conditional checking for which macro tag is being asked for, if its the NAME then just return whatever was submitted in the form from the first page, if it's the second tag, open the database look for the name, if it's there return the message associated and if it cant be found send back 'You have no message'. Notice in this bit as well how the T_Name is activated and de-activated inside the DS_Name. |
if (TagString="NAME") ReplaceText=Request->ContentFields->Values["NAME"]; else if (TagString=="MESSAGE") { DS_Name->Enabled=true; T_Name->Enabled=true; T_Name->First();
T_Name->IndexName="SE-NAME"; T_Name->FindNearest(OPENARRAY(TVarRec,(Request->ContentFields->Values["EN"])));
if (T_Name->GotoKey()) ReplaceText=T_Name->FindField("MESSAGE")->Value; else ReplaceText="You have no message";
T_Name->Active=false; DS_Name->Active=false; } | | | | | |
Notice in the above code the use of 'ContentField'
If your form submits as POST use Request->ContentFields->Values["<var>"]; If your form submits as GET use Request->QueryFields->Values["<var>"];
where <var> is the name of the form element as denoted in the HTML |
|
Step |
Description |
| |
That's It !!! - You just gotta lump it on an
IIS server somewhere and run it... need a server ? - look here.... www.defined.net, they do ISAPI hosting and Server hosting and socket apps and dedicated hosting and all sorts ! |
|
.
|
|