Récuperer la valeur retournée après un HTTP POST

yodarkyodark Membre
05:49 modifié dans API AppKit #1
Bonjour,

Ma question est très simple, comment faire pour récuperer le contenu d'une page web retrounée après un POST ? J'ai le code source suivant :

httpBodyString=[[NSMutableString alloc] initWithFormat:@&quot;radiobutton=1&amp;boutton=1&amp;rndnocheck=0&quot;];<br />urlString=[[NSMutableString alloc] initWithString:@&quot;www.site.com&quot;];<br />	url=[[NSURL alloc] initWithString:urlString];<br />	[urlString release];<br />	<br />	NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];<br />	[url release];<br />	[urlRequest setHTTPMethod:@&quot;POST&quot;];<br />	[urlRequest setHTTPBody:[httpBodyString dataUsingEncoding:NSISOLatin1StringEncoding]];<br />	[httpBodyString release];<br />	<br />	NSURLConnection *connectionResponse = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];<br />	if (!connectionResponse)<br />	{<br />		NSLog(@&quot;Failed to submit request&quot;);<br />	}<br />	else<br />	{<br />		NSLog(@&quot;Request submitted %@&quot;,connectionResponse);<br />		<br />	}<br />

la variable connectionResponse ne contient pas le retour HTTP de ma requete. Je voudrais pouvoir récuperer le toute dans un String

