Working the Web the Power of CFHTTP and CFCONTENT
One of the most useful and powerful tags in ColdFusion's arsenal is CFHTTP. CFHTTP enables developers to perform a host of functions and operations using the GET and POST methods of Hypertext Transfer Protocol (HTTP). Some of the uses for CFHTTP are the generation of static Hypertext Markup Language (HTML), the retrieval of files, the exchanging of information with other web servers, and the polling of other applications and web servers for content such as news feeds.
Another powerful tag is the CFCONTENT tag, which enables you to set the Multipurpose Internet Mail Extension (MIME) type of content returned from a ColdFusion template as well as download files from the server to the client. By default, ColdFusion returns a MIME content type of text/html so that a web browser renders your template text as a web page.
With these two tags, you have tremendous power to create rich and sophisticated web applications that make use of the Web's underlying protocol, HTTP.
The following sections cover uses of the CFHTTP tag.
Using CFHTTP to GET Information
Developers use CFHTTP to retrieve information from web sites. You use GET to retrieve files, including text and binary files, from a specified server. The retrieved data then is stored in a specific ColdFusion variable, cfhttp.filecontent. For example, let's say that we want to get a specific web site and display it in a browser. We could use the code in Listing 16.1.
Listing 16.1 getcnn.cfm
<cfhttp method="get" url="http://www.cnn.com" port="80" resolveurl="yes" proxyport="80" redirect="yes" useragent="mozillia/4.5 (WinNT; u)" timeout="180" throwonerror="yes"> </cfhttp> <cfoutput> #cfhttp.filecontent# </cfoutput>
If you test this template in your browser, you see the CNN.com web site. You can try changing the uniform resource locator (URL) variable to any other web site and see what happens.
CFHTTP has many attributes. For a list of them, check out Appendix A, "Tag Reference." In our code, we define the first attribute method, which has to be GET. We then define another required attribute, URL, which is the full URL to the site we want to retrieve. Next we define the port attribute to be 80 (the standard HTTP port for web servers). Now we have set an attribute called resolveurl, which, when used in conjunction with the port attribute, appends the port value to each resolved URL. This is useful and important for dealing with URLs in the requested document. They might be relative URLs, and by setting the resolveurl attribute to Yes, it guarantees the URL contained in the requested page resolves correctly.
Another useful attribute of the CFHTTP tag is the optional redirect attribute. This attribute enables you to gracefully deal with redirect. If you set it to Yes, it enables you to follow up to five redirects. After that, it treats the redirect as if the redirect attribute were set to No, which causes it to fail on the redirect.
We can also set the useragent attribute so that the web server with which we are interacting thinks that we are using a specific web browser. In our example, we set it to mozillia/6 (WinNT; u), which makes the web server think we were using Netscape 6. We then set the optional attribute timeout, which tells the ColdFusion Server to stop trying to resolve a URL after some specific amount of time in seconds. If you do not specify a time, CFHTTP defaults to the timeout value set in the ColdFusion Administrator page. If no timeout is set, ColdFusion indefinitely tries to resolve the URL, and because ColdFusion assigns a thread to each CFHTTP request, this can cause the server to hang. In practice, there is no reason not to set the timeout period, and it is best that you do.
Finally, we set a final optional attribute called throwonerror. This attribute, when set to Yes, lets us catch exceptions thrown by CFCATCH using a CFTRY/CFCATCH block. This is especially useful when you are testing and debugging tags that are requesting content from a variety of sites.
Another useful attribute that we can set in the CFHTTP tag is file. Using it, you can download various types of file types or directly save content from CFHTTP to a file. For example, if we want to save our CNN page to a file, we could use the code in Listing 16.2.
Listing 16.2 savecnn.cfm
<cfhttp method="get" url="http://www.cnn.com" port="80" proxyport="80" redirect="yes" useragent="mozillia/6 (WinNT; u)" timeout="180" throwonerror="yes" file="cnn2002029.htm" path="c:\internet\www\mysavedcontent\" > </cfhttp>
Notice that we used another attribute here. It's called path, which simply tells the tag where to put the file. In addition, when you retrieve a file, ColdFusion creates another variable that you can access. It's called cfhttp.mimetype.
Creating a Query Object
One of the more annoying things ColdFusion developers have to deal with is extracting data from text files. Often, this is because of some legacy system that stores its data in text files instead of in a database. Although there are many ways in ColdFusion to solve the problem of extracting data from a text file, CFHTTP gives us a powerful tool for dealing with data in text files. ColdFusion and CFHTTP enable you to build query objects from a delimited text file.
For example, let's say we have a simple text file, such as the one shown in Listing 16.3.
Listing 16.3 Order1
Product,Quantity ColdFusion MX,100, Jrun,2 DreamWeaver,5
We could create a query object out of the text, as shown in Listing 16.4.
Listing 16.4 creataquery.cfm
<cfhttp method="get" url="http://localhost:8100/cfhttp/order1.txt" name="testquery" delimiter="," textqualifier=" "> Product||Quantity</br> <cfoutput query="testquery"> #Product#, #Quantity#</br> </cfoutput>
Using CFHTTP to build queries is pretty straightforward as you can see. Much like the earlier example, we define the method as GET and the URL points directly to where our file resides. Then we use the name attribute to identify our query, just like we would in CFQUERY. Because our text file uses commas to delimit the information in it, we use the attribute delimiter and give the value delimiter=",". Finally, we set the attribute to " " because there is no text qualifier. If there is a text qualifier, you must surround all field values with the text qualifier character. To include a text qualifier character in a field, use a double character. For example, if the text qualifier is ", you should use "" to include a quotation mark in the field.
When you are working with your text files, you must remember that the first row of text is always interpreted as column headings; thus, that row is skipped. You can override the file's column heading names by specifying a different set of names in the columns attribute. You must specify a name for each column. You then use these new names in your ColdFusion Markup Language (CFML) code. In addition, if you have column headings with the same names, ColdFusion adds an underscore character to the duplicate column name to make it unique. For example, if two CustomerID columns are found, the second is renamed CustomerID_.
POST operations are the most common HTTP operations developers use to work with HTTP-based applications that expect an input and generate an output or response. CFHTTP also supports POST operations, as defined by HTTP 1.1. This standard enables ColdFusion developers to pass form variables, URL data, common gateway interface (CGI) variables, cookies, eXtensible Markup Language (XML), and other forms of information. For CFHTTP POST operations, you have to use a new tag called cfhttpparam for each variable you want to post.
Let's create a simple example that lets you see all the different post types at work. See Listing 16.5.
Listing 16.5 testpost.cfm
<cfhttp method="Post" url="http://localhost:8100/cfhttp/posttest.cfm"> <cfhttpparam type="Cookie" value="8798798798" name="somecookie"> <cfhttpparam type="CGI" value="97987987" name="cgivariable"> <cfhttpparam type="URL" value="897987987" name="someurl"> <cfhttpparam type="Formfield" value="Robi Sen" name="myname"> <cfhttpparam type="File" name="afile" file="e:\dev\BLara.jpg"> </cfhttp> <cfoutput> File Content:</br> #cfhttp.filecontent#</br> </br> Mime Type: #cfhttp.MimeType#</br> </cfoutput>
In this file, we are setting the CFHTTP method to POST as well as pointing the URL to a ColdFusion template we will create in a minute. Notice that we have created several CFHTTPARAM tag sets. CFHTTPARAM's general syntax is as follows:
<cfhttparam Name="variablename" Type="transactiontype" Value="valueofthevariable" File="filename">
This tag helps specify which type of POST you want to do using the type attribute, which supports five different options:
In the preceding example, we have used each CFHTTPPARAM type. Now we can create another template called posttest.cfm, as shown in Listing 16.6.
Listing 16.6 posttest.cfm
<cffile destination="e:\temp\" nameconflict="Overwrite" filefield="Form.afile" action="Upload" attributes="Normal"> <cfoutput> The URL variable is: #URL.someurl# <br> The Cookie variable is: #Cookie.somecookie# <br> The CGI variable is: #CGI.someurl#. <br> The Formfield variable is: #Form.myname#. <br> The file was uploaded to #File.ServerDirectory#\#File.ServerFile#. </cfoutput>
This template simply validates that you did indeed pass each of the five different variable types, along with associated data. To test it, call testpost.cfm in your browser to confirm, remembering to change the values of the file paths to those of your own machine.
As you can see, CFHTTP provides you with a simple yet powerful way to work with HTTP.
As noted in the beginning of the chapter, the CFCONTENT tag enables you to set the MIME type of content returned from a ColdFusion template as well as download files from the server to the client. CFCONTENT proves very useful when you are trying to provide content to a number of clients or sources, such as in the case of content syndication.
In general, syndication is the supply of material for reuse and integration with other material, often through some sort of paid service subscription. The most common example of syndication is in newspapers, where such content as news, comics, columns, and so on is usually syndicated content. Newspapers receive the content from the content providers, reformat it as required, integrate it with other copy, print it, and publish it.
Using CFCONTENT, you can do much the same thing with any data you want. For example, let's say that we want to syndicate our products catalog to associate vendors and our major clients. Few of them support XML, so we have opted to supply our catalog in HTML and plain American Standard Code for Information Interchange (ASCII) text, which our less-sophisticated partners can use to update their automated purchasing solutions against our catalog (see Listing 16.7).
Listing 16.7 catalogServer.cfm
<cfquery datasource="icf" name="createCatalog" dbtype="ODBC"> SELECT Category.CategoryID AS Category_CategoryID, Category.CategoryName, Category.CategoryDescription, Product.ProductID, Product.VendorID AS Product_VendorID, Product.ProductDate, Product.CategoryID AS Product_CategoryID, Product.ProductName, Product.ProductDescription, Product.ProductPricePerUnit, Product.ProductLicenseID, Vendor.VendorID AS Vendor_VendorID, Vendor.VendorName FROM Vendor INNER JOIN (Category INNER JOIN Product ON Category.CategoryID = Product.CategoryID) ON Vendor.VendorID = Product.VendorID; </cfquery> <cfsetting enablecfoutputonly="yes" showdebugoutput="no" /> <cfif isDefined("FORM.getcatalog") or isDefined("Url.getCatalog") or isDefined("CGI.getCatalog")> <cfif isDefined("FORM.getcatalog")> <cfif FORM.getcatalog Is "HTML"> <cfcontent type="text/html"> <cfsavecontent variable="tempHTML"> <table> <tr> <td>CategoryName</td> <td>ProductID</td> <td>VendorName</td> <td>ProductName</td> <td>ProductDescription</td> <td>ProductPricePerUnit</td> </tr> <cfoutput query="createCatalog"> <tr> <td>#CategoryName#</td> <td>#ProductID#</td> <td>#VendorName#</td> <td>#ProductName#</td> <td>#ProductDescription#</td> <td>#ProductPricePerUnit#</td> </tr> </cfoutput> </table> </cfsavecontent> <cfoutput>#tempHTML#</cfoutput> <cfelseif FORM.getcatalog IS "TEXT"> <cfcontent type="text/text"> <cfoutput query="createCatalog"> #CategoryName#,#ProductID#,#VendorName#,#ProductName#,#ProductDescription#, #ProductPricePerUnit# </cfoutput> <cfelse> Error-- You must pass either HTML,File, or Text </cfif> <cfoutput> #FORM.getcatalog# some response </cfoutput> <cfelseif isDefined("Url.getCatalog")> <cfif Url.getCatalog Is "HTML"> <cfcontent type="text/html"> <cfelseif Url.getCatalog IS "TEXT"> <cfcontent type="text/text"> <cfelse> Error-- You must pass either HTML,File, or Text </cfif> <cfelseif isDefined("CGI.getCatalog")> <cfif CGI.getCatalog Is "HTML"> <cfcontent type="text/html"> <cfelseif CGI.getCatalog IS "TEXT"> <cfcontent type="text/text"> <cfelse> Error-- You must pass either HTML,File, or Text </cfif> </cfif> <cfelse> <cfcontent type="text/HTML"> no message recived </cfif>
For the sake of the example, we create a file that holds our catalog in the first part of this template; but in reality, your catalog might be dynamically generated at the time of request, or more likely, frequently updated at specific intervals. Next in the file, we define a simple conditional logic exposing our catalog to requests from HTTP posts from forms, URLs, and CGI requests, thus providing the largest possible base of subscribership. Next we use the CFCONTENT tag to retrieve the catalog file for the requesting client and to ensure that the MIME type is text/html. Later in the code, we change that to text/plain, making sure that the requesting client does not resolve it as HTML.
You can test the catalogServer.cfm template with the template shown in Listing 16.8. It enables you to simulate various clients and post operations.
Listing 16.8 catalogClient.cfm
<cfhttp url="http://localhost:8100/cfhttp/catalogServer.cfm" method="POST" resolveurl="yes"> <cfhttpparam type="FORMFIELD" name="getCatalog" value="TEXT" /> </cfhttp> <cfoutput><pre>#CFHTTP.Header#</pre></cfoutput> <cfoutput>#CFHTTP.FileContent#</cfoutput>
You can also use CFCONTENT for creating more secure web applications. For example, let's say you have content that you want users to pay for, such as your artwork, comic strips, special reports, and so on. One thing people do to circumvent paying for these types of content is to guess filenames and URLs to access the content without first paying for it. To protect against this, you could put that content in a directory not usually accessible from your web server and use CFCONTENT to access it for customers who have paid. You can also use CFCONTENT to protect your site content/downloads from being leeched from another site without users passing through your site first.
CFHTTP and CFCONTENT are very powerful tags. CFHTTP, along with simple logic, enables you to create powerful web agents that can automate the process of filling out forms, interact with remote applications through HTTP, and create queries from delimited text.
CFCONTENT enables you to change MIME types, to force a MIME type to be specified with returned ColdFusion content, and to download files from the server to a client. This enables you to create applications where the same data can be viewed from a number of applications, from typical web browsers to PDAs.