In the previous section, you learned how to use XHR to talk to a local
weather.php
file that in turn calls the Yahoo! Weather API. You might
wonder why XHR doesn’t go directly to the Yahoo! Weather API. It turns out that
because of cross-domain security issues in the browser, you can’t use the XHR
object to make a request to a server that is different from the originating server of the
JavaScript code. That would apply to the Flickr API as it does to the Yahoo! Weather API.
To get around this issue, you will need a little help from a server-side proxy in the
form of a PHP script whose job it is to take a tag and bounding box as input, call the
Flickr API to get photos, and return that in XML or JSON to the calling script.
I’ll show you how to write a server-side proxy to the Flickr API to get geotagged photos, but first I’ll prove to you that you can’t use XHR to go directly to the Yahoo! Weather API.
Let’s see why weather.html
can’t just call Yahoo! directly. You can find out what happens by running the following code, which instead of calling the local weather.php
goes directly to http://xml.weather.yahoo.com/forecastrss?p=94720
:[134]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Direct connect</title> <script type="text/javascript" src="/lib/yui/build/yahoo/yahoo.js"></script> <script type="text/javascript" src="/lib/yui/build/event/event.js"></script> <script type="text/javascript" src="/lib/yui/build/connection/connection.js"></script> </head> <body> <div id="status"></div> <script> div = document.getElementById('status'); var handleSuccess = function(o){ function parseHeaders(headerStr){ var headers = headerStr.split("\n"); for(var i=0; i < headers.length; i++){ var delimitPos = headers[i].indexOf(':'); if(delimitPos != -1){ headers[i] = "<p>" + headers[i].substring(0,delimitPos) + ":"+ headers[i].substring(delimitPos+1) + "</p>"; } return headers; } } if(o.responseText !== undefined){ div.innerHTML = "Transaction id: " + o.tId; div.innerHTML += "HTTP status: " + o.status; div.innerHTML += "Status code message: " + o.statusText; div.innerHTML += "HTTP headers: " + parseHeaders(o.getAllResponseHeaders); div.innerHTML += "Server response: " + o.responseText; div.innerHTML += "Argument object: property foo = " + o.argument.foo + "and property bar = " + o.argument.bar; } } var handleFailure = function(o){ if(o.responseText !== undefined){ div.innerHTML = "<li>Transaction id: " + o.tId + "</li>"; div.innerHTML += "<li>HTTP status: " + o.status + "</li>"; div.innerHTML += "<li>Status code message: " + o.statusText + "</li>"; } } var callback = { success:handleSuccess, failure: handleFailure, argument: { foo:"foo", bar:"bar" } }; var sUrl = "http://xml.weather.yahoo.com/forecastrss?p=94720"; var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback); </script> <div id="status"></div> </body> </html>
If you try to run this, you will get a JavaScript error. In Firefox, if you look in the Firefox error console, you’ll see the following:
Error: uncaught exception: Permission denied to call method XMLHttpRequest.open
The main lesson here is that XHR lets you access URLs only from the same
domain—for security reasons. Let’s prove that by making a new HTML file in
the same directory as a local copy of weather.php
. This security issue, and
the workaround by the server-side proxy, is explained here:
http://developer.yahoo.com/javascript/howto-proxy.html
In case you are still skeptical, you can change the JavaScript in your HTML to access weather.php
from this:
var sUrl = "http://xml.weather.yahooapis.com/forecastrss?p=94720";
to this:
var sUrl = "./weather.php?p=94720";
When you load weather.proxy.html
,[135]<div>
—but that’s not very nice. Let’s now move toward getting Flickr information.
Based on what you just learned, you now know that you need to get results about Flickr geotagged photos from the Flickr API into the browser using XHR. Hence, you’ll need a server-side proxy for bridging any client-side script with Flickr. That’s the aim of this section.
As an exercise, I recommend you write this code yourself before studying the solution presented. Think about how weather.php
works and how you can use flickr.photos.search
to look for geotagged photos. You can imagine a PHP script that gives access to the full range of input parameters for flickr.photos.search
in searches of public photos and returns the search results in a variety of useful formats. You can find a list of the input parameters for flickr.photos.search
here:
http://www.flickr.com/services/api/flickr.photos.search.html
A script that I wrote to serve as a server-side proxy for
flickr.photos.search
is flickrgeo.php
. You can run the
script here:
http://examples.mashupguide.net/ch10/flickrgeo.php
The code is listed here:
http://examples.mashupguide.net/ch10/flickrgeo.php.txt
Moreover, you will find a complete listing of the code in Chapter 13, including a description of how it handles KML and KML network links (which is beyond what is covered here). In this section, I’ll describe the overall structure of flickrgeo.php
and discuss some example usage.
With several exceptions, all the parameters for flickr.photos.search
are also parameters for flickrgeo.php
:
user_id
tags
tag_mode
text
min_upload_date
max_upload_date
min_taken_date
max_taken_date
license
sort
privacy_filter
accuracy
safe_search
content_type
machine_tags
machine_tag_mode
group_id
place_id
extras
per_page
page
There are three differences between the parameters for flickr.photos.search
and for flickrgeo.php
. First, the api_key
is hardwired for flickrgeo.php
. Second, instead of using the single bbox
parameter from flickr.photos.search
to specify the bounding box for geotagged photos, flickrgeo.php
takes four parameters: lat0
, lon0
, lat1
, and lon1
where lat0
, lon0
and lat1
, lon1
are, respectively, the southwest and northeast corners of the bounding box. Hence, the value of the bbox
parameter for flickr.photos.search
is {lon0},{lat0},{lon1},{lat1}
.
Second, instead of using the format parameter for Flickr API methods, which takes one of rest
(the default value), xml-rpc
, soap
, json
, or php
, flickrgeo.php
uses an o_format
parameter to control the output of the script. These are the values recognized by the script:
rest
returns the default (rest) output from the Flickr API.
json
returns the JSON output from the Flickr API.
html
returns an HTML form and list of photos.
kml
returns the search results as KML (see Chapter 13 for more details).
nl
returns the results as a KML network link (see Chapter 13 for more details).
If the o_format
is not set or is equal to html
, then you want to return the HTML form and a display of the photos. If the o_format
is rest
, return the default output from the Flickr API (rest
). If it’s json
, you want to return the JSON output with no callback.
For example, a sample invocation of this script shows the first page of geotagged photos tagged with cat
from all over the world:
If you change the o_format
to json
, you get JSON output:
This script generates a simple user interface so that you can test the input
parameters. That is, you can use the html
interface to see what photos are
coming back and then switch the output to json
, rest
,
kml
, or nl
to be used in your server-side proxy. Much
of the code is devoted to generating KML and KML network links, functionality used in
Chapter 13. There’s also some other convenience functionality: automatic form
generation, error checking, and some useful default values for the bbox
parameter. Again, consult Chapter 13 for more details.