Sending Content to the Browser

When a FoxWeb script is executed, any text not enclosed within script delimiters is returned to the browser. FoxWeb code within script delimiters can send content to the browser by using the Response object.

Sending Content with FoxWeb Code

To send content to the browser from within a code block, use the Response.Write method. The following code is a variation of the code we saw in the Writing FoxWeb Scripts topic:

Multiplication Table (multiples of 7)<P>
<%FOR i = 1 TO 9
    Response.Write(STR(i) + " times 7 = " + STR(i*7) + "<br>")
NEXT%>

There is absolutely no difference between using the Response.Write method, and the equal sign to send content to the browser. The following two commands are functionally identical:

<% Response.Write(DATETIME()) %>

<%= DATETIME() %>

You should utilize FoxWeb expressions, code blocks and HTML blocks in a way that creates the most legible code.

Buffering Content

By default, FoxWeb completes execution of a script before sending any content to the browser. This process is known as buffering. You can use the Response.Buffer property to disable buffering for the currently executing script, so that the Web server returns content as it processes the script.

The advantage of buffering your FoxWeb scripts is that any time during execution of the script you can abort sending the content that has already been processed and send something else instead. You can use the Response.Clear method to clear the response buffer.

Another advantage of buffering is that it allows you to change the HTTP header at any time during execution of your script. This is important because certain properties and methods, such as Response.SetCookie, Response.Expires, Response.Redirect, etc., modify the HTTP header. HTTP headers must be sent to the browser before the actual content, so they can not be changed after content has already been sent.

To illustrate the effects of Buffering try running the following script:

<HTML>
<BODY>
<% FOR i = 1 TO 5
    WAIT "" TIMEOUT 1
    Response.Write(STR(i) + "<br>")
NEXT %>

</BODY>
</HTML>

The above script executes a loop 5 times, and with each iteration it sends the iteration number after sitting idle for a second. If buffering is turned on all you will see is the final output 5 seconds after you send your request. If on the other hand buffering is turned off, your browser will display the number of each iteration as it happens. To see that add the command Response.Buffer = .F. right before the FOR/NEXT loop:

<HTML>
<BODY>
<% Response.Buffer = .F.
FOR i = 1 TO 5
    WAIT "" TIMEOUT 1
    Response.Write(STR(i) + "<br>")
NEXT %>

</BODY>
</HTML>

Even if buffering is turned on, you can use the Response.Flush method to send what has already accumulated in the buffer to the browser.

A potential disadvantage of buffering is that the user does not see any of the server's response until the script has finished executing. For scripts that take a long time to execute, users could experience long wait times before seeing the page. Even though the total time that it takes for the script to finish executing is roughly the same (actually the script executes slightly faster if buffering is enabled), the perceived speed of execution can be faster if buffering is turned off.

Buffering is turned on by default for FoxWeb scripts. You can use the FoxWeb Control Center to turn off buffering for the entire server.

Redirecting the Browser to Another Page

Instead of sending content to a browser, you can redirect it to another URL with the Response.Redirect method. The following script checks if the user has used Secure Sockets Layer (SSL) to connect to the server and, if not, it forces the browser to re-connect with SSL. The script can determine whether the browser has used SSL by checking the port to which the browser has connected. By default regular HTTP uses port 80, while SSL utilizes port 443.

<% IF Request.ServerVariables("SERVER_PORT") != "443"
    Response.Redirect("https://www.MyServer.com/login.fwx")
ENDIF %>

Note that the Redirect method instructs the browser to connect to a different URL, by adding a command in the HTTP header. For this reason it must be executed before any output is sent to the browser. Place the Redirect command at the top of your script before any HTML code, to ensure that no output has been sent yet. Alternatively, keep the Response.Buffer property enabled, as explained in the Buffering Content section of this topic.

Returning Non-HTML Content

Servers do not necessarily have to send HTML content to the browser. Instead they can send other types of content, such as spreadsheets, Adobe Acrobat files, plain text, graphics, etc.. The browser determines how to process the incoming content, based on the value of the Content-Type header. This header enables the browser to determine whether it can display the file itself or whether it has to call another application. For example, if the Web server returns a Adobe Acrobat (PDF) file, the browser must be able to start the Acrobat Reader to display the page. The Web server recognizes file types by mapping the file name extension to a list of MIME (Multipurpose Internet Mail Extensions) types. For example, to start Microsoft Word, the browser needs to recognize the application/msword MIME type.

