How to access Web Services with GWT
February 6th, 2008 - Written by in Using GWT
There are a lot of new and interesting web services popping up on the web these days, and you may want to make use them in your GWT applications. In this short guide, I will show you how to call these public apis from within your GWT app.
The main difficulty when trying to talk to some web service on another server is getting past your web browser’s Same-Origin Policy. This basically says that you may only make calls to the same domain as the page you are on. This is good for security reasons, but inconvenient for you as a developer as it eliminates the use of GWT’s HTTP library functions to achieve what we want to do. One way to get around this is to call a web service through a javascript tag which bypasses this problem. In his book, Google Web Toolkit Applications, Ryan Dewsbury actually explains this technique in more detail and provides a class called JSONRequest which handles all the hard work for us. JSON is one of the more popular data formats, so most web services support it. Lets leverage Ryan’s code and take a quick look at how it works.
public class JSONRequest { public static void get(String url, JSONRequestHandler handler) { String callbackName = "JSONCallback"+handler.hashCode(); get( url+callbackName, callbackName, handler ); } public static void get(String url, String callbackName, JSONRequestHandler handler ) { createCallbackFunction( handler, callbackName ); addScript(url); } public static native void addScript(String url) /*-{ var scr = document.createElement("script"); scr.setAttribute("language", "JavaScript"); scr.setAttribute("src", url); document.getElementsByTagName("body")[0].appendChild(scr); }-*/; private native static void createCallbackFunction( JSONRequestHandler obj, String callbackName)/*-{ tmpcallback = function(j) { ::onRequestComplete(Lcom/google/gwt/core/client/JavaScriptObject;)(j); }; eval( "window." + callbackName + "=tmpcallback" ); }-*/; }
To make our request we call the get
method with the web service url, and an implementation of the JSONRequestHandler interface. This interface has one method called onRequestComplete(String json)
. This is where you’ll handle the JSON formatted data once it comes back from the server. When calling a service from within a script
tag, we need to specify the name of a callback function in the request. Most services let you specify the name yourself, so the first get
method generates a callback name for you. The createCallback
method is a JSNI method that simply calls your JSONRequestHandler
implementation when the call returns via the callback name. Note, if you use this class, to make sure and change the package name for the JSONRequestHandler
call to the correct location. Finally, the get
method will call the addScript
function which is responsible for embedding the tag on your page and setting its
src
attribute to the web service url.
Now that I’ve described the technique to make the calls, you’ll need to find some APIs to use it with. When looking at an API specification, make sure it has a parameter to return results in JSON format, and that it supports a callback parameter. Here are some example web service APIs to start with.
– Get a list of my uploaded videos.
JSONRequest.get( "http://gdata.youtube.com/feeds/api/users/bogleg/uploads?alt=json-in-script&callback=", new JSONRequestHandler() { ... });
– Get the social graph of bradfitz.com.
JSONRequest.get( "http://socialgraph.apis.google.com/lookup?&q=bradfitz.com&pretty=1&fme=true&callback=", new JSONRequestHandler() { ... });
Digg API – Get the top stories from Digg.
JSONRequest.get( "http://services.digg.com/stories/top?appkey=http%3A%2F%2Fgwtsite.com&type=javascript&callback=", new JSONRequestHandler() { ... });
GeoNames webservice – Get a zip-code location.
JSONRequest.get( "http://www.geonames.org/postalCodeLookupJSON?postalcode=94566&country=US&callback=", new JSONRequestHandler() { ... });
I hope you found this short tutorial useful. If you want to learn more about GWT, make sure to .
[...] Fong from GWT Site has posted a tutorial on how to read data from a 3rd party server using JSONP. The tutorial is derived from the [...]
Couple of suggestions.
In your createCallback function, it is better to write
$wnd[callbackName]=tmpcallback
(or window[callbackName])
because GWT can obfuscate the paramater ‘callbackName’. In general, don’t use eval() inside JSNI functions.
It’s probably better practice to inject stuff into $wnd/$doc than the IFRAME.
Also, you probably want to delete $wnd[callbackName] in the closure, so that you don’t leak.
Cheers,
-Ray
Thanks for the tips Ray. I’ll try them out.
The article by Google’s Dan Morrill, titled actually uses the techniques Ray describes. It’s quite good and goes into more detail then I do.
Actually you DO want to use the IFRAME in this case. Using $wnd /$doc does not work for this type of JSON (the JSON must be loaded in the same frame its processed in). Dan’s article covers this as well.
Another note. GWT is not going to obfuscate a string you pass into JSNI – it will obfuscate the variable NAME, but definitely not the value.
Yes, it will obfuscate the name which is what I meant (if it obfuscated values, no application would work) The eval() still can fail, but for a different reason, I didn’t notice the callback name was being used by value the first time. But tmpcallback is being used by name, so “=tmpcallback” can fail. This construct is unsafe.
Dan’s point about same-iframe applies if you’re using GWT’s JSON library, which the above code is not. I tend to write custom JSNI JSON code because I find the JSON library overly cumbersome and heavyweight. Yes, if you want to use instanceof in JSNI code, you need to load in the same IFRAME, or you can stringify the JSON and reparse it, which is another workaround I’ve seen people use.
In any case, the gotchas involved cry out for a standard GWT solution that doesn’t require people to roll their own.
Ray, you do have some good points but the above code WAS meant to be used with GWT’s JSON library. The JavaScriptObject passed back in the callback is used in the constructor of a JSONObject. It’s not in the class implementation but it is the best way to access the feed data in application code.
GWT’s JSON object model is very appropriate for this task. Creating separate JSNI methods for each value and object that can be returned in a feed is much more cumbersome than navigating the JSON library.
I would also not recommend turning the returned JavaScript object into a string since there is no standard way to do this. Note that these JSONP API’s don’t actually pass a string but call a function with a JavaScript object as a parameter. So you would need to convert this object to a JSON string somehow. Then you would need to parse it again. It’s quite an expensive task given the faster and simple alternative is using a class like the one above.
Another note is that some JSONP API’s don’t let you specify the callback method name and will just call a top level function named “callback” – this is why an array wasn’t used in the class the above code was derived from.
If you’d like to discuss further, I’d be happy to chat through email.
Ryan,
Whether the JSON library is more cumbersome or not depends on the complexity of the parsing. It’s far more verbose than the Javascript equivalent, so if you have a very complex JSON result, and you need to extract portions of it, writing a method a JSNI method that does ‘jsonobj.foo.bar[3].baz’ is more compact than the GWT JSON equivalent. I’ve written fairly complex JSON parsers using the GWT JSON module and they were more cumbersome and slower than a custom JSNI approach. (e.g. I parse 20,000 element multiresolution datastructures) I personally find the JSON module has more utility for generating JSON than parsing it. It’s like DOM vs XPath, I’d much rather use a DSL to navigate these objects. It doesn’t buy much type-safety either, unlike the GWT 1.5 ‘zero-overhead’ JSO approach. For 1.5, I’d like to see two approaches. One, using JSO subclasses to map JSON objects, and two, a more dynamic DSL driven approach, so that one may say JSONObject.get(“foo.bar.baz”)
I’m not sure I understand how the inability to override the callback name means you have to use eval() instead of array notation. For example, if callback is fixed as ‘callback’, you can write either window.callback = yourfunction or window['callback'] = closure. The issue is, as you know, that you can’t write eval(‘window.whatever=tmpcallback’) and define tmpcallback a local GWT variable, because as you pointed out, GWT won’t obfuscate the String argument of eval() but WILL obfuscate the local variable declaration name.
My two cents. Also, the JSON library irks me for other reasons, such as the naming of methods ‘is*()’ that don’t return boolean, but rather either null or a cast. It sticks out given normal Java conventions, and leads to cumbersome looking code. I guess it’s the best they can do, if only Java had Scala’s Option types, pattern matching, and for-comprehension.
-Ray
Ok, I see what you’re getting at. Probably for your large datasets the JSON lib wouldn’t be appropriate unless it had an XPATH like feature. You should probably send that feature request over to the GWT guys.
Fortunately this article deals with simple feeds which are typically a list of Items. Something that the JSON library is perfectly suited to. I think this is the more common application of JSON. Even for large datasets its typical to load by page.
Ray,
I agree with the points that you have made here except for one small issue. As far as I can tell the eval call is necessary to create a unique function name. If you know of a way to create a unique function name I would happily change it in my code. I tried writing a function to do this same thing until I ran into this same problem and went looking and found this class.
Thanks,
Matt
[...] You can read the full post here. [...]
[...] Blog bookmarks 02/12/2008 GWT Site » How to access Web Services with GWT [...]
Can anybody tell me step by set process of how to make a application in gwt using java. From path setting to how to run everything i will be grateful to him. my mail id is
I would suggest reading the GWT Getting Started guide.
Excuse me.
Could someone tell me how should I write the server site code to reply a correct JSON data?
Thanks!
Hi,
I’m just learning GWT and have Ryan’s book which is helping a great deal. I would like to integrate my GWT app with RESTlets using JSON representation. The JSONRequest technique in this forum would only work for Http GET – is this correct? I’m looking for a solution that would allow all Http method types to be used. I suppose I could use the GWT HttpRequest, but it appears that the GWT HttpRequest is limited in that it only supports methods GET and POST. I can probably get by with this limitation if I have to, but is there anything better out there?
Thanks!
Andre
I haven’t tried this myself, but have you looked at the gwt-rest project?
You can achieve this by setting a property on the header. Something on the lines of
httprequestbuilder.setHeader(“X-HTTP-Method-Override”, “DELETE”);
The request can still go out as POST, but the header is set to DELETE (or even PUT- which ever is applicable)
The RestService could strip off the header and process accordingly.
Thanks,
Vikas
Along this lines I have just started using Jersey the jax-rs RI which allows you to setup a servlet filter which will inspect a parameter and call the proper GET, POST, PUT, DELETE methods on your resources. You can also use Jersey to marshall JSON based on jaxb annotations.
Is there any other way to access a web service with GWT except JSON? JSON would be very difficult for me to implement right now and doesn’t seem very mature.
-mark
Hi Mark,
Why would you say that JSON is not very mature? Anyway, it all depends on the web service API and what it supports. GWT can support calling XML web services as well, but you have to make sure you can get around the Same-Origin policy.
Excellent! I forwarded this blog post to my friend who could use some of the advice that you gave! Thanks again!
Hm. I’ve implemented the above functions but when I call my web service, it seems I get an invalid label error in Firebug. I’ve tried to adding parentheses to the eval function but it doesn’t seem to work. Any thoughts?
[...] (more) [...]
Is it possible to return XML from the webservice? I’ve followed the example and it works like a charm. However the webservice i need to call can only return XML and i’m confused how to get this (as a string?) back to Java.
Hi Robert Willems,
Have you manage to solve your problem of accessing XML webservice and parsing the stuff?
TA.
Thanks for this code that is very clear and usefull.
Anyway, I’ve face (and solved) a problem.
As I was fetching UTF-8 data from a service, these data where showing some really bad looking characters in my HTML page.
I solved the problem by adding the following line in the JSONRequest.addScript() method :
scr.setAttribute(“charset”, “UTF-8″);
Cheers !
Hello Chris
I am new to Web services and GWT. But, I am trying to run your example.
But, I am facing some basic problem.
I am able to get expected result while running the web service URI separately. That means, If I type “http://localhost:8080/GWTWebServices/resources/activities/1/” in my browser, I get the perfect result.
Now, I want to see that on browser. I simply want to see single resource to test the client. So what do I need to change in your piece of code. I think, I need to change JSONRequest class methods definition, but dont know what to make changes.
Can you please help me with this kind of scenario?
Thanking you.
Sumved Shami
Can anyone help me to solve the above problem?
Sumved Shami
Hi!
I really appreciate your article. I ran into the Same Origin Policy-”problem” a few days ago, when I tried to connect to the Yahoo Finance module to get quotes as CSV data. The problem is, that it doesn’t allow a callback parameter.
As I am quite new to GWT programming, I would like to ask, how could one receive these kinds of data via GWT?
I think this tutorial would be named “How to do http request with gwt”
Web service is not simply http request but a complex software system allowing interaction between systems. It use often soap and wsdl technologies.
For a good description of what a webservice is : http://en.wikipedia.org/wiki/Web_service
But good tutorial and good site!
I disagree with both titles actually. This is more specifically how to get around the same origin policy(SOP) of modern browsers using a new concept called JSONP or JSON with Padding. Technically JSONP services are RESTful web services, so it is indeed a web service. If you would like to make an HTTP request like you have suggested in the title provided, you would want to use the included HTTPRequestBuilder. The only problem is that this must obey the SOP. If you would like to access services outside the origin you will have to create a server-side proxy, but the issue with this is that the target service can’t be authenticated because the request comes from the server and does not contain the client’s credentials. Also, if not implemented properly this can appear to the provider as a DOS attack because all requests are coming from the same machine.
Is it possible to do the same thing with a POST instead of a GET? I’m not quite sure how to achieve this. Advice would be appreciated.
Hi !
How can I realize an access to a Web Service, which responses a XML-Site. It doesn`t work with RequestBuilder.