Réponses

  • FloFlo Membre
    05:49 modifié #2
    Pourquoi ne pas utiliser la méthode de NSString :
    <br /> – initWithContentsOfURL:encoding:error:&nbsp; <br />
    


    ça te met le contenu de la page dans ton NSString que tu peux ensuite parser.
  • yodarkyodark Membre
    05:49 modifié #3
    Oui c'est cette méthode que je voulais utiliser. Mais comment faire pour poster des variables en même temps que la requête ? Je vois bien comment envoyer un GET il suffirait de faire

    initWithContentsOfURL:@www.site.com?getvariable=mavariable encoding:error: 

    Comment l'utiliser avec POST?
  • NoNo Membre
    décembre 2008 modifié #4
    dans 1228927384:

    Ma question est très simple, comment faire pour récuperer le contenu d'une page web retrounée après un POST ?

    Puisque d'après ton code, tu utilises NSURLConnection en asynchrone, alors il faut que la méthode delegate connection:didReceiveResponse: soit implémentée.
    Ce delegate est appelé quand la réponse au POST est reçue.

    En paramètre, cette méthode reçoit un argument de classe NSHTTPURLResponse par lequel tu pourras récupérer le contenu de la réponse HTTP au POST.
  • schlumschlum Membre
    05:49 modifié #5
    Vala... Ou alors un "sendSynchronousRequest:returningResponse:error" qui renvoie directemement un NSData* avec toute la réponse d'un coup  :P
  • yodarkyodark Membre
    05:49 modifié #6
    j'ai de la peine a comprendre comment utiliser

    sendSynchronousRequest:returningResponse:error


    Je dois mettre quoi dans returningResponse ? Je dois quand même initer NSURLConnection ? "connectionResponse" ne répond pas à  cette méthode sendSynchronousRequest
  • schlumschlum Membre
    05:49 modifié #7
    dans 1229019165:

    j'ai de la peine a comprendre comment utiliser

    sendSynchronousRequest:returningResponse:error


    Je dois mettre quoi dans returningResponse ? Je dois quand même initer NSURLConnection ? "connectionResponse" ne répond pas à  cette méthode sendSynchronousRequest


    Tu l'appelles avec :
    NSURLResponse *response = nil;<br />NSError *err = nil;<br />NSData *result = [NSURLConnection sendSynchronousRequest: urlRequest returningResponse:&amp;response error:&amp;err];
    


    (tu t'en fous, ce sont des objets qui sont auto-released si définis lors du processus)
    Il n'y a pas de NSURLConnection à  initier, c'est une méthode de classe (donc qui ne s'appelle pas sur une instance !)
  • muqaddarmuqaddar Administrateur
    05:49 modifié #8
    Bonjour,

    J'essaie de me connecter à  un site, la connexion marche, mais la page retournée par le site (NSData) me dit que je n'ai pas saisi mon identifiant...

    // build the request<br />	NSString *loginURLString = @&quot;http://www.mysite.com&quot;;<br />	NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:loginURLString]];<br />	[urlRequest setHTTPShouldHandleCookies:YES];<br />	[urlRequest setHTTPMethod:@&quot;POST&quot;];<br />	[urlRequest setValue:login forHTTPHeaderField:@&quot;user&quot;];<br />	[urlRequest setValue:password forHTTPHeaderField:@&quot;passwrd&quot;];<br />	NSLog(@&quot;%@&quot;, urlRequest);<br />	// connect to try login<br />	NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self startImmediately:YES];	<br />	[connection release];
    


    et

    <br /># pragma mark	delegate methods<br /><br />- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {<br />	NSLog(@&quot;%@&quot;, connection);<br />	NSLog(@&quot;reponse : %@&quot;, [response URL]);	<br />	<br />}<br /><br />- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {<br />	NSLog(@&quot;data : %@&quot;, [[NSString alloc]initWithData:data encoding:NSISOLatin1StringEncoding]);<br />}<br /><br />- (void)connectionDidFinishLoading:(NSURLConnection *)connection {<br />	[activityIndicatorView stopAnimating];	<br />}
    


    Est-ce que je saisi bien le user et le password dans mon urlRequest ?
    login et password sont bien entendu saisis correctement.
  • schlumschlum Membre
    05:49 modifié #9
    C'est bien "passwrd" ? et pas "password" ?
  • AliGatorAliGator Membre, Modérateur
    avril 2009 modifié #10
    Tout dépend du site en question, mais en effet avec ton code tu n'envoies pas le login et le mot de passe dans tes données POST alors que j'imagine que c'est ce que tu souhaites faire, non ?

    En effet tu définis les valeurs pour "login" et "passwrd"... en tant que "HTTPHeaders", or les headers c'est pour décrire ta requête du genre son Content-Type, sa méthode GET ou POST, ... mais pas les données que tu envoies.
    Il faut donc si tu veux passer les données en POST que tu passes une chaà®ne du genre "login=....&passwrd=..." dans le body de ta requête, via un [tt][urlRequest setHTTPBody:...][/tt].

    Après pour construire ce body proprement, faut juste faire gaffe au échappements de caractères. Le plus simple étant de faire :
    NSString* postBody = [NSString stringWithFormat:@&quot;login=%@&amp;passwrd=%@&quot; ,<br />    [login stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],<br />    [password stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]  ];<br /><br />[urlRequest setHTTPBody: [postBody dataUsingEncoding:NSUTF8StringEncoding]];
    


    Même si je me demande s'il n'existe pas dans la classe NSDicitonary une façon de demander la représentation "key1=value1&key2=value2&..." typique de l'utilisation dans des URLs ou des données POST, car dans ce cas ça serait plus propre de construire le dictionary et récupérer cette forme encodée à  partir de ce dernier... Mais bon pour ton besoin ça devrait suffire.
  • muqaddarmuqaddar Administrateur
    avril 2009 modifié #11
    dans 1240480538:

    C'est bien "passwrd" ? et pas "password" ?


    Oui c'est bien passwrd.

    dans 1240480712:

    Tout dépend du site en question, mais en effet avec ton code tu n'envoies pas le login et le mot de passe dans tes données POST alors que j'imagine que c'est ce que tu souhaites faire, non ?


    C'est exactement ce que je veux faire. AH bein je viens de voir ta réponse !
    Je trouve cela vraiment plus logique, je me demandais le rapport entre les headers et les variables envoyées qui font effectivement partie du body !

    Je teste et je te tiens au courant.
    Merci Ali !
  • muqaddarmuqaddar Administrateur
    05:49 modifié #12
    C'est bon, ça marche nikel !
    Merci encore. :)
  • muqaddarmuqaddar Administrateur
    05:49 modifié #13
    Un peu de POO maintenant. :-)

    Pour faire les choses proprement, je me suis taillé un Controller de loging (URLLoggerController) vers un site Internet qui se charge donc d'envoyer la requête POST et qui contient aussi les méthodes delegate de NSURLCONNECTION.

    Maintenant, au moment d'erreurs ou autre dans ces méthodes delegate, je voudrais envoyer des messages à  la classe qui instancie ce nouveau controlleur et qui le nourrit.

    Par exemple, quand un utilisateur est connecté, ça ne suffit pas de passer une variable d'instance (un BOOL) à  YES dans le contrôleur de loging.
    En effet, la méthode qui vérifie son état est appelée trop tôt dans mon code de la classe principale. En fait, elle doit être appelée pendant que le code de URLCONNECTION s'éxecute dans un autre thread en parallèle je pense...?
    Du coup, ma classe principale ne voit pas le BOOL à  YES.

    J'ai donc pensé (de mémoire) à  utiliser le NotificationCenter qui pourrait poster une notification depuis ma méthode delegate et observer ça dans la classe principale. Je crois aussi de mémoire qu'il est gourmand en mémoire le notificationCenter...

    Bon, en fait, je voulais savoir si c'était une bonne façon de faire.
    Merci.
  • AliGatorAliGator Membre, Modérateur
    05:49 modifié #14
    Je vois 2 solutions "propres" possibles à  ton problème :
    1) Les notifications
    2) Les delegates

    Sachant qu'il n'est pas rare non plus que dans les frameworks Apple on trouve les 2 combinés, ou une méthode de delegate qui par défaut si non implémentée émet une notification par exemple.

    Perso je ne suis pas gros utilisateur des NSNotifications, je trouve plus joli le paradigme delegate. Rien ne t'empêche donc de te créer ton propre delegate, je fais ça souvent pour ma part, et en plus avec des protocoles formels (j'aime pas les protocoles informels, en plus c'est has-been ça a été créé juste parce qu'à  l'époque y'avait pas les mots clés @optional, mais maintenant que ça existe en Obj-C 2, j'utilise que des protocoles formels c'est 100x plus propre)


    En gros ça donne qqch comme ça :
    1) Pour ta classe URLLoggerController
    // .h :<br />@protocol URLLoggerControllerDelegate<br />-(void)URLLoggerController:(URLLoggerController*)loggerCtrl didConnectSuccessfully:(BOOL)success;<br />@end<br /><br />@interface URLLoggerController<br />{<br />&nbsp; id&lt;URLLoggerControllerDelegate&gt; delegate;<br />&nbsp; ...<br />}<br />@property(nonatomic, assign) id&lt;URLLoggerControllerDelegate&gt; delegate;<br />-(void)connectWithLogin:(NSString*)login password:(NSString*)password;<br />...<br />@end<br /><br />// .m<br />@implementation URLLoggerController<br />-(void)methodeDelegateDeNSURLConnectionQuandSuccess<br />{<br />&nbsp; [delegate URLLoggerController:self didConnectSuccessfully:YES];<br />}<br /><br />-(void)methodeDelegateDeNSURLConnectionQuandError<br />{<br />&nbsp; [delegate URLLoggerController:self didConnectSuccessfully:NO];<br />}<br />@end
    


    2) Puis dans la classe qui instancie ton URLLoggerController :
    // .h :<br />#import &quot;URLLoggerController.h&quot;<br />@interface MyMachin : ParentClass &lt;URLLoggerControllerDelegate&gt;<br />{<br />&nbsp; BOOL isConnected;<br />&nbsp; URLLoggerController* myURLLoggerController;<br />}...<br />@end<br /><br />// .m :<br />-(IBAction)connect<br />{<br />&nbsp;  ...<br />&nbsp;  myURLLoggerController = [[URLLoggerController alloc] init];<br />&nbsp;  ...<br />&nbsp;  myURLLoggerController.delegate = self;<br />&nbsp;  [myURLLoggerController connectWithLogin:... password:...];<br />&nbsp;  ...<br />}<br />-(void)URLLoggerController:(URLLoggerController*)loggerCtrl didConnectSuccessfully:(BOOL)success<br />{<br />&nbsp; isConnected = success; // mise à  jour de ta variable d&#39;instance isConnected<br />&nbsp; // + affichage NSAlert d&#39;erreur if (!success)<br />}
    
  • muqaddarmuqaddar Administrateur
    05:49 modifié #15
    Ok, j'ai fait comme toi.
    J'ai ajouté mon protocole, mon objet delegate, et ma méthode delegate.
    Cela me paraà®t plus "objet" que la notification.

    Juste une chose :

    J'ai dû viré les signatures de "id delegate" avec le protocole dans le .h de URLLoggerController. En effet, je n'ai pas trouvé d'astuce me permettant de décrire le protocole avant @interface, car dans ce cas-là  il ne trouve pas la classe URLLoggerController qui est déclarée après... et qui est utilisée dans la déclaration de la méthode du protocole.

    Donc j'ai déclaré le protocole après @interface, ce qui donne au final :

    #import &lt;Foundation/Foundation.h&gt;<br /><br />@interface URLLoggerController : NSObject {<br />	id delegate;<br />	<br />	BOOL userIsLogged;<br />	NSString *urlString, *loginString, *passwordString, *varsChainString, *errorURLString;<br />}<br /><br />@property (nonatomic, assign) id delegate;<br /><br />@property (nonatomic) BOOL userIsLogged;<br />@property (nonatomic, retain) NSString *urlString, *loginString, *passwordString, *varsChainString, *errorURLString;<br /><br />- (id)initWithBaseURL:(NSString *)anURLString login:(NSString *)aLogin password:(NSString *)aPassword <br />	varschain:(NSString *)aVarsChain errorLoginURL:(NSString *)anErrorURLString;<br /><br />- (void)tryToLogin;<br /><br />@end<br /><br />@protocol URLLoggerControllerDelegate<br />- (void)URLLoggerController:(URLLoggerController*)loggerCtrl didConnectSuccessfully:(BOOL)success;<br />@end
    


    Note bien, que j'ai dû virer <URLLoggerControllerDelegate> après "id" puisqu'il ne le trouve pas avant...
  • AliGatorAliGator Membre, Modérateur
    avril 2009 modifié #16
    C'est simple, j'ai juste oublié de déclarer que la classe URLLoggerController existe, avant de l'utiliser dans la définition du protocole.

    Il suffit (astuce que tu ne semblais pas connaà®tre donc :)) de rajouter ceci juste avant de déclarer le @protocol URLLoggerControllerDelegate :
    @class URLLoggerController;
    
    et du coup tu pourras remettre la déclaration du @protocol avant la déclaration de @interface URLLoggerController... et donc remettre les déclarations du delegate avec id<URLLoggerControllerDelegate> et non plus juste "id".


    Bon en fait j'ai fait exprès de pas le mettre de suite ce @class, pour que tu réalises l'erreur et comprenne mieux du coup l'intérêt et l'astuce de le rajouter... (comment ça c'est pas crédible ? :))
  • muqaddarmuqaddar Administrateur
    05:49 modifié #17
    Génial.
    J'avais déjà  vu ce @class fût un temps, mais je ne me souvenais pas de sa fonction par rapport à  un #import.

    En tout cas, merci.

    Sinon, je vais p-e passer sur une requête synchrone, ça paraà®t plus logique pour un logging. Puisque de toute façon, je ne peux pas faire le reste sans être logué. N'est-ce pas ? ;)

  • AliGatorAliGator Membre, Modérateur
    avril 2009 modifié #18
    C'est malin, tout ça pour ça alors ? ;)

    Heu ceci dit l'inconvénient d'une requête synchrone c'est qu'elle te bloque ton interface le temps de l'accès au réseau etc... donc pas si intéressant que ça.

    De toute façon c'est plutôt à  la partie interface de gérer cette partie "je peux rien faire tant que je suis pas loggué", désactivant des contrôles tant que tu ne t'es pas logué, par exemple...

    Donc moi je garderai l'aspect asynchrone du logging, qui peut te permettre par exemple d'afficher une roue qui tourne pour montrer l'activité (...hum... :)) en attendant que le site te réponde si le logging a marché ou pas. Quitte quand tu veux faire une requête à  ton site pour laquelle tu sais que tu as besoin d'une connection, si isConnected=NO alors tu mémorises la requête dans une variable d'instance tampon, et tu demandes le logging... et une fois le logging effectué avec susscès, tu lances ta requête mémorisée dans ton ivar tampon pour l'exécuter automatiquement après le logging, enfin ce genre d'astuce quoi.
  • muqaddarmuqaddar Administrateur
    05:49 modifié #19
    Ah bein là  c'est exactement ce que je suis en train de faire pour le coup !
    Allé au boulot !

    Pour la roue qui tourne, c'est déjà  fait. ;-)
Connectez-vous ou Inscrivez-vous pour répondre.