shake, with fries

by Zach Carter

Posts tagged html5

Jul03

Cross-site HTTP Request for jsonip

json javascript html5 jsonp ajax | comments

I've added cross-site HTTP request support for jsonip, a micro-service I introduced previously that retrieves a client's IP address. Now, with HTTP access controls and a browser that supports them (such as Firefox 3.5,) any site can use a regular XmlHTTPRequest to utilize the service instead of JSONP. This might not be terribly useful today, as most browsers don't support HTTP access controls, but the arrival of Firefox 3.5 beacons the future of possibilities.

Here's a new cross-site HTTP request usage example:

<script>
  var req = new XMLHttpRequest();  
  req.open('GET', 'http://jsonip.appspot.com', true);  
  req.onreadystatechange = function (e) {  
    if (req.readyState === 4) {  
      if(req.status === 200) { 
        var ip = JSON.parse(req.responseText); 
        alert(ip.address);
      } else {  
        alert("Error loading page\n");  
      }
    }  
  };  
  req.send(null); 
</script>

You'll notice it looks just like a regular XmlHTTPRequest, except the domain does not have to be your own. My service at jsonip.appspot.com is using special headers that allow this to work instead of greeting you with the usual permission errors.

The source code of the python script I use for the service is below. You'll notice where the special headers are added:

import os
import cgi

form = cgi.FieldStorage()
callback = form.getvalue('callback','')

address = cgi.escape(os.environ["REMOTE_ADDR"])

json = '{"ip": "'+address+'", "address":"'+address+'"}'

#Allow cross domain XHR
print 'Access-Control-Allow-Origin: *'
print 'Access-Control-Allow-Methods: GET'

if callback != '':
  print 'Content-Type: application/javascript'
  result = callback+'('+json+');'
else:
  print 'Content-Type: application/json'
  result = json

print ''
print result

The magic happens here, with these printed headers:

print 'Access-Control-Allow-Origin: *'
print 'Access-Control-Allow-Methods: GET'

The first header means any domain can use this service cross-domain. Alternatively, you could have a comma seperated list of domains instead of a * to allow requests only from those domains. I want my service open to all sites, so I use a *.

The second header is self-explanatory (as if the first wasn't:) there's no need to support any other HTTP methods for this simple service, so I limit them to GET.

There are more access control headers available, so do make sure to read the article before trying these out. Some configurations could be dangerous, depending on your service.

Other jsonip Changes

  • JSON output uses double quotes for property names, as per the JSON specification
  • Added address property to use instead of the old ip property to retrieve the IP address. I've left the ip property for backwards compatibility.

The old, JSONP method of use still works as expected:

<script>
// callback function
function getip(jsonip){
  alert(jsonip.address) // alerts the ip address
}
</script>
<script type="text/javascript" src="http://jsonip.appspot.com/?callback=getip"> </script>

May23

Unobtrusive JavaScript in Rails 3

rails javascript html5 | comments

One of the coolest features I've heard about the upcoming Rails version 3 is its support for unobtrusive JavaScript.

The current JavaScript helpers in Rails produce HTML littered with in-line Prototype specific code:

<a href="#" onclick="new Ajax.Request('/comments/1', {asynchronous:true, evalScripts:ture, method:'delete'}); return false;">Destroy</a>

The new technique instead adds hooks in the form of HTML5 data attributes to the markup:

<a href="/comments/1" data-remote="true" data-method="delete">Destroy</a>

While unobtrusive, this particular example provided by DHH is still not degradable, but that's another issue (use button_to instead of link_to for deletes.)

I currently use a technique similar to data attributes, but utilizing the class attribute:

<a href="/comments/1" class="remote delete">Destroy</a>

And in my jQuery code, I can identify the element by its class name and dispatch the appropriate event handlers:

$('a.remote').click( /* click handler */ )

The click handler would use the href attribute and delete class name to know where and how to send the request.

This works well for hand crafted unobtrusive scripts, but you wouldn't want your framework overloading class names with arbitrary data, as it may conflict with stylesheet rules, and, it's kind of an abuse of the class attribute anyway. Using a future compatible HTML attribute is a nice solution. If you're worried about validation, you could always switch to the HTML5 doctype.

Since the scripts have been (rightfully) separated from the content, it also becomes much easier to switch out JS frameworks and still be able to use the built in Rails helpers. Cheers to this change!

Jan17

The Future of the Web

html5 rdf semanticweb | comments

The debate over supporting RDFa within HTML5 has been burning hot. A quote from the editor of the HTML5 specification reveals what the friction is really about:

It's not clear to me that the future of the Web is structured data. If anything, I'd predict it was unstructured data. It would be useful to know why you think structured data is the future. - Ian Hickson

I've heard similar sentiments from others at Google. And it's true, Google is great at understanding unstructured data. But for the rest of us application and script writers, structured data is especially useful. Until the layman's tools for information gathering are on Google's level, any realization of a Semantic Web will depend on structured data.