DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 19.14 Sending XML Data and Receiving a Response

19.14.1 Problem

You want to send XML data to a server-side script and load the response into the same Flash movie.

19.14.2 Solution

Use the XML.sendAndLoad( ) method.

19.14.3 Discussion

The sendAndLoad( ) method does just what its name suggests—it sends XML data to a server-side script and loads the response back into Flash. It is ideal when you want to send data to the server for processing and then receive the results back in the Flash movie. Additionally, since there are limitations in the XML.send( ) method, the sendAndLoad( ) method is the best choice even in situations in which you don't expect any response from the server.

The sendAndLoad( ) method sends the XML data from the object on which it is called. You must specify a target XML object into which the response is loaded. You can use the same XML object to load the data as you use to send it, but generally it is better to create a separate XML object for the loaded data, since the loaded response replaces any existing data in the target XML object. Therefore, in most cases, you should create two XML objects—one for sending and one for loading. For example:

// Create the loading object.
myLoadXML = new XML(  );

// As when loading data using the load(  ) method, set ignoreWhite to true.
myLoadXML.ignoreWhite = true;

// Define the onLoad(  ) method. (See Recipe 19.11.)
myLoadXML.onLoad = function (success) {
  if (success) {
    trace("The XML was successfully parsed. Here are the contents: ");
    trace(this);
  } else {
    trace("There was an error parsing the XML data");
  }
};

// Create the sending object.
mySendXML = new XML("<sendingData>this is some data</sendingData>");

// Set the content type and XML declaration. See the Recipe 19.14.3 for details.
mySendXML.contentType = "text/xml";
mySendXML.xmlDecl = "<?xml version=\"1.0\" ?>";

// Call the sendAndLoad(  ) method. This sends the data to a script and tells Flash to
// load the server response into myLoadXML.
mySendXML.sendAndLoad("cgi-bin/processXML.cgi", myLoadXML);

In the preceding example, you may notice that we also set the contentType and xmlDecl properties of the sending XML object. Though not always necessary, it is a good idea to set these properties before sending the data. Some scripts/languages require them, but defining them doesn't hurt even if they are not required. By default, the contentType property is set to "application/x-www-form-urlencoded", and the xmlDecl property has no defined value. In most cases you'll want to set contentType to "text/xml" and set xmlDecl to "<?xml version=\"1.0\" ?>".

Now let's take a look at a complete working example. In what follows, we'll first create the necessary client-side ActionScript code, and then you should choose from one of the server-side solutions. Choose the one that is supported by your server (or your personal computer if you use that as your server). There are server-side scripts in Perl, PHP, and ColdFusion.

The first thing you should do is create a Flash movie and add the PushButton component to the Library by dragging an instance from the Components panel to the Stage. Then add the following code on the first frame of the main timeline:

function init(  ) { 

  // Create an XML object to handle the response from the server.
  result_xml = new XML(  );
  result_xml.onLoad = function (  ) {
    if (this.firstChild.firstChild.nodeValue == "1") {
      _root.message_txt.text = "Saved successfully."
    } else {
      _root.message_txt.text = "Error.";
    }
  };

  // Create two text fields. One for messages to the user, one for the username.
  this.createTextField("message_txt", 1, 100, 100, 100, 20);
  this.createTextField("username_txt", 2, 100, 150, 100, 20);

  // Create a push button instance.
  this.attachMovie("FPushButtonSymbol", "saveScore_pb", 3, {_x: 100, _y: 180});

  message_txt.text = "Enter a username and click the button";
  message_txt.autoSize = true;
  username_txt.border = true;
  username_txt.type = "input";
  saveScore_pb.setClickHandler("saveScore");
  saveScore_pb.setLabel("Save");
}

// A function to generate a random score from 500 to 1000
function generateScore (  ) {
  return Math.round(Math.random(  ) * 500) + 500;
}

function saveScore (  ) {

  // Create a score.
  var score = generateScore(  );

  // Create the XML packet string.
  var xmlStr = "<gamescore><username>" + username_txt.text +
               "</username><score>" + score + "</score></gamescore>";

  // Create an XML object using the XML string.
  var score_xml = new XML(xmlStr);

  // Set the content type and XML declaration. Then send the XML data to the server
  // script. This example uses a URL for a ColdFusion page on the localhost. Use the
  // correct URL for your script. Also, when invoking the sendAndLoad(  ) method, tell
  // Flash to return the results to the result_xml object.
  score_xml.contentType = "text/xml";
  score_xml.xmlDecl = "<?xml version=\"1.0\" ?>";
  score_xml.sendAndLoad("http://localhost/gamescores.cfm", result_xml);

  // Tell the user that the score is being sent to the server.
  message_txt.text = "Score (" + score + ") sent!";
}

