Returning JSONP: A Proposal

81 views
Skip to first unread message

Matt Gersting

unread,
Nov 9, 2011, 3:42:37 PM11/9/11
to taffy...@googlegroups.com
A little while back I started developing a service for my company that, among other things, needed to be able to return data formatted as JSONP.  I hit a road block early on - there was no way to cleanly add the JSONP function callback data - but came up with a quick and dirty workaround:  I did the actual data formatting in the resource itself, then passed that output to a representation that just passed it through untouched.  This was well and good until I decided that resource also needed to be able to return the data in plain JSON format.  I couldn't do it because I had hard coded the formatting.

So, when I came back to it today I decided to see if I could fix the problem.  My solution (proposed and working on my copy) is thus:

I've added a new "property" to baseRepresentation.cfc - variables.additionalArgs - and the requisite getter/setter.  I've added a third argument to resource.representationOf() - additionalArgs - that takes a struct and then conditionally uses the new setter to push this data into the active representation.  Now, whatever data was put into that struct is now available to representation.

If it sounds OK, I'll be happy to put them in my Taffy branch and make a pull request for review.  Would enjoy everyone's thoughts or if anyone has solved this another way.

Thanks.

Adam Tuttle

unread,
Nov 9, 2011, 8:32:05 PM11/9/11
to taffy...@googlegroups.com
Regardless of how it's done, I think supporting jsonp is a must! Thanks for pointing it out.

I think the solution you're describing may be a little too general. We should be able to build jsonp into the framework somehow, since there are a few assumptions we can make...

I have to admit that I'm not entirely up to speed on the standards surrounding jsonp. My current understanding is that somehow you have to specify that you want the data back as jsonp (how?), and also provide a callback method name, right? I think the default/standard callback specifier (how you specify the name of the callback you want to be called) is just "callback"...

So the intention is that you would then include the script on your page in something like this manner:


And the api will return something like:

useReturnData([{"foo":true,"bar":false},{"foo":false,"bar":true}]);

... and it's up to you to have already implemented the useReturnData function somewhere on the page.

Assuming the above (and thus, my understanding of jsonp) is correct, then...
  • We're indicating that we want to use jsonp in this example by using it as an extension (ala Rails)... but I'm already considering removing this functionality for other reasons.
  • Is there a standard jsonp Accepts/Content-Type header? In my brief googling, it looks like jsonp must be returned as content type "application/javascript" or some browsers won't execute it. It may be worth pointing out: you can't set the accepts header when embedding a script tag, so we can't build anything that looks for a specific Accepts header. The browser's going to send whatever value it likes. 
  • So either we need to use the .jsonp "extension" functionality to indicate the response should be jsonp, or we need to look for a specific url parameter. If we're going to look for a url parameter, I think we should either go with "callback" which I think would make it work well with jQuery out of the box, or "jsonp_callback" which would be a little bit more explicit and less likely that your code will have an accidental argument name collision.
Given all of this, my only thought is that your api would treat the request as any other json request (just return the raw data and use the normal serialization techniques), and then once it's returned to the framework and just before the framework would return it to the client, it checks to see if the user actually wants jsonp (either via extension or via callback specification) and wrap the result in the callback.

Thoughts?

Adam

Matt Gersting

unread,
Nov 9, 2011, 9:09:15 PM11/9/11
to taffy...@googlegroups.com
I think as long as Taffy is supporting the file extension functionality supporting JSONP via that method would be expected.  And essentially it is - I just added the getAsJsonp() method to the my representation class and voila.

Looking around I found some differing in the callback format.  Wikipedia suggests that the standard format is:

server.com/resource?jsonp=callbackName

My experience with jQuery's JSONP request generation is that it sends the callback as

server.com/resource?callback=generatedByjQueryOrUserDependingOnWhatTheyDo

The framework could decide to support a given number of commonly used conventions.  Perhaps an array or list in the configuration of acceptable queryStringValues in order of precedence.

Admittedly I'm probably a little biased towards what I coded up today, but it may be due to differing ideas about the nature of the framework itself.  While Taffy obviously support JSON out of the box, I tend to think of it as completely format agnostic.  It does all the heavy lifting, but where the developer really comes in (besides connecting up to their internal data and systems) is in the custom representations.  This really is the only area that developers would really be expected to possibly tinker.  Granted, given the ubiquity of JSON / XML / raw HTML etc it's probably not altogether a regular thing to have to write a completely new representation.  That being said, by allowing the resource to push data into the representation it opens it up for any potential new format down the road, and keeps them within the custom representation idea. No changing the framework core for odd custom implementations.

Barney Boisvert

unread,
Nov 10, 2011, 9:57:07 PM11/10/11
to taffy...@googlegroups.com
One thing potentially worth considering is signed requests. Much like
HTTP (and mail) headers, the 'x-' prefix is a likely candidate for
extra parameters which don't describe the request itself, but rather
encoding information. AWS follows this paradigm, we use this for all
of our APIs at Mentor, and I've adopted it across the board wherever
possible. For example, 'x-callback' for a callback parameter, so we
have a request like this:

/path/to/resource?whateverParams&x-callback=myFunction

This allows the callback to be excluded from signing considerations
(along with all other 'x-' parameters). Standardized or not is a
different question, but certainly something with huge precedent.

All told, I'm not sure supplying a callback parameter should be
sufficient to indicate a JSON/P response is desired. It certainly
makes sense to support a '.jsonp' extension (parallel to '.json', and
'.xml') as an indicator of format. However, passing the callback I
think DOES belong outside the request. So I'd propose a response
indicator (the extension) with a default callback name, and an
optional way of specifying a specific callback if that's what you
want. The important piece is that you should be able to grant someone
access to a JSON/P URL (including authorizing it) but let _them_
specify the callback method.

cheers,
barneyb

--
Barney Boisvert
bboi...@gmail.com
http://www.barneyb.com/

Adam Tuttle

unread,
Nov 10, 2011, 10:33:50 PM11/10/11
to taffy...@googlegroups.com
Sage advice as usual, Barney, thanks.

I'm not sure I follow on the bits about "signing" and "authorizing" though? Specifically:

>This allows the callback to be excluded from signing considerations (along with all other 'x-' parameters).

and

>The important piece is that you should be able to grant someone access to a JSON/P URL (including authorizing it) but let _them_ specify the callback method.

It could just be that you're using these to refer to whatever authentication method may be in use (basic auth, api key, etc), in which case I think I get it. I'm just not sure if that's what you're alluding to. So if not, what exactly do you mean? :)

Thanks,
Adam

Barney Boisvert

unread,
Nov 10, 2011, 11:28:44 PM11/10/11
to taffy...@googlegroups.com
I'm sorry, should have been more explicit. For example, if you use
protected assets on S3, you're familiar with signing a request. In a
nutshell, you assemble your URL (e.g.,
'http://s3.barneyb.com/path/to/object?Expires=123000000') and then
cryptographically sign the whole URL and append the signature (e.g.,
'http://s3.barneyb.com/path/to/object?Expires=123000000&Signature=abacadaba12345').
At this point you have a signed URL which can be used to make a
specific request. Because it's signed with your secret key, it's safe
to distribute that URL to "untrusted" people for them to use (e.g., in
an A tag for a protected download). The signature ensures that that
untrusted user can't change the URL in any way, they can only use it
as is.

Now consider that same case where the URL is pointing at a JSON/P
endpoint. In this case the untrusted user is going to be using your
URL to feed data into *their* application. So you're going to be
signing a URL which allows an untrusted user to make a JSON/P request,
but you don't necessarily know how their application's internals work,
and so therefore can't necessarily know what they're going to want for
a callback. Consider the case of using jQuery to make a callback,
where you specify a URL and a callback. Perhaps the server generates
a signed URL and passes it to your JavaScript to actually invoke
(perhaps more than once, though still subject to expiration).

In that particular case, it becomes VERY important that your URL is
tightly controlled within control of the API client (because it's
going to go to an untrusted user), but at the same time the untrusted
user has to be able to interact with the URL (not the API, the URL) on
it's terms so they have to be able to specify the callback.

If you're using basic auth or an API key, for example, there's no way
to hand an authenticated request config to an untrusted user to make
the actual request, because the request has to be made with your
credential. However a signed request config CAN be passed to an
untrusted user, because it can be made (without modification) without
requiring your credential. Obviously S3 (the example above) doesn't
support JSON/P callbacks, but Twitter, for example, does. With the
Twitter API you can't give an arbitrary third party one-time use to
access your protected tweets. You can grant them permanent use via
OAuth, or you can make your feed public (so everyone has access), but
you can't go half way. With a signed URL, you CAN offer that sort of
functionality.

Does that make more sense?

cheers,
barneyb

Adam Tuttle

unread,
Nov 11, 2011, 12:08:38 AM11/11/11
to taffy...@googlegroups.com
I think so. You kind of lost me in the middle 2 paragraphs a little bit, but I picked back up again at the end. :)

So if I understand correctly, your point is that the x-parameters (eg x-callback) would be excluded when encrypting the url on the client side, and then when the server receives it, decrypts, and compares, it would ignore any differing params that start with 'x-'.

That's pretty smart. :)

Adam

Barney Boisvert

unread,
Nov 11, 2011, 1:13:32 AM11/11/11
to taffy...@googlegroups.com
Exactly.  The 'x-' params exist outside the core framework.  They're "eXtra"; not controlling semantics, just syntax, just as 'X-' HTTP headers or 'X-' mail headers do.

And just to be clear, I don't in any way claim I came up with this idea.  The HTTP spec was the genesis (I think) and AWS provided the inspiration (at least to me) for leveraging the distinction in auth schemes. 

cheers,
barneyb
Reply all
Reply to author
Forward
0 new messages