In my application, i have a button wich calls an ASIHTTPRequest. The request goes fine and i receive an response string. But when the request finishes, it always goes to the method: requestFinished. And i also got a requestFailed method. But even when i give a wrong link, the request finsihes and never fails.. Why is this? This is my code:
-(void)fetchForm:(id)sender {
NSURL *URL=[NSURL URLWithString:#"http://www.mydomain.nl/testGet.php"];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:URL] autorelease];
[request setDelegate:self];
[request setDidFailSelector:#selector(requestFailed:)];
[request startAsynchronous];
}
-(void)requestFinished:(ASIHTTPRequest *)request {
NSLog(#"Request Success!");
}
-(void)requestFailed:(ASIHTTPRequest *)request {
NSLog(#"Request Failed!");
}
EDIT:
I read a little documentation on the ASIHTTPRequest website. And i came to the conclusion that i need to see for myself if there is an error code. i do this with:
int statusCode = [request responseStatusCode];
if (statusCode == 404) {
NSLog(#"Statuscode 404 has occured!");
}
There are several conditions that might affect error reporting with HTTP requests. From ASIHTTP's viewpoint, if the request object can be successfully built, sent, and a some kind of response is received, then everything is ok.
In my case, for example, my ISP has a proxy that will return an error page with many not existing URLs and sometimes even with ill-formed URLs. In such cases, ASIHTTP will not fail. I don't know if this is also your case, but it was for me.
If you look at the file ASIHTTPRequest.m and search for failWithError, you will see all the cases where ASIHTTP will trigger the mechanism that leads to the didFailSelector to be called. You might even set a breakpoint in the failWithError method to see if it is called.
EDIT:
In a sense ASIHTTPRequest mechanism is very basic and covers failures at the network level. If you receive a response then it is an application level failure and you have to deal with it.
First thing is checking the HTTP status code:
int statusCode = [request responseStatusCode];
NSString *statusMessage = [request responseStatusMessage];
This will allow you to identify 404, 500, and so on.
If this does not work and the server does not send an error code, then the only way to go about it is parsing the response you receive and, if it does not contain the data you were waiting for, fail.
try this one -
[request setDidFailSelector:#selector(requestFailed:)];
try this:-
[request setDidFinishSelector:#selector(requestFinished:)];
[request setDidFailSelector:#selector(requestFailed:)];
Write these two lines and then try.
Related
I'm trying to use an ASIFormDataRequest in my iPhone application to send a video I have just recorded to a server, along with a string that I can use to ID who the video belongs to. The code for doing so is here:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *urlvideo = [info objectForKey:UIImagePickerControllerMediaURL];
NSString *urlString=[urlvideo path];
NSLog(#"urlString=%#",urlString);
NSString *str = [NSString stringWithFormat:#"http://www.mysite.com/videodata.php"];
NSURL *url = [NSURL URLWithString:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
NSData *patientData = [_patientCode dataUsingEncoding:NSUTF8StringEncoding];
[request setFile:urlString forKey:#"video"];
[request setData:patientData forKey:#"patientcode"];
[request setRequestMethod:#"POST"];
[request setDelegate:self];
[request startSynchronous];
NSLog(#"responseStatusCode: %i",[request responseStatusCode]);
NSLog(#"responseString: %#",[request responseString]);
[picker dismissViewControllerAnimated:YES completion:nil];
}
Everything seems to work correctly, I get back a status code of 200 and the method finishes as expected. However, nothing seems to be received by the php file on the server. I added this line to my server-side php code:
echo(count($_POST));
This returns 0, so it seems as though nothing is actually getting posted by the ASIFormDataRequest. I feel like there might be some simple step I am missing as I have never used the ASIFormDataRequest before, but any help would be greatly appreciated.
EDIT: I changed the setData:patientData to setPostValue:_patientCode and now that part of the post is getting sent correctly, so it seems as though setPostValue works but setData and setFile do not.
You should use -addData:forKey: and -addFile:forKey: instead of -setData:forKey: and -setFile:forKey:.
Other than that, check the debug output when you compile with DEBUG_FORM_DATA_REQUEST.
You should be sending a multipart/form-data request when using -addFile:forKey: and a application/x-www-form-urlencoded request when using -addPostValue:forKey:.
Does your server handle multipart/form-data requests?
I ended up finding the answer while reading something else and noticing that they did something interesting: The file I uploaded was simply going into the $_FILES array, not the $_POST array. I also changed [request setData:patientData forKey:#"patientcode"]; to [request setPostValue:_patientCode forKey:#"patientCode"]; and then that appeared in the $_POST array as I wanted so I was able to get both the string I was sending and the file I was sending in my php script.
I would like to send a file asynchronously to the server; however, it seems like when I do send the request to the server. The server gives me a HTTP code: 200, which is OK but no file is being uploaded to the server.
However, when I leave it to synchronous... it works perfectly. Weird..
Any ideas would be greatly appreciated,
- (void) sendCSVtoServer: ( Session * ) archive_session {
NSLog(#"file name: %#", [archive_session getFile]);
NSURL *url = [NSURL URLWithString:#"http://xx.x.xxx.xxx:3000/xxx/xxxxxxxx"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue: [archive_session getEmail] forKey:#"email"];
[request addFile: [archive_session getFile] forKey:#"csv"];
[request setDelegate:self];
[request startSynchronous];
}
Thanks!
You are setting a delegate, but you mention you did not implement any of those methods. ASIFormDataRequest extends ASIHTTPResquest, and therefore inherits all its properties.
You will have to implement methods decalred in ASIHTTPRequestDelegate, at least
- (void)requestFinished:(ASIHTTPRequest *)request;
and
- (void)requestFailed:(ASIHTTPRequest *)request;
Maybe others depending on your needs.
Your upload is probably still working (can you check your server?), but, when you send the request asynchronously, you don't have any way to determine if the request has succeeded or failed; right now you are just sedning and forgetting.
All the methods declared in ASIHTTPRequestDelegate.h are marked as optional, which is why the debugger, compiler, and runtime is not complaining.
How to access the (POST)data sent with the request from the requestFailed/requestFinished function.
- (void) abc {
NSString *postString = #"john";
NSURL *url = [NSURL URLWithString:#"http://abc.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setPostValue:postString forKey:#"name"];
[request setDelegate:self];
[request startAsynchronus];
}
- (void) requestFinished:(ASIHTTPRequest *)request
{
// Question is whether the request holds the sent post values.
// If it holds. how can we access them.
// i tried using [request valueForKey:#"name"];
// but it won't work.
}
Handling success and failure for multiple requests in delegate methods
If you need to handle success and failure on many different types of
request, you have several options:
If your requests are all of the same broad type, but you want to
distinguish between them, you can set the userInfo NSDictionary
property of each request with your own custom data that you can read
in your finished / failed delegate methods. For simpler cases, you can
set the request’s tag property instead. Both of these properties are
for your own use, and are not sent to the server.
If you need to handle success and failure in a completely different way for each
request, set a different setDidFinishSelector / setDidFailSelector for
each request For more complex situations, or where you want to parse
the response in the background, create a minimal subclass of
ASIHTTPRequest for each type of request, and override requestFinished:
and failWithError:.
That provided me a good solution to handle different requests.
You could try this -
- (void)requestFinished:(ASIHTTPRequest *)request {
NSLog(#"Response %d ==> %#", request.responseStatusCode, [request responseString]);
}
You can also handle other methods if you choose, such as:
- (void)requestStarted:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
The docs are located at http://allseeing-i.com/ASIHTTPRequest/ and are fantastic.
You can cast your request into a ASIFormDataRequest:
if ([request isKindOfClass:[ASIFormDataRequest class]]) {
ASIFormDataRequest *requestWithPostDatas = (ASIFormDataRequest *)request;
NSArray *myPostData = [requestWithPostDatas getPostData];
}
You will also have to make "postData" accessible with a "getPostData" public function in ASIFormDataRequest.
I've recently started trying to implement HTTP upload support to a program, but I've been having some difficulty doing so. This is the first time I've ever used objective-c (although I have a C background), so I'm still very new to the language. I've been trying to get it to work using the HTTPRequest library, but haven't been able to get anything to start working. It's a fairly large program (2500~ lines) so I won't paste it here. I'll just paste the function itself here.
- (void)Upload2HTTP2:(NSString *)ZipPath
{
[self UpdateProgressBar:#"Upload2HTTP opening Connection to Website..."];
NSLog(#"Upload2HTTP called\n");
//URL to be used to upload
NSURL *url = [NSURL URLWithString:#"http://ftp.website.com"];
NSLog(#"Upload2HTTP -%#\n",url);
//Creates the new ASIFormDataRequest object to do the uploading
//Uses the ASIHTTPRequest and ASIFormDataRequest libraries
// http://allseeing-i.com/ASIHTTPRequest/ for more information
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request addRequestHeader:#"Referer" value:#"http://ftp.website.com"];
//Sets the authentication information
//This should have already been retrieved in RetrieveFromBrowser
[request setUsername:RespUID];
[request setPassword:RespPWD];
//Sets the file to be uploaded
[request setFile:ZipPath forKey:#"Customer Upload"];
//Starts the transfer?
[request startAsynchronous];
}
ZipPath, RespUID, and RespPWD are all set in another area of the program. Basically, I've got the username/PW for the HTTP authentication, and the path to the file I want to upload, but I've very little experience with the language and this library, so I'm a bit lost. I can't give any specific errors or reasons as to why it hangs, I just know that after I click upload in the program, it runs through this function, and the program hangs trying to upload the file. Is there anything I'm missing or doing wrong in this function? I'd really appreciate any help you guys could lend.
Thanks :)
ASIHTTPRequest's asynchronous networking takes advantage of the delegate design pattern. Setting the request's delegate property to self, having that class adhere to the ASIHTTPRequestDelegate protocol, and implementing - (void)requestDidFinish: and - (void)requestdidFail: should give you callbacks for finish and failure. Quick example:
- (void)Upload2HTTP2:(NSString *)ZipPath
{
...
request.delegate = self;
[request startAsynchronous];
}
- (void)requestDidFinish:(ASIHTTPRequest *)request
{
NSLog(#"Success! Do stuff here with [request responseData] or [request responseString]");
}
- (void)requestDidFail:(ASIHTTPRequest *)request
{
NSLog(#"Failure! Check out [request error] for details");
}
Basically what's happening is that I need to download a whole bunch of files in my app and I've set up a queue of sorts that downloads each file with an NSURLConnection and stores the server response incrementally in an NSMutableData until the download is finished and then writes the whole thing to disk.
Here's the relevant parts:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)_response {
response = [_response retain];
if([response expectedContentLength] < 1) {
data = [[NSMutableData alloc] init];
}
else {
data = [[NSMutableData dataWithCapacity:[response expectedContentLength]] retain];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)_data {
[data appendData:_data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"saved: %#", self.savePath);
[data writeToFile:self.savePath atomically:YES];
}
Any insights as to why this would be awfully slow? It's pretty bad with the Simulator and gets even worse on an actual device. My maximum download size is around 2 megabytes, so I figured storing the whole thing in memory until it finishes wouldn't be that bad of an idea. This gets up to about 20KB/s at best (with a direct ad-hoc wifi connection).
Edit: in all my test cases I do get a Content-Length header, so it's not a matter of growing the NSMutableData with each bit of response received.
Edit 2: this is all Shark gives me.
Edit 3: So this is how I set up the connection
NSMutableURLRequest *request = [[NSMutableURLRequest requestWithURL:[NSURL URLWithString:[#"http://xxx.xxx.xxx.xxx/index.php?service=" stringByAppendingString:service]]] retain];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[[options JSONRepresentation] dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
[conn start];
Of course I don't actually have a hardcoded url and both request and conn are instance variables of the downloader class. Not that it should matter, but for JSON I'm using http://code.google.com/p/json-framework/. Options and service are method parameters (NSString and NSDictionary), not that those should matter either.
Boy this is embarrassing. Turns out my Content-Length header was inaccurate, which resulted in NSURLConnection needing to wait for some sort of timeout before it would finish, even though it had all the data. Makes sense really. Maybe this will help someone else out.
I would profile to find out where the slow down is occurring and in what pattern. Put a log statement in connection:didReceiveData to see record how often it is called. You're looking for:
The relative elapsed time between calls to the method.
Whether the time between calls increases as the app runs.
If the elapsed time between calls is where the app spends most of its time then the bottleneck is in the request itself. Either the request is misconfigured of the server is not sending quickly.
If the time between calls increases the longer the app runs, then it is probably a memory issue. As the data grows larger and the memory more constrained, the app has to swap more stuff in and out of memory which slows everything down. To test, log the various didReciveMemoryWarning methods in any active objects.
Update:
According to Shark, the problem is in your URL request and not the code you posted. You need to look at how you set up the request.