// Call the init(  ) function to get things started.
init(  );

Obviously, in a real-world example the user's score would be generated based on his performance in a game. In this example we just want to demonstrate sending and receiving XML, so we generate the score randomly.

The next step is to create the server-side script. First, here's the Perl option. If you use this option, place the following code in a text file named gamescores.cgi (or gamescores.pl) in a directory on your web server that has CGI access enabled (usually cgi or cgi-bin).

#!/usr/bin/perl

# Flash/Perl+CGI XML interaction demo
# Arun Bhalla (arun@groogroo.com)

use strict;
use XML::Simple;
use CGI;

my $ScoreFile = "scores.txt";

# Here we assume that this CGI script is receiving XML in text/xml form 
# via POST.  Because of this, the XML will appear to the script via STDIN.
my $input = XMLin(join('',<STDIN>)); 

# Write out the HTTP header
print CGI::header('text/xml');

# Try to open score file for writing, or return an error message.
open(SCORES, ">> $ScoreFile") || (printMessage(0) && 
                  die "Error opening $ScoreFile");

# Save the score in a pipe-delimited text file.
print SCORES join('|', $input->{username}, $input->{score}), "\n";

# Return the result in XML.
printMessage(1);

# Subroutine to output the result in XML.
sub printMessage {
  my $value = shift;
  my $message = {};
  $message->{success} = $value;
  print XMLout($message, keeproot => 1, rootname => 'result');
}

If you are using ColdFusion, a sample ColdFusion script is provided in the following code block. Place this code in a ColdFusion page named gamescores.cfm within a directory on your web server that can run ColdFusion pages.

<cfsilent>
<cfsetting enablecfoutputonly="Yes">
<cfset success = 0>
<cftry>
  <!--- XML packet sent by Flash MX. // --->
  <cfset scores_xml = XmlParse( getHTTPRequestData(  ).content ) >
 
  <!--- Parse out the XML packet sent from Flash MX. // --->
 
  <!--- Grab the username and score from the XML document and save as  
        local variables so they are easier to work with. // --->
  <cfset username = scores_xml.gamescore.username.XmlText >
  <cfset score = scores_xml.gamescore.score.XmlText >
 
  <!--- Append the latest score to our scores file. This could also be  
        stored in the database or another XML document. // --->
  <cffile action="APPEND" file="#ExpandPath( 'scores.txt' )#"
  output="#username#|#score#|#getHTTPRequestData(  ).content#" addnewline="Yes">
  <cfset success = 1 >
  <cfcatch type="Any">
    <cfset success = 0 >
    <cffile action="APPEND" file="#ExpandPath( 'attempts.txt' )#" output="ERROR" 
    addnewline="Yes">
  </cfcatch>
</cftry>
</cfsilent>
<cfcontent type="text/xml">
<cfoutput><?xml version="1.0" ?><success>#success#</success></cfoutput>
<cfsetting showdebugoutput="no" enablecfoutputonly="No">

If you are using PHP on your server, a sample PHP script follows. Place this code in a PHP page named gamescores.php on your web server in a directory that allows PHP access.

<?php

// Read In XML from Raw Post Data.
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];

// Process XML using DOM PHP extension.
$document = xmldoc($xml);

// Read root element <gameinfo>.
$rootElement = $document->root(  );

// Read child nodes <username> and <score>.
$childNodes = $rootElement->children(  );

$data = "";

// Loop through child nodes and place in array.
foreach($childNodes as $childNode){
  // Add data to array;
  $name = $childNode->tagName(  );
  $value = $childNode->get_content(  );
  $data[$name] = $value;
}

// Append data to scores.txt ( format: username|score )
$fp = fopen("scores.txt","a+");
$dataString = $data['username'] . "|" . $data['score'] . "\n";
fputs($fp,$dataString,strlen($dataString));
fclose($fp);

// Return success code to Flash
echo "<success>1</success>";
?>

Once you have the Flash movie and the server-side script in place, you need only to run the movie and click on the button to test it. The movie should get a successful response, and if you check in the directory on the server in which the script has been created, you should find a scores.txt file containing the data you just entered via the Flash movie.

    [ Team LiB ] Previous Section Next Section