jsongwtjsonp

How to handle JSONP callback using GWT?


I'm using GWT and I want to make a JSONP request, which invokes a GWT method of mine when it returns.

However, I'm having trouble figuring out how to specify the GWT method to invoke on callback. Can anyone help? Here's my example code:

private native void fetchUserData(String accessToken) /*-{
    var callback = "com.company.example.FacebookApi::handleUser";
    var url = "https://graph.facebook.com/me?access_token="+accessToken+"&callback=" + callback;

   // use jsonp to call the graph
   var script = document.createElement('script');
    script.src = url;
    document.body.appendChild(script);

  }-*/;

  public void handleUser(Object o) {
    Window.alert("Received object with class: " + o.getClass().getName())
  }

This code is ported from this example: Facebook Without SDK.

Alternatively, I just discovered there's a GWT JsonpRequestBuilder which I haven't had a chance to use yet, but if anyone can give an example without using any native code... then all the better.

Thanks!


Solution

  • Figured it out, thanks in large part to these examples:

    Gwt + JSONP

    Cross Domain Requests with Gwt, Jsonp

    Cross site referenceing in GWT

    Here's the updated code, per the comments (no callback specified, using Javascript Overlay Type)

      private void fetchDataUsingGwt() {
        String url = "https://graph.facebook.com/me?access_token=" + accessToken;
        JsonpRequestBuilder requestBuilder = new JsonpRequestBuilder();
        requestBuilder.requestObject(url, new AsyncCallback<FbUser>() {
      @Override
      public void onFailure(Throwable caught) {
        Window.alert(caught.getMessage());
      }
    
      @Override
      public void onSuccess(FbUser fbUser) {
          if (fbUser.isError()) {
            StringBuilder builder = new StringBuilder();
            builder.append("Fb error: ");
            builder.append(fbUser.getError().getMessage() + ", ");
            builder.append(fbUser.getError().getCode());
            String message = builder.toString();
            Window.alert(message);
            return;
          }
    
          StringBuilder builder = new StringBuilder();
          builder.append("Fetched user: " + fbUser.getFirstName() + " " + fbUser.getLastName());
          builder.append(" from " + fbUser.getHometown().getName());
          builder.append(" born on " + fbUser.getBirthday());
          builder.append(" with id " + fbUser.getId() + " and email " + fbUser.getEmail());
          builder.toString();
          String details = builder.toString();
          Window.alert("Got: " + details);
      }
    });
    

    }

    And the response is automatically wrapped using JSO like so:

      public class FbError extends JavaScriptObject {
        protected FbError() {
        }
    
        public final native String getMessage() /*-{
                return this.message;
        }-*/;
    
        public final native String getType() /*-{
                return this.type;
        }-*/;
    
        public final native String getCode() /*-{
                return this.code;
        }-*/;
    
        public final native String getSubCode() /*-{
                return this.error_subcode;
        }-*/;
    
      }
    
      public class Hometown extends JavaScriptObject {
        protected Hometown() {
        }
    
        public final native String getName() /*-{
                return this.name;
        }-*/;
    
        public final native String getId() /*-{
                return this.id;
        }-*/;
      }
    
      public class ErrorableJso extends JavaScriptObject {
    
        public boolean isError() {
          return getError() != null;
        }
    
        public final native FbError getError() /*-{
                return this.error;
        }-*/;
      }
    
      public class FbUser extends ErrorableJso {
    
        // TODO: Separate call needed to retrieve profile pic
    
        protected FbUser() {
        }
    
        public final native String getFirstName() /*-{
                return this.first_name;
        }-*/;
    
        public final native String getLastName() /*-{
                return this.last_name;
        }-*/;
    
        public final native String getId() /*-{
                return this.id;
        }-*/;
    
        public final native String getBirthday() /*-{
                return this.birthday;
        }-*/;
    
        public final native String getEmail() /*-{
                return this.email;
        }-*/;
    
        public final native Hometown getHometown() /*-{
                return this.hometown;
        }-*/;
      }
    

    For completeness, this is the raw JSON response the JSO wraps. Because of the inheritance, the same FbUser object is used if there's either an error like so:

    {
       "error": {
          "message": "Error validating access token: Session has expired at unix time 1342044000. The current unix time is 1342050026.",
          "type": "OAuthException",
          "code": 190,
          "error_subcode": 463
       }
    }
    

    Or the expected User object:

    {
       "id": "23232323",
       "name": "Andrew Cuga",
       "first_name": "Andrew",
       "last_name": "Cuga",
       "link": "http://www.facebook.com/TheAndy",
       "username": "TheAndy",
       "birthday": "02/20/2011",
       "hometown": {
          "id": "108530542504412",
          "name": "Newark, Delaware"
       } // ... etc
    }
    

    Note the error and hometown fields in the JSON response are easily wrapped into JavaScriptObjects.