[Objective-c + JSON-RPC] Discuter avec un serveur
yass_1988
Membre
Bonjour,
voila je suis tout nouveau je viens tout juste de m'inscrire sur ce forum. Je suis étudiant et je viens tout juste de me lancer le langage Objecive-c et dans la programmation d'aplication mobile pour Iphone ou j'essaie d'utiliser xCode 4.
Et du coup j'ai un petit problème à vous soumettre, vous comprendrez aussi que je suis tout nouveau, donc soyez un peu indulgent avec moi /smile.png' class='bbc_emoticon' alt=':)' />
Voila, je souhaite poourvoir discuter avec un serveur en utilisant du Json-Rpc, en gros pour le moment, je veux juste me logué dessus recevoir le cookie de session et le token.
Donc dans votre grande générosité serait il possible que quelqu'un me renseigne sur les étapes à faire et si une librairie est nécéssaire ou pas, à part le framework de base sur mon xCode je connais rien d'autre. (et vous comprendrez aussi que mon niveau d'anglais est ....)
Avec si possible un ptit exemple, svp ?
voila je suis tout nouveau je viens tout juste de m'inscrire sur ce forum. Je suis étudiant et je viens tout juste de me lancer le langage Objecive-c et dans la programmation d'aplication mobile pour Iphone ou j'essaie d'utiliser xCode 4.
Et du coup j'ai un petit problème à vous soumettre, vous comprendrez aussi que je suis tout nouveau, donc soyez un peu indulgent avec moi /smile.png' class='bbc_emoticon' alt=':)' />
Voila, je souhaite poourvoir discuter avec un serveur en utilisant du Json-Rpc, en gros pour le moment, je veux juste me logué dessus recevoir le cookie de session et le token.
Donc dans votre grande générosité serait il possible que quelqu'un me renseigne sur les étapes à faire et si une librairie est nécéssaire ou pas, à part le framework de base sur mon xCode je connais rien d'autre. (et vous comprendrez aussi que mon niveau d'anglais est ....)
Avec si possible un ptit exemple, svp ?
Mots clés:
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Ca fait un bail que je l'utilise pour ma propre appli, et ça marche très bien.
Je suis allé voir ton framework et après avoir parcouru tes classes, je suis allé voir ton projet_exemple, et à vrais dire, je n'y ai pas compris grand chose (désolé je débute).
Si possible peux-tu implémenter ton framework avec un prit exemple, style une fonction pour se loguer sur un serveur avec un user et un password et qui me retourne le cookie de connection ?
Cela m'aiderais beaucoup à avancer.
Merci d'avance
Mon framework permet d'échanger avec n'importe quel serveur qui communique en JSON-RPC. Il permet d'envoyer n'importe quel message en JSON-RPC quand tu lui fournis le nom de la méthode à appeler et les paramètres à passer à ladite méthode.
Mais après, chaque serveur propose ses propres méthodes de WebService. Ton serveur peut avoir une méthode de WebService pour retourner une liste d'utilisateurs, ou une méthode de WebService pour te retourner la météo d'une ville donnée... ou une méthode de WebService pour "se loguer avec un login/pass et qu'il te retourne un cookie de connexion". Un autre serveur proposera d'autres méthodes, avec d'autres paramètres, ... ça dépend de ton serveur, je ne sais pas quelles méthodes il expose, quelles sont les noms des méthodes qu'il propose et les paramètres qu'il attend pour ces méthodes, je peux pas le deviner.
C'est un peu comme si je tu avais un parseur XML et que tu me demandais comment parser un XML de liste de livres que tu as récupéré. Je ne sais pas quelle est la structure de ton XML, je ne peux pas le deviner, donc je ne peux pas trop te donner d'exemple, le parseur XML en question étant générique à toi d'indiquer les tags dont tu veux lire le contenu, etc.
bah avec mon framework JSON-RPC c'est pareil, ça permet de communiquer avec un serveur qui respecte le protocole JSON-RPC, après pour appeler une méthode de ton WebService avec mon framework bah encore faut-il connaà®tre le nom de la méthode à appeler et ses paramètres.
Ma méthode pour se logé est la suivante:
{"login":{"envelope":"JSON-RPC-2.0","transport":"POST","parameters":[{"type":"string","name":"s_login","optional":false},{"type":"string","name":"s_password","optional":false},{"type":"boolean","name":"b_remember_me","optional":true,"default":false}],"returns":["null","null"]}
Donc il faut du JSON-RPC version 2.0 et POST en transport, pour les paramètres obligatoire, il faut le login et le passeword,le reste est optionnel et est censé me retourné rien du tout, sinon une erreur. Pour se connecter sur mon serveur, http://IP/auth
Voila, je sais pas si tu peux me faire un ptit exemple avec cette méthode, histoire de mieux appréhender la suite. Je t'en serais reconnaissant.
(cf la doc Doxygen fournie avec le projet, ou même les petites pages de démarrage rapide dans la partie Wiki de mon GitHub, en particulier celle-là ) ? Pourtant je pensais avoir fait une doc assez riche ?
Je dois créer une instance de service, en fournissant l'URL et la version JSON-RPC de mon WebService :
JSONRPCService * service = [JSONRPCService serviceWithURL:"http//IP/auth version:JSONRPCVERSION_2.0];
Et après appelez la méthode de la manière suivante:
[service callMethodWithName:@login parameters:mkArray(@s_login,@s_password)]
Voila, dit moi si je suis sur la bonne voie /huh.gif' class='bbc_emoticon' alt='???' />
Et sinon comment faire pour envoyer également un identifiant à ma méthode, non pas en paramètre de ma méthode, mais un identifiant à ma méthode, (Je ne sais pas si je me suis fait comprendre )
Après une fois que la méthode est appellée, tu peux récupérer le résultat de plusieurs manières :
- soit tu implementes les méthodes de delegate pour recevoir toutes les réponses, quitte à filtrer ensuite en utilisant le paramtres JSONRPCMethodCall qui est passé en delegate et qui indique à quelle méthode et quels paramètres correspond cette réponse
- soit tu utilises le fait que callMethodWithName te retourne un objet JSONRPCResponseHandler, solution que je te conseille. Le principe de cet objet retourné c'est juste de permettre d'indiquer comment va être manipulée/traitée la réponse.
Quand tu récupères un objet JSONRPCResponseHandler, tu peux :
- lui indiquer le delegate et la callback à appeler (un peu sur le même principe que le mécanisme target/action, tu indiques un objet delegate et la méthode/le @selector à appeler quand la réponse est disponible)
- ou plutôt lui indiquer un completionBlock (perso j'adore les blocks, une fois qu'on y est habitué c'est royal ça fait du code propre à lire et sympa, mais si tu n'es pas familier encore avec les blocks je comprend que ça puisse te dérouter), c'est à dire lui indiquer un bloc de code à exécuter quand la réponse sera disponible
- tu peux aussi faire d'autres petits trucs sympas avec ce ResponseHandler, comme par exemple demander à ce que la réponse JSON soit automatiquement convertie en un objet Cocoa de ton modèle plutôt que de récupérer un NSArray ou un NSDictionary... mais bon n'allons pas trop vite.
Du coup le principe pour traiter la réponse, c'est soit tout ton JSONRPCService a un delegate et reçoit toutes les réponses (pas très pratique, mais faisable), soit en retour de l'appel à callMethodWithName:parameters: (ou toute méthode similaire), indiquer à l'objet ResponseHandler retourné comment traiter la réponse :
Par contre du coup, il me reste toujours ce petit problème, voila comme je veux que ma méthode soit identifié ( int id ), je ne sais pas du coup ou le fournir, est ce dans l'instance de service ou dans la méthode a appelé ou ailleurs??
Sinon question un peu con, tu me diras, est ce que pour implémenter ton framework, est ce que vraiment un simple glisser-déposer du dossier dans mon dossier de projet suffis, ou d'autre manipulation sont nécessaire ?
Sinon merci encore pour toute tes précisions /smile.png' class='bbc_emoticon' alt=':)' />
Est ce moi qui est mal fait ?
Sinon pour identifier ta méthode je ne comprend pas trop ce que tu veux faire, et l'utilité que tu en aurais... tu voudrais mettre une sorte de tag sur les appels de méthodes que tu envoies, pour pouvoir le comparer à la réception de la requête par exemple ?
En même temps, si tu utilises les méthodes que je t'ai indiquées pour récupérer la réponse à chaque appel de méthode, en as-tu vraiment besoin ?
Ceci dit, sache qu'en interne chaque appel de méthode JSON-RPC est composée, d'après la spécification JSON-RPC, d'un nom de méthode, d'une liste de paramètres... et d'un "UUID" (identifieur unique). Or tu peux accéder à ce UUID via la propriété uuid de l'objet JSONRPCMethodCall. C'est sans doute avec cela que tu vas pouvoir t'en sortir si tu veux un identifiant unique par appel de méthode.
- Si tu crées toi-même un JSONRPCMethodCall (pour lui indiquer le nom de la méthode et les paramètres, et le passer ensuite à la méthode callMethod) tu peux donc directement accéder au uuid généré pour ce JSONRPCMethodCall.
- Si tu appelles la méthode de commodité "callMethodWithName:parameters" qui construit le JSONRPCMethodCall pour toi, elle te retourne un objet JSONRPCResponseHandler comme je t'ai déjà expliqué, et cet objet contient une propriété te permettant d'accéder au JSONRPCMethodCall, et donc de là de récupérer le uuid utilisé.
Si tu utilises ARC dans ton projet (et donc que tu vas avoir plein de messages concernant les appels à retain/release), il faut que tu désactives ARC pour les fichiers de mon framework (Rajouter le flag "-fno-objc-arc" pour les fichiers de mon framework, dans l'onglet "Build Phases" des réglages du projet sous Xcode4, colonne "Compiler Flags" de la section "Compile Sources")
//
// main.m
// test_connection
//
// Created by Yassine Badih on 05/04/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "JSONRPCService.h"
#import "JSONRPCMethodCall.h"
#import "JSONRPCResponseHandler.h"
int main(int argc, char *argv[])
{
NSURL * serviceURL = /color][color=#7134aa]NSURL[/color][color=#000000] [/color][color=#401082]URLWithString[/color][color=#000000]:[/color]@"http://***.***.***.***/auth/rpc.php"[color=#000000;
JSONRPCService * service = /color][color=#578187]JSONRPCService[/color][color=#000000] [/color]serviceWithURL[color=#000000]:serviceURL [/color]version[color=#000000]:[/color]JSONRPCVersion_2_0[color=#000000;
NSLog(@blabla);
[[service callMethodWithNameAndParams:@login,@admin,@123456, nil]
completion:^(JSONRPCMethodCall * methodCall,id result,NSError * error)
{
NSLog(@La méthode de login %@ vient de répondre. Le résultat est : %@, l'erreur est : %@", methodCall, result, error);
}];
NSLog(@blabla);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass(/color][color=#578187]AppDelegate[/color][color=#000000] [/color]class[color=#000000));
}
}
objc[949]: Object 0x6878e10 of class NSURL autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
objc[949]: Object 0x6879160 of class JSONRPCService autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
2012-04-05 11:06:02.736 test_connection[949:f803] blabla
2012-04-05 11:06:02.758 test_connection[949:f803] blabla
2012-04-05 11:06:03.249 test_connection[949:f803] La méthode de login <JSONRPCMethodCall login(admin,123456)> vent de répondre. Le résultat est : (null), l'erreur est : (null)
Par contre normalement, qu'on je suis censé me logé avec la méthode login, elle est censé me renvoyé nulle en résultat (Visiblement c'est bon), mais je dois aussi recevoir un cookie de session
Du coup ça n'a rien à voir avec mon framework (tu mettrais du code à toi là dedans au aurais aussi ce genre d'erreur)
En général dans un programme Cocoa, on ne touche jamais au main.m. On laisse le main.m créer une autoreleasepool et appeler UIApplicationMain, comme c'est fait par défaut, qui va s'occuper de tout plein de trucs nécessaires (préparer une RunLoop, charger ton interface principale, initialiser les classes donc la UIApplication et son delegate, etc).
Du coup, en Cocoa, si tu veux exécuter du code au lancement de ton application, ne ne faut pas les placer dans le main, mais laisser le UIApplicationMain faire son boulot, et mettre le code dans le delegate de ta UIApplication (dans AppDelegate.m donc), dans la méthode "applicationDidFinishLaunchingWithOptions:" qui elle est appelée quand l'initialisation est finie et ton programme est prêt.
C'est possible ? Comment ?
(Le WebService que tu attaques n'est donc pas forcément conforme à 100% à la spec donc, mais bon, passons)
Par contre normalement les cookies sont gérés automatiquement par le WebKit, tu ne devrais donc pas avoir à t'en soucier. Quand tu reçois des cookies dans les headers de la réponse d'une requête, le WebKit les enregistre dans le HTTPCookieStorage pour les garder de côté naturellement.
Tu devrais donc pouvoir récupérer les cookies directement via [[[font=Courier, Consolas, monospace]NSHTTPCookieStorage [/font][font=Courier, Consolas, monospace]sharedHTTPCookieStorage] [/font][font=Courier, Consolas, monospace]cookiesForURL:service.serviceURL][/font] pour aller lire tous les cookies associés à ton service.
Merci pour tes conseils et ton framework (Bien pratique comparé à DefferedKit et surtout plus facile à prendre en main).
j'espère que vous serez tirs à l'écoute si d'autre soucis se présenter /smile.png' class='bbc_emoticon' alt=':)' />
Dossier ViewController.m:
#import "ViewController.h"
#import "JSONRPC.h"
#import "JSONRPC_Extensions.h"
#import "JSONRPCService.h"
#import "JSONRPCMethodCall.h"
#import "JSONRPCResponseHandler.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
/color][color=#b41ca4]super[/color][color=#000000] [/color]viewDidLoad[color=#000000;
NSURL * serviceURL = /color][color=#7134aa]NSURL[/color][color=#000000] [/color][color=#401082]URLWithString[/color][color=#000000]:[/color]@"http://***.***.***.***/auth/rpc.php"[color=#000000;
JSONRPCService * service = /color][color=#578187]JSONRPCService[/color][color=#000000] [/color]serviceWithURL[color=#000000]:serviceURL [/color]version[color=#000000]:[/color]JSONRPCVersion_2_0[color=#000000;
service [color=#38595d]callMethodWithNameAndParams[/color]:[color=#c72c25]@"login"[/color], [color=#c72c25]@"admin"[/color], [color=#c72c25]@"*****"[/color], [color=#b41ca4]nil[/color completion:^(JSONRPCMethodCall * methodCall,id result,NSError * error)
{
NSArray * tab = [/color][color=#7134aa]NSHTTPCookieStorage[/color][color=#000000] [/color]sharedHTTPCookieStorage[color=#000000 cookiesForURL:service.serviceURL];
int i=[tab count];
for(int j=0;j<=i;j++)
{
NSHTTPCookie * Cookie;
Cookie = [tab objectAtIndex:j];
if (Cookie [color=#401082]name[/color isEqualToString:@VHTSESSION])
{
NSLog(@" Voila la valeur du cookie: %@",[Cookie value]);
service [color=#38595d]callMethodWithNameAndParams[/color]:[color=#c72c25]@"get_token"[/color], [color=#b41ca4]nil[/color completion:^(JSONRPCMethodCall * methodCall,id result,NSError * error)
{
NSString * resultat = result;
NSLog(@Voila la valeur du token: %@",resultat);
}
];
}
}
}
];
/// Do any additional setup after loading the view, typically from a nib.
}
voici le résultat sur le terminal :
2012-04-12 15:03:47.845 vht2[2691:f803] Voila la valeur du cookie: 3go84vadpbol9gngaisp0g40s0
2012-04-12 15:03:47.848 vht2[2691:f803] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(0x13dc022 0x156dcd6 0x13c8d88 0x2721 0x9e22 0xa29a49 0xa27e84 0xa28ea7 0xa27e3f 0xa27fc5 0x96cf5a 0x39afa39 0x3a7c596 0x39a6120 0x3a7c117 0x39a5fbf 0x13b094f 0x1313b43 0x1313424 0x1312d84 0x1312c9b 0x12c57d8 0x12c588a 0x26626 0x220d 0x2175)
terminate called throwing an exception(lldb)
Donc en gros il récupère bien le cookie que je souhaite, mais il s'arrête et veux pas appeler la méthode get_token, qui est pourtant correctement appelé par moi.
Comprend pas /huh.gif' class='bbc_emoticon' alt='???' />??,
Le problème viens de ta boucle...
Tu lui dis ici d'incrémenter j tant qu'il est inférieur ou égal à i. i quant à lui représente le nombre d'objet total dans le tableau... Un tableau en informatique ça commence à 0 donc la borne supérieur est toujours le compte -1...
Tu aurais du écrire j<i.
Ok je me cache !!!!!!!!!!!!!!!
voila cette fois ci je souhaite définir tout ce que j'ai fait la-dessus dans une méthode qui me retournera un tableau contenant la valeur de mon cookie de session et de mon token.
- (NSArray *) login
{
NSString * http=@"http://";
NSString * suite=@/auth/rpc.php;
NSString * lien=[NSString stringWithFormat:@%@%@%@",http,self.adre,suite];
NSURL * serviceURL = [NSURL URLWithString:lien];
JSONRPCService * service = /color][color=#578187]JSONRPCService[/color][color=#000000] [/color]serviceWithURL[color=#000000]:serviceURL [/color]version[color=#000000]:[/color]JSONRPCVersion_2_0[color=#000000;
service [color=#38595d]callMethodWithNameAndParams[/color]:[color=#c72c25]@"login"[/color], [color=#b41ca4]self[/color].[color=#7134aa]log[/color], [color=#b41ca4]self[/color].[color=#7134aa]pwd[/color], [color=#b41ca4]nil[/color completion:^(JSONRPCMethodCall * methodCall,id result,NSError * error)
{
NSArray * tab = [/color][color=#7134aa]NSHTTPCookieStorage[/color][color=#000000] [/color]sharedHTTPCookieStorage[color=#000000 cookiesForURL:service.serviceURL];
int i=[tab count];
for(int j=0;j<i;j++)
{
NSHTTPCookie * Cookie;
Cookie = [tab objectAtIndex:j];
if (Cookie [color=#401082]name[/color isEqualToString:@VHTSESSION])
{
NSString * resultat1;
resultat1=color=#7134aa]NSString[/color] [color=#401082]stringWithFormat[/color]:[Cookie [color=#401082]value[/color];
service [color=#38595d]callMethodWithNameAndParams[/color]:[color=#c72c25]@"get_token"[/color], [color=#b41ca4]nil[/color completion:^(JSONRPCMethodCall * methodCall,id result,NSError * error)
{
NSString * resultat2;
resultat2=[NSString stringWithFormat:result];
}
];
}
}
}
];
NSArray * tabLogin = [NSArray arrayWithObjects:resultat1, resultat2, nil];
return tabLogin;
}
du coup il m'affiche pas mal d'erreur, le premier qui me dit que je ne peux pas utiliser la méthode value sur mon objet Cookie, car cette méthode est déjà défini sur d'autre objet de mon framework de base et aussi à l'avant dernière ligne ou je défini mon tableau tabLogin que l'utilisation de mes identificateurs ne sont pas déclaré.
/huh.gif' class='bbc_emoticon' alt='???' />
Le second problème par contre c'est normal. Tu déclares resultat1 et resultat2 dans des blocs entre accolades, donc ces variables n'existent qu'à l'intérieur du bloc de variables. Ca c'est du C de base. Exemple : Du coup il faut que tu déclares les variables que tu veux utiliser au bon endroit (à l'extérieur de ton bloc "if" dans mon exemple ci-dessus) pour qu'elles aient une visibilité pour tout le reste de la fonction et pour pouvoir y accéder à la fin de ta fonction aussi.
Bon ensuite une fois que tu auras fait ça, tu auras un autre problème, non plus lié à du C de base, mais au blocks Objective-C, petite particularité des blocks ObjC qui capturent les variables, et qui font que par défaut tu ne peux pas modifier à l'intérieur d'un block "^{ ... }" (comme ceux que tu mets en paramètre de "completion:") une variable qui a été déclarée à l'extérieur de ce block. Si tu veux pouvoir modifier ces variables déclarées à l'extérieur d'un block et capturées par ce block, il faut les déclarer avec le mot clé "__block" devant.
- (NSArray *) login
{
NSString * http=@"http://";
NSString * suite=@/auth/rpc.php;
NSString * lien=[NSString stringWithFormat:@%@%@%@",http,self.adre,suite];
NSURL * serviceURL = [NSURL URLWithString:lien];
__block NSString * valCookie;
__block NSString * valToken;
JSONRPCService * service = /color][color=#578187]JSONRPCService[/color][color=#000000] [/color]serviceWithURL[color=#000000]:serviceURL [/color]version[color=#000000]:[/color]JSONRPCVersion_2_0[color=#000000;
service [color=#38595d]callMethodWithNameAndParams[/color]:[color=#c72c25]@"login"[/color], [color=#b41ca4]self[/color].[color=#7134aa]log[/color], [color=#b41ca4]self[/color].[color=#7134aa]pwd[/color], [color=#b41ca4]nil[/color completion:^(JSONRPCMethodCall * methodCall,id result,NSError * error)
{
NSArray * tab = [/color][color=#7134aa]NSHTTPCookieStorage[/color][color=#000000] [/color]sharedHTTPCookieStorage[color=#000000 cookiesForURL:service.serviceURL];
int i=[tab count];
for(int j=0;j<i;j++)
{
NSHTTPCookie * Cookie;
Cookie = [tab objectAtIndex:j];
if (Cookie [color=#401082]name[/color isEqualToString:@VHTSESSION])
{
valCookie=[Cookie value];
service [color=#38595d]callMethodWithNameAndParams[/color]:[color=#c72c25]@"get_token"[/color], [color=#b41ca4]nil[/color completion:^(JSONRPCMethodCall * methodCall,id result,NSError * error)
{ // Too many arguments to method call, expected 1, have 2
valToken=[NSString stringWithFormat:result];
}
];
}
}
}
];
NSArray * tabLogin = color=#7134aa]NSArray[/color] [color=#401082]arrayWithObjects[/color]:valCookie, valToken, [color=#b41ca4]nil[/color;
return tabLogin;
Une idée ?
2012-04-17 13:56:48.674 vht2[1080:f803] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 0 beyond bounds for empty array'
*** First throw call stack:
(0x13de022 0x156fcd6 0x13ca644 0xbbf7 0x2bbb 0xefa1e 0x4e401 0x4e670 0x4e836 0x5572a 0x26596 0x27274 0x36183 0x36c38 0x2a634 0x12c8ef5 0x13b2195 0x1316ff2 0x13158da 0x1314d84 0x1314c9b 0x26c65 0x28626 0x28ed 0x2855)
terminate called throwing an exception(lldb)