french english

RSS 1.0 
 
 Login 
 Password 
 Créer un compte 
 
01-10-08 / 01:06 : Cappuccino : JSON, JSONP et CPJSONPConnection (cjed)
Les applications clientes Cappuccino communiquent avec les serveurs via des requêtes HTTP avec un format d'échange qui est soit du XML (format/schéma propre à définir, on parle de XML-RPC), soit le format standardisé et simple JSON (n'est pas du XML). Le plus souvent les requêtes HTTP sont effectuées depuis du code javascript (AJAX, utilise la classe javascript XMLHttpRequest), afin de ne pas avoir à rafraîchir l'ensemble de la page. Cependant, pour des raisons de sécurité, Ajax (classe XMLHttpRequest) ne permet pas de faire des appels cross-domain, le domaine de l'url appelée via Ajax doit correspondre au même domaine que l'url de la page actuelle (par exemple sur ce site le déroulement des news utilise un appel Ajax vers un fichier php - renvoie le contenu d'une news, qui est ensuite ajouté dynamiquement à une div précédemment cachée -, et ce fichier se trouve sur le même domaine).
Pour contourner cette limitation une astuce a été utilisée, référencée JSONP (JSON with padding). Il s'agit d'ajouter dynamiquement dans la page (via DOM) une balise script de type javascript, dont le source pointe vers l'url de traitement située sur l'autre domaine. Dans ce cas précis l'appel cross-domain est permis. Puisqu'on est dans le cadre d'un appel AJAX il faut que la fin du traitement côté serveur (récupération du contenu d'une news, recherche, etc.) déclenche un callback côté client pour que le rafraîchissement puisse avoir lieu (que le client puisse exploiter le résultat de l'appel).
La seconde astuce consiste à renvoyer, au lieu du contenu au format JSON (ne produirait aucun résultat car appelé dans le contexte d'une balise script), une instruction javascript d'appel de méthode javascript (le fameux callback), avec en paramètre de cette méthode le résultat calculé côté serveur (au format JSON).
Ce sera par exemple myCallBackFunction( { "x": 10, "y": 15} ) si le traitement renvoie un couple de valeurs. Ainsi le résultat de l'appel déclenchera côté client la métode callback javascript (appel possible depuis la balise script), avec le paramètre retour. Afin que le serveur sache qu'elle nom de méthode callback renvoyer dans sa chaîne retour (portion myCallBackFunction), on doit préciser un paramètre additionnel dans l'url d'appel, dont la valeur est le nom de la méthode callback.
Exemples :
http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo
&output=json&callback=callBackResult&query=nomRecherché

http://www.flickr.com/services/rest/?method=flickr.photos.search&tags=tagRecherché&media=photos&machine_tag_mode=any
&format=json&api_key=ca4dd89d3dfaeaf075144c3fdec76756
&jsoncallback=CPJSONPConnectionCallbacks.callbackXX.
Le nom du paramètre stockant le nom de la méthode callback varie suivant le service JSONP appelé (ici Yahoo et Flickr), de même que le paramètre précisant le format (json).
On simule en fait le fonctionnement de l'API XMLHttpRequest, inutilisable ici pour les raisons de sécurité mentionnées (il faudrait se demander s'il n'est pas risqué de contourner cette limitation au final...)

Via le code source de l'application de démo Flickr Photo Demo de Cappuccino, on découvre la classe CPJSONPConnection.
On l'instancie (dans la classe AppController dans l'exemple) via les instructions Objective-J (javascript) suivantes :

var request = [CPURLRequest requestWithURL:"url du service JSONP"];
(l'url contient notamment le paramère précisant le format json)
var connection = [CPJSONPConnection sendRequest: request callback: "jsoncallback" delegate: self];
(pour ce service, Flickr JSONP, le paramètre stockant le nom de la méthode callback est jsoncallback)

Dans le code source de CPJSONPConnection on s'aperçoit que la valeur CPJSONPConnectionCallbacks.callbackXX passée comme nom de callback (plus précisement comme valeur du paramètre jsoncallback - ce nom de paramètre est variabilisé dans la classe CPJSONPConnection) dans l'url d'appel de ce service Flickr est mappée sur une fonction javascript (en fait un élément d'un tableau de méthodes callback) définie comme suit (appelle du code Objective-J - javascript) :
[_delegate connection:self didReceiveData:data];
[self removeScriptTag];
La méthode connection: didReceiveData est donc appelée (interprétation de la chaîne javascript retournée) au final côté client, sur la classe javascript AppController (car défini comme delegate lors de l'appel de CPJSONPConnection sendRequest: callback: delegate: - on avait passé self). Cette méthode delegate, de signature - (void)connection:(CPJSONPConnection)aConnection didReceiveData:(CPString)data, peut ensuite extraire les informations de la chaîne data (String au format JSON, qui correspond au résultat).

Dans le cas d'un appel d'une url sur le même serveur il n'est pas nécessaire de passer par le procédé JSONP, et on peut utiliser la classe XMLHttpRequest, wrappée (masquée) en fait via la classe CPURLConnection (l'appel est bien de type Ajax). On précise l'objet delegate à la création de la CPURLConnection (méthode de classe suivante) :
connectionWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate
(L'url passée est construite comme précédemment via un objet CPURLRequest)
Ici cependant, puisque l'appel sera réalisé au final par la classe XMLHttpRequest (précisera une méthode callback particulière - mais toujours la même) il n'est pas nécessaire de passer au service métier le nom de la méthode callback en paramètre de l'url (cette information est passée lors de l'utilisation de la classe XMLHttpRequest), et ce dernier renverra dans la chaîne résultat simplement le message JSON (pas besoin d'y préfixer le nom de la méthode callback javascript, on sera dans un cas Ajax classique).
Dans l'objet delegate (typiquement l'AppController) il suffit alors d'implémenter la méthode suivante (nom de méthode callback précisée sur l'XMLHttpRequest lorsque l'instance CPURLConnection envoie la requête - méthode start) :
-(void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data

Si le retour (String data) est au format JSON (qu'on soit dans un appel JSONP via une CPJSONPConnection, ou bien un appel Ajax standard via CPURLConnection) il est possible de désérialiser simplement cette chaîne en un objet javascript structuré, via la méthode CPJSObjectCreateWithJSON :
Exemple :
-(void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data
{
var myJSObject = CPJSObjectCreateWithJSON(data);
...
}

Pour une application cliente riche les services métier appelés seront très probablement déployés sur un autre serveur (et autre domaine) que celui servant la page d'interface cliente, et le mécanisme d'appel devra donc être de type JSONP.
Si le service côté serveur est implémenté en php, son retour JSON sera de la forme :
<?=$_GET['jsoncallback']?>( { "greeting":"Hello from request."} )

Thread sur Cappuccino.com
Thread dans un forum Google groups

MAJ : see also the CP2JavaWS Cappuccino client to Java remote services bridge, that allows easy call of remote business services, using provided proxy (client-side) and JSON servlet (server-side). It completely hides the CPJSONPConnection and CPURLConnection classes use, manages encoding/decoding and namespace of services methods’s parameters and return, call of a delegate handlers for success response and fail, in the same way (use syntax) as GWT does.
Commentaires
Poster un commentaire 
  
    
  image de securisation du formulaire


  
      (sera ajouté après validation)