You are reading a MIX Online Opinion. In which we speak our minds. Karsten Januszewski Meet Karsten Arrow

Opinions

7Comment Retweet

JSON-P: An Elegant Hack (And Another Hack: Creating a JSON-P Service with the WCF REST Starter Kit!)

Nov 16, 2009 In Development By Karsten Januszewski

I’m working on some prototyping for an upcoming Mix Online prototype (a bit recursive, no?). My prototype provides a service with a REST interface, which has a few methods that send data via JSON and XML.

I’m using the WCF REST Starter Kit (http://www.asp.net/downloads/starter-kits/wcf-rest/) to get a REST service up and running quickly, and using the UriTemplate syntax to make my REST service nice and Web 2.0-like.  It’s working out wonderfully. Check out the Starter Kit and its excellent supporting documentation here: http://msdn.microsoft.com/en-us/library/ee391967.aspx.

The Cross Domain Scripting Problem

The service I’m prototyping will be consumed by other websites via javascript. At first, my architecture required the user to put a reference to my javascript in his or her page, to get around the cross domain scripting problem.  This is pretty standard operating procedure, but it isn’t ideal—someone who wants to use my script has to load a third-party script from a different server, and take a dependency on me.

In cases where the service provides more than data (say a mapping service like Virtual Earth), there’s no way to get around requiring someone using the service to embed the script in their page. But in cases like mine, where the service is just providing data, it’s best to allow the client to get at the data without referencing a script. But how?  Browsers don’t allow cross domain calls from javascript.

As it turns out, there is a solution: JSON-P. The use of JSON-P allows my service to be called cross-domain without requiring an explicit embedding of a third party script.

What’s JSON-P?

JSON-P is a Javascript Object Notation with Padding.  What does that mean?  Pretty simple,really: When a provider returns its data formatted as JSON-P,the JSON is wrapped with a callback specified by the client.

So, this JSON:

     { ‘name’: ‘Karsten’ }

becomes this JSON-P:

     myCallback( { ‘name’: ‘Karsten’ } ); 

myCallback gets called by the client when it is passed to the service.  What’s the point?  Well, instead of referencing the third party script at design-time, the client can call the service just as they would call an AJAX service from the existing domain. The result? The client can get at data from a service that is cross domain.

Most javascript libraries support JSON-P, so you don’t have to manually do anything—it "just works". With jQuery, you don’t even have to provide the callback; you can just add a question mark to the end of the service call, like so:

     callback=? 

The callback gets auto-generated and you simply embed your logic in the call, like this:


   $.getJSON("http://example.com/service?callback=?",               function(data) {
        //do something with the data                

      }); 

Safety Issues

Some folks worry about security holes with JSON-P, but I wonder—is the JSON-P solution really that much different than embedding a script from another URL in your page?

I think not. In fact, you might argue that JSON-P is (marginally) more secure than embedding a third party script in your page, because usually JSON-P is just returning data.

I say marginally because there’s nothing stopping the service that’s returning the JSON-P from returning malicious code instead of data. So you’d better trust the service, just as you’d better trust any third party script that you embed in your page.

An Elegant Hack?

JSON-P might just be a cross site scripting hack—but it’s a very elegant hack that means my service can be called cross domain.  And it is becoming increasing popular. Services such as Twitter, YouTube, Digg and a host of other big name services out there offer their data up as JSON-P.

Supporting JSON-P

This all leads me to my prototype, in which I want to support JSON-P.  JSON-P isn’t supported by default in WCF until .NET 4. But, it turns out there is a very nice sample which supports JSON-P in WCF. Download it here: http://msdn.microsoft.com/en-us/library/cc716898.aspx.

JSON-P and the REST Starter Kit

It turns out that you can use JSON-P in combination with the REST Starter Kit. Here’s the signature of a method that combines URITemplate features of the REST Starter Kit with the JSONPBehavior attribute, provided by the WCF JSON-P sample:


    [WebGet(ResponseFormat = WebMessageFormat.Json,             UriTemplate = "{userName}?cb={callbackName}")]       [OperationContract]    [JSONPBehavior(callback = "callbackName")]    public string GetDataJSONCallback(string userName,            string callbackName)
    {
        return userName + “: I was called by WCF!”;
    } 

What’s going on here?  Well, the first attribute, WebGet, specifies that we want to 1) return JSON and 2) that our URITemplate has a path which includes the name of the callback. (Note: Don’t be distracted by the userName parameter; I included that to show how the REST path syntax works.)

The crux is that the variable name of the callback name, in this case callbackName, needs to match between the WebGet attribute, the JSONPBehavior attribute and the signature of the method call itself.  If this is the case and all three are the same, magic happens: the service will return its data wrapped in JSONP with the name of the callback specified by the client.  Thanks, WCF!

The Trade-off

When you use the JSONPBehavior attribute, you intercept the request and override the WCF MessageEncoder. This means you bypass some of the glue in the REST Starter Kit, so things like caching will no longer work. This is the only "gotcha" with combining the REST Starter Kit and the JSONPBehavior attribute. But for me, the tradeoff is worth it.

To make life more encapsulated, I went ahead and took the four classes from the JSON-P sample (JSONPBehavior.cs, JSONPBindingElement.cs, JSONPBindingExtension.cs and JSONPEncoderFactory.cs) and compiled them into a single .dll which I then referenced in my WCF Service.  Hat’s off to whoever wrote such a well-encapsulated sample. 

Follow the Conversation

7 comments so far. You should leave one, too.

James Senior said on Nov 18, 2009

Hey Karsten

Make sure you check out the JSONP functionality in the new ASP.NET Ajax Library. It makes calling services from other websites super easy.

Here is a sample: http://www.asp.net/ajaxlibrary/HOW%20TO%20Use%20JSONP%20to%20Request%20Data%20from%20Remote%20Websites.ashx

Cheers
James

Sigurður Karl Magnússon said on Jan 11, 2010

Thanks for the post.

I implemented the example above and the response from the WCF REST service is still just JSON structure without a function call wrapped around it.

But behind the code this get executed:
OperationContext.Current.OutgoingMessageProperties.Add(JSONPMessageProperty.Name, property);

where
Name = ""Microsoft.ServiceModel.Samples.JSONPMessageProperty"
property = is an instance of JSONPMessageProperty that has the name of the callback method.

When using JQuery getJSON my coworker still gets the invalid label error.

Karsten Januszewski Karsten Januszewski said on Jan 22, 2010

@Sigurður -- Hmm, not sure what is going on there. Did you try hitting your service directly (not through jQuery) to see what it returns?

Motion Detector Cameras Motion Detector Cameras said on Dec 10, 2010

This is the only "gotcha" with combining the REST Starter Kit and the JSONPBehavior attribute. But for me, the tradeoff is worth it.
http://www.securityandselfdefensestore.com/products/hidden-cameras/motion-activated.html

Free Online Dating Site Reviews said on Jan 12, 2011

I implemented the example above and the response from the WCF REST service is still just JSON structure without a function call wrapped around it.

Truman One said on Jan 17, 2011

Thanks - good info!!

Before JSONP, you said you "required the user to put a reference to my javascript in his or her page...to get around the cross domain issue". Can you give me a few more hints on that? I am trying to see if I really need JSONP or would the other option suffice in my case.

Mowadogmawn said on May 18, 2011

Hi !!! Good job!