The MIME type "application/unknown" is a special case, in that it has the effect of causing browsers to display the "Save As/Open" dialog for files that they can normally display inline.

FoxWeb scripts can set the MIME type of the returned content, by setting the Response.ContentType property. The following example retrieves the name of the file to be downloaded from the Query String and, based on the extension, it sets the MIME type and sends the file to the browser:

<%
Response.Buffer = .T.
FileName = Request.QueryString("FileName")
FileExtension = UPPER(JustExt(FileName))
* Verify that the file exists and that it is one of the authorized types
DO CASE
CASE EMPTY(FileName)
    DO ReturnErr WITH "You must specify a file in the query string"
CASE NOT FILE(FileName)
    DO ReturnErr WITH "Can not find file " + FileName
CASE INLIST(FileExtension, "XLS", "CSV")
    ContentType = "application/vnd.ms-excel"
CASE FileExtension == "GIF"
    ContentType = "image/gif"
CASE INLIST(FileExtension, "JPG", "JPEG")
    ContentType = "image/jpeg"
OTHERWISE
    DO ReturnErr WITH "Not authorized to download files of this type"
ENDCASE
* Read contents of file into string
FileContent = FILETOSTR(FileName)
* Specify file name in case the user decides to save the file
Response.AddHeader("Content-Disposition", "inline; filename=" + FileName)
* Specify size so that the browser's progress bar works properly
Response.AddHeader("Content-Length", LTRIM(STR(LEN(FileContent))))
Response.ContentType = ContentType
* Send file to browser
Response.Write(FileContent)
ENDPROC


PROCEDURE ReturnErr
PARAMETERS ErrMsg
Response.Clear
%>

<HTML><BODY><%= ErrMsg %></BODY></HTML>
<%
Response.End
ENDPROC
%>

The above example illustrates the use of the ContentType property and also introduces the Request.AddHeader property, which can be used to add any HTTP header to the response, returned by FoxWeb. In this case we are adding two headers: Content-Disposition informs the browser what file name to use if the user decides to save the content to a file. The Content-Length header informs the browser what the size of the content being downloaded is, which can be used to display the progress bar properly.

Note: Not all browsers recognize the above HTTP headers. In general, newer browsers are better than older ones, and tend to do the right thing in most cases. You should experiment with different headers, based on the MIME type and the desired effect.

Even though this particular example retrieves content from a file, you can also get content from other sources, such as a memo field in a table. You could, for example, have a library of images, saved in a FoxPro table and write an application that presents a search page to the user, and then returns the desired image in the output of a script. You can use VFP's FILETOSTR command to populate memo fields with the contents of a file. Note that if your memo fields will contain binary data, such as images, you should use the NOCPTRANS clause when defining the table. For more information on FILETOSTR and NOCPTRANS consult the VFP documentation.

The FoxWeb sample script download.fwx, illustrates the concepts discussed in this section.

Following is a list of some common content types:

image/gif GIF images
image/jpeg JPEG images
text/xml XML documents
application/vnd.ms-excel Microsoft Excel worksheet
application/msword Microsoft Word document
text/plain Content returned as text instead of interpreted HTML statements
application/unknown Browser presents "Save as/Open" dialog instead of trying to display the file.

Preventing Browsers from Caching Content

To prevent a browser from caching the output of your scripts, use the Response.Expires or Response.ExpiresAbsolute properties, which set the Expires HTTP header. The Expires property is numeric, and is interpreted as the number of minutes from now, after which the page will need to be reloaded. The ExpiresAbsolute property is of type DateTime, and is the actual time that the page expires:

<% Response.Expires = 0 %>

A value of 0 forces cached pages to expire immediately. Since HTTP headers must be sent to the browser before any HTML content is sent, either put the Response.Expires property before any HTML tags or buffer the page.

Note that each browser version has its own rules for determining whether to cache content and how to deal with expired pages. Some browsers will not even let you use the back button to return to a page that has already expired, while some other older browsers will ignore the Expires header altogether.


© Aegis Group