Building a Basic IP Address Service
I was doing some casual end of day surfing the other day when I ran across Rich Tretola’s blog post on getting a user’s IP address into Flash. What I really liked about Rich’s implementation was the simplicity of the approach - put the IP in the containing HTML. When I wanted to solve this same problem in a past project, my first reaction was to build a service, because, well, everything is a service these days. Here’s my little IP service, built on ColdFusion, and a little ActionScript to use it.
Getting the IP address of a client is remarkably easy when you handle it at the server. Most server technologies capture this information as some fashion of server variable. In the case of the IP address in ColdFusion, you’ll find this information on the CGI structure in the REMOTE_ADDR property. If you CFDUMP the CGI structure, you’ll often from the IP in other properties as well. I simply chose the REMOTE_ADDR property because it seemed more consistent.
<cfoutput>#CGI.REMOTE_ADDR#</cfoutput>
Simply getting the IP wasn’t enough for me though, and I wanted to provide some additional value. A quick search around the web landed me with several “geo IP” services that map an IP to a specific geography. Calling a web service from ColdFusion is a matter of using the CFINVOKE tag, specifying the URL to the WSDL of the web service, and the operation (called “method” in ColdFusion) on that service that you want to call. It’s also helpful to specify a return variable.
<cfinvoke returnvariable="geoip" webservice="http://www.webservicex.net/geoipservice.asmx?WSDL" method="GetGeoIP"> <cfinvokeargument name="IPAddress" value="#CGI.REMOTE_ADDR#" /> </cfinvoke>
In the case of this geo-IP web service, you’ll get a few pieces of information back, most notable is the name of the country from which the IP originates and the respective country code. That’s three pieces of information to hand back, and since Flash does such an excellent job with XML, I chose to go that direction. I little fiddling with the data and I landed at a schema I like, into which I simply dumped the return values from the web service call.
<result>
<ip><cfoutput>#geoip.getIP()#</cfoutput></ip>
<country name="<cfoutput>#geoip.getCountryName()#</cfoutput>" code="<cfoutput>#geoip.getCountryCode()#</cfoutput>" />
</result>I think there’s a tendency for this to get a little confusing. The above ColdFusion code builds a service that can be called by any client capable of handling XML as a result format. This little service itself however, actually leverages another web service. Let’s call it composite web services. That’s the great thing about web services; they open the door for you to mix and match the data in your own new and interesting ways. Who knows, somebody else may use this ColdFusion service in their own service, making the service chain nested three levels.
On to consuming this service from ActionScript. If you’re using Flex, this is an HTTPService call where HTTPService.resultFormat is E4X. Put a result handler on there as well, and you’re off to the races. Since this is really more about Flash in general however, I’m going to talk about using URLLoader, which is really what HTTPService uses under the covers.
To interact with this service, you instantiate a new URLLoader object, add the event listener for Event.COMPLETE, so you know when the data has been returned, and then call URLLoader.load() passing a URLRequest instance. The URLRequest instance contains the URL that acts as the endpoint for this service. While my little ColdFusion service here is public, I make no garuntees about it being around forever. If you like what I’ve done here, you should probably look to create a similar service on a server you control.
var loader:URLLoader = null; loader = new URLLoader(); loader.addEventListener( Event.COMPLETE, doIPComplete ); loader.load( new URLRequest( IP_SERVICE ) );
When the results come back, the event handler that you specified on the URLLoader will be called. In this case, I know we’re dealing with XML, so I create a new XML object from the raw text string contained in the URLLoader.data property. From there I can use E4X to quickly access the pieces of data in which I am interested. Note that the root node is actually a given, and doesn’t need to be specified on the XML object. So rather than “result.result.country.@name” we can simply say “result.country.@name” where the first “result” is actually the variable name.
public function doIPComplete( event:Event ):void { var loader:URLLoader = event.currentTarget as URLLoader; var result:XML = new XML( loader.data ); ip.text = result.ip; country.text = result.country.@name; code.text = result.country.@code; }
It seems obvious that the next step would be to wrap this in it’s own class that can be easily reused. I’m lazy I guess then, because since this is but a few lines of code it barely seems worth the effort. Since JavaScript doesn’t have the ability to reach across domains, it would probably also make sense to provide a little script that could be included in HTML. If you build a JavaScript wrapper, let me know and I’ll put it up on the server as well. Or if you build an ActionScript class wrapper, let me know so I can help share your work. In the meantime, here’s the source code for both the ColdFusion part and the ActionScript application. An example application is also available for viewing.