Recipe 20.13.
Sending XML
Problem
You want
to send XML data to a server-side script.
Solution
Create a URLRequest instance containing
the XML data to send. Use flash.net.sendToURL( ) to send
the data and ignore the server response, use flash.net.navigateToURL( ) to
send the data and open the server response in a specific browser
window, or use URLLoader.load( ) to both send
the data and download the response into the .swf file.
Discussion
XML is normally used to transfer data to and
from applications, and in this case, Flash movies. Therefore, it is
quite unusual that you would want to create XML objects in your
Flash movies for use within Flash alone. Instead, you generally
load XML data from another source, create XML data in Flash for the
purpose of sending to another application, or both.
This recipe examines sending XML data from Flash
to another application, and there are lots of reasons to do this.
For example, in a Flash game you might want to use XML to send the
user's name and score to the server. At other times, you might want
to send XML packets to a server-side script to invoke server-side
functionality. This is a process that is sometimes called a
remote procedure call (RPC),
and it can use XML to send the function invocation information
(function name, parameters, etc.) to the server. There is a formal
specification for using XML in this manner called XML-RPC (see http://www.xmlrpc.com). So, as you can
see, the possibilities for sending XML to the server are quite
diverse.
As discussed in
Recipe 20.11 and Chapter
19, ActionScript 3.0 consolidates sending and loading data into
the methods of the flash.net package. Previously, the
XML class contained both send( ) and sendAndLoad(
) methods to send XML to a server, but in ActionScript 3.0 you
must use URLRequest instead. Recipes
19.6 and 19.7
cover the basic techniques using a URLReuest instance to
send and receive data.
Let's take a look at a complete working example.
In what follows, you'll first create the necessary client-side
ActionScript code, and then you should choose from one of the
server-side solutions. Choose one that's 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 new
ActionScript 3.0 class with the following code:
package {
import flash.display.*;
import flash.text.*;
import flash.filters.*;
import flash.events.*;
import flash.net.*;
public class XMLSendLoadExample extends Sprite {
private var _message:TextField;
private var _username:TextField;
private var _save:SimpleButton;
public function XMLSendLoadExample( ) {
initializeDispaly( );
}
private function initializeDispaly( ):void {
_message = new TextField( );
_message.autoSize = TextFieldAutoSize.LEFT;
_message.x = 10;
_message.y = 10;
_message.text = "Enter a user name";
_username = new TextField( );
_username.width = 100;
_username.height = 18;
_username.x = 10;
_username.y = 30;
_username.type = TextFieldType.INPUT;
_username.border = true;
_username.background = true;
_save = new SimpleButton( );
_save.upState = createSaveButtonState( 0xFFCC33 );
_save.overState = createSaveButtonState( 0xFFFFFF );
_save.downState = createSaveButtonState( 0xCCCCCC );
_save.hitTestState = save.upState;
_save.x = 10;
_save.y = 50;
// When the save button is clicked, call the handleSave method
_save.addEventListener( MouseEvent.CLICK, handleSave );
addChild( _message );
addChild( _username );
addChild( _save );
}
// Creates a button state with a specific background color
private function createSaveButtonState( color:uint ):Sprite {
var state:Sprite = new Sprite( );
var label:TextField = new TextField( );
label.text = "Save";
label.x = 2;
label.height = 18;
label.width = 30;
var background:Shape = new Shape( );
background.graphics.beginFill( color );
background.graphics.lineStyle( 1, 0x000000 );
background.graphics.drawRoundRect( 0, 0, 32, 18, 9 );
background.filters = [ new DropShadowFilter( 1 ) ];
state.addChild( background );
state.addChild( label );
return state;
}
private function handleSave( event:MouseEvent ):void {
// Generate a random score to save with the username
var score:int = Math.floor( Math.random( ) * 10 );
// Create a new XML instance containing the data to be saved
var dataToSave:XML = <gamescore>
<username>{username.text}</username>
<score>{score}</score>
</gamescore>;
// Point the request to the script that will handle the XML
var request:URLRequest = new URLRequest( "/gamescores.cfm" );
// Set the data property to the dataToSave XML instance to send the XML
// data to the server
request.data = dataToSave;
// Set the contentType to signal XML data being sent
request.contentType = "text/xml";
// Use the post method to send the data
request.method = URLRequestMethod.POST;
// Create a URLLoader to handle sending and loading of the XML data
var loader:URLLoader = new URLLoader( );
// When the server response is finished downloading, invoke handleResponse
loader.addEventListener( Event.COMPLETE, handleResponse );
// Finally, send off the XML data to the URL
loader.load( request );
}
private function handleResponse( event:Event ):void {
try {
// Attempt to convert the server's response into XML
var success:XML = new XML( event.target.data );
// Inspect the value of the success element node
if ( success.toString( ) == "1" ) {
_message.text = "Saved successfully.";
} else {
_message.text = "Error encountered while saving.";
}
} catch ( e:TypeError ) {
// Display an error message since the server response was not understood
_message.text = "Could not parse XML response from server.";
}
}
}
}
The contentType property of a
URLRequest instance is set to
application/x-www-form-urlencoded by default, so it's
important to set contentType to text/xml whenever
you want to send XML data. Additionally, you'll want to set the
method of the request to URLRequestMethod.POST to
send the XML data to the server via HTTP POST.
|
Obviously, in a real-world example, the user's
score would be generated by her 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 appears 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 => 'success');
}
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. --->
<cfset scores_xml = XmlParse( getHTTPRequestData( ).content ) >
<!--- Parse out the XML packet sent from Flash. --->
<!--- 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, place the
following 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>";
?>
All three scripts have one thing in common: they
all output an XML response with <success> as the
root node, containing a text node value of 1 for successful save
and 0, which indicates some type of error.
To test the Flash movie once you have it and the
server-side script in place, you need only to run the movie and
click on the button. 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 that was entered via the Flash movie.
See Also
Chapter
19specifically Recipes 19.6
and 19.7,
and
Recipe 20.11.
|