Simon Online

2009-04-18

ASP.net MVC returning JSONP

I’ve been working on a piece of code which returns JSON in response to some request. It has all being going fine on the local host but I’ve just deployed it up to my new test server and started to access it using JQuery and it stooped working. This is, of course, typical. This time however, my problem was ignorance rather than a programming blunder. As it turns out when returning JSON across domains you need to actually return JSONP in order to get call backs working. What is JSONP? I’m glad you asked, because I had no idea either. Basically it is the same JSON you love but wrapped with a bit of text which specifies the name of the function to call upon returning.

JSON:

{“userID”:”00000000-0000-0000-0000-000000000000”³,”success”:”false”}

JSONP:

somefunction({“userID”:”00000000-0000-0000-0000-000000000000”³,”success”:”false”})

Easy enough. In JQuery you need to just add another parameter to the JSON call in order to pass the name of the function to the server

$.getJSON(URL +“/json/Message/sendMessage?userName=”+ $(“#userName3”).val()+
“&messageText=”+ $(“#message”).val()+
“&userKey=”+ key +
“&jsoncallback=?”,
function(json)
{
}
);

JQuery will automatically replace the ? with the name of your callback function, in this case an anonymous function.

Having discovered all of this I realized that I now had a ton of code returning JsonResults which needed to be changed. I figured the best way to do this was to actually create a JsonpResult which was based off of the JsonResult. So I did just that, basing it off of the now open sourced ASP.net MVC JsonResult

publicclass JsonpResult : System.Web.Mvc.JsonResult
{
publicoverridevoid ExecuteResult(ControllerContext context)
{
if(context ==null)
{
thrownew ArgumentNullException(context);
}

HttpResponseBase response = context.HttpContext.Response;

if(!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType =application/json;
}
if(ContentEncoding !=null)
{
response.ContentEncoding = ContentEncoding;
}
if(Data !=null)
{
// The JavaScriptSerializer type was marked as obsolete prior to .NET Framework 3.5 SP1

#pragma warning disable 0618
HttpRequestBase request = context.HttpContext.Request;

JavaScriptSerializer serializer =new JavaScriptSerializer();
if(null!= request.Params[jsoncallback])
response.Write(request.Params[jsoncallback]+(+ serializer.Serialize(Data)+));
else
response.Write(serializer.Serialize(Data));

#pragma warning restore 0618
}
}
}

I then extended Controller

publicclass WopsleController : Controller
{
protectedinternal JsonpResult Jsonp(object data)
{
return Jsonp(data,null/ contentType /);
}

protectedinternal JsonpResult Jsonp(object data,string contentType)
{
return Jsonp(data, contentType,null);
}

protectedinternalvirtual JsonpResult Jsonp(object data,string contentType, Encoding contentEncoding)
{
returnnew JsonpResult
{
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding
};
}

}

and altered all my controllers to extend WopsleController rather than Controller. Seems to work pretty well.

2009-02-28

Best Bug I've Written Today

Today I create a handy little class in my attempt to rewrite wordle. It encapsulated a word and a word count and implemented comparable

1 package com.simontimms.wordle;
2
3 public class WordCountElement implementsComparable<WordCountElement>{
4
5
6 privateint count =0;
7 privateString word;
8
9
10 publicWordCountElement(String word,int count)
11 {
12 this.word = word;
13 this.count = count;
14 }
15
16 publicintcompareTo(WordCountElement toCompare)
17 {
18 return this.count - toCompare.getCount();
19
20 }
21
22 publicvoidsetCount(int count) {
23 this.count = count;
24 }
25
26 publicintgetCount() {
27 return count;
28 }
29
30 publicvoidsetWord(String word) {
31 this.word = word;
32 }
33
34 publicStringgetWord() {
35 return word;
36 }
37 }

At another point in my code I created a sorted set out of these WordCountElements

1 publicTreeSet<WordCountElement>getSortedSetOfWordCounts(String textToAnalyze)
2 {
3 HashMap<String, WordCountElement> unsortedSet =getWordCounts(textToAnalyze);
4 TreeSet<WordCountElement> sortedSet =newTreeSet<WordCountElement>();
5
6 for(String s : unsortedSet.keySet())
7 {
8 sortedSet.add(unsortedSet.get(s));
9 System.out.println(s);
10 }
11 return sortedSet;
12 }

I couldn’t get my unit tests to pass when I had more than one word in the unsortedSet. I ended up debugging it and found that even though I was adding two different words only the first was present in the sorted set. Well select might not be broken but perhaps sorted sets were. Can you see the bug?

That’s right, the custom comparator resulted in two words with equal counts being equal. So select wasn’t broken. Drat.

2009-02-26

Tweetdeck on 64bit Ubuntu Llinux

Tweetdeck is a very powerful twitter client which is written in the rather nifty Adobe Air framework. Unfortunately the air platform does not yet have support for 64-bit Linux. I imagine it will come along once flash 10 for 64 bit Linux comes out of beta. There is a rather long tutorial on how to work around the limitations and run it in 32-bit mode on 64-bit linux right here but that isn’t enough to run tweetdeck. You’ll also need to pull down a copy of 32 bit libgnome-keyring to allow it to access your keyring.

wget http://ubuntu.interlegis.gov.br/ubuntu/pool/main/g/gnome-keyring/libgnome-keyring0_2.22.2-0ubuntu1_i386.deb
ar x libgnome-keyring0_2.22.2-0ubuntu1_i386.deb data.tar.gz
tar -xzvf data.tar.gz
cp usr/lib/* /usr/lib32/
On a related note it is a shame that the default Air page is so pedestrian. I am filled with no excitement upon visiting it.