Is there an easy way to dump the response data from a TTURLRequest? I'm getting a server side error and I'd like to be able to quickly log the response in the console without having to tail the server logs. I'm using Three20's TTURLRequest in conjunction with TTURLJSONResponse, and so far I haven't been able to view that data easily unless the response is a JSON string. Any suggestions? Thanks!
Try to print the NSERROR
NSData *returnData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
Related
I need help getting a basic understanding of uploading a file to a Django view via a post request. This is the Django form I'd like to upload the image to:
https://domfa.de/upload_profile/
This is the views.py code for this exact Django view URL:
def profile_picture(request):
if request.POST:
form = UserProfileForm(request.POST, request.FILES)
obj = form.save(commit=False)
obj.user_id = request.user.id
obj.profile_picture = obj.profile_picture
obj.save()
return render_to_response('profile.html', args, RequestContext(request))
else:
formNew = UserProfileForm()
args = {}
args.update(csrf(request))
args['uid'] = request.user.id
args['form'] = formNew
return render_to_response('profile.html', args, RequestContext(request))
And the actual form for this is extremely simple with just a single field for the actual profile picture:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('profile_picture',)
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
So the Django side works great, I'm always able to upload an image successfully as long as I'm logged in. I'm stuck though however on how to POST an image to this extremely simple Django view, I've already logged in to the Django server as a user using a separate NSUrl request:
UIImage *picture = [UIImage imageNamed:#"myFile.png"];
NSData *imageData = UIImagePNGRepresentation(picture);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:[NSURL
URLWithString:#"https://domfa.de/upload_profile/"]];
[request setHTTPMethod:#"POST"];
[request setValue:#"image/png" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody:imageData];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSLog(#"%#", returnData);
So assuming all the Django code works, which it does, what's wrong with my objective-c code which does the actual image uploading?
And also, how could I get response messages from the server in the NSLog so I could better know why the server won't accept a file POST request?
Getting feedback what is going on when sending a HTTP request from iOS
The problem is that you are not passing in a pointer to a NSURLResponse object which you could examine after the request was made.
NSURLResponse *response;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
Now you can examine the output by looking at the object.
Why is it not uploading?
There are several issues with your approach: You are using django's forms API which is OK, but there are two pitfalls in your code:
CSRF You are not setting the the CSRF value correctly when submitting the form. You can workaround it by first accessing your form with a GET request, just like a browser would do it, then get the CSRF token from the cookies which the server set and then sending a POST request including the retrieved CSRF token. See also CSRF handling in AJAX requests
Data encoding You are not sending form data back to the view, but raw data instead. This will not work this way. You would have to send proper POST form data. You could do it manually or use ASIHTTPRequest which abstracts the cumbersome handling of multipart/form-data.
I would rethink the design decision to use a form to programatically upload a picture. Why not use something more like a REST API for this? There are great frameworks for implementing a clean REST API in django (e.g. http://tastypieapi.org/).
Maybe looking at the API description of Twitter and app.net could help inspiring you how to build this.
I am confused about this method, specially about the error part and the documentation. Here on the documentation it says that
If the request completes successfully, the data parameter of the
handler block contains the resource data, and the error parameter is
nil. If the request fails, the data parameter is nil and the error
parameter contain information about the failure.
The problem is that, I have seen that some http response error codes fall into the "there is an error" and some dont, furthermore I can have data and error at the same time (but supposedly one should be nil at all times according to the documentation).
[NSURLConnection sendAsynchronousRequest:theRequest
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
For example, my server sends a 401 response if the user uses wrong credentials, and this is cached inside the error clause, but if i send a 409 (duplicate entry) this is NOT cached by it. Im having some issues redirecting the flow because of this, im not sure what this method will consider an error and what it will not.
Also, how do i properly check for this?, I thought that just checking if there is an error, and then checking what was the code of the response would suffice to display a message or take an action, but as I described before, some "error" codes don't necessarily generate the error object. Should i first check for the status code?
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = [httpResponse statusCode];
if (responseStatusCode == 200)
{
// If 200, assume everything went well?
} else
{
// Something went wrong, check for the code and the error here?
}
From my experience an error occurs when there is no response at all from the server, that's why the data is 0. If a server returns 400 or 500 error then these are a response from your server and depending on your server code maybe an error message you can parse.
Does
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
return error = nil when data is not nil?
if there is data returned, is error always nil?
that's what the documentation says ...
how is error affected by the http status code?
like if the status code is not 200, does it always return an error?
The NSError is not related with the HTTP Response. The NSError is returned when the connection itself failed. A bad HTTP status code means the request itself works for the client.
You have to manually check the status code.
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = [httpResponse statusCode];
Now you can test if the status is 200.
To answer to your questions :
return error = nil when data is not nil?
if there is data returned, is error always nil?
If the data == nil then you should have error != nil
how is error affected by the http status code? like if the status code
is not 200, does it always return an error?
If you got a status code then it means the connection worked (from a connection request point of view, not the HTTP one) so error == nil and data should be not nil
You answered your first two questions yourself already. The documentation is right there.
Regarding the status code: No, status codes do not affect the NSError at all. This NSError is reserved for when a NSURLConnection actually fails in a non-HTTP way. For example when you're actually offline and no request can be made at all. Also keep in mind, that NSURLConnection is not tied to HTTP requests in any way. iOS also supports FTP connections out of the box (no status code like in HTTP here) and you can even write your own protocol support for almost anything you can think of.
Of course there will just occure an NSError if the request failed to get the data. But if the server returns you any other status code than 200, it will be like an error because you couldn't get the data. But by checking the status code you can give more specific error messages to the user instead of just Couldn't load data, because you can specify if there was an NSError which could be caused by missing internet connection or if you received any bad status code and the server is not available..
Several times before I used the method...
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
to send/receive data synchronously when in an NSOperation and it has worked.
I'm working on a new project (new API, new server) now and the error just isn't being populated.
I'm using Charles proxy to snoop the network traffic and I can see "Response Code 500 Internal Server Error" coming back from the failed request in Charles but in the app the error object is always nil.
Like I said, I've used this loads before and the error is always populated if an error occurs.
Is there something on the server side that I can point out to the server side dev? He's adamant that I must be doing something wrong. The only thing I don't understand is that Charles is picking up response code 500 from it.
N.B. The problem is not that the error is occurring. The problem is that when it does occur the NSError object is not reporting it back to me. Just to clear it up in case anyone tells me to ask the server dev to fix the error. :-)
Your error object is not populated because the request succeeded from NSURLConnection's perspective -- that is, it was able to send data to the server and receive a response.
For protocol-specific errors, you need to inspect the NSURLResponse you get back. In this case, cast the response to NSHTTPURLResponse and invoke -statusCode.
The error is only populated if there is no response from the server or not a valid HTTP response, e.g. connection loss etc.
"Response Code 500 Internal Server Error" means there was an internal server error and your server returns an HTTP message with the response code 500. You should use NSHTTPURLResponse instead of NSURLResponse and call the statusCode method to check the HTTP response code. If the response code starts with 2 everything is fine. So I usually have a check like this: statusCode / 100 == 2.
For more HTTP response codes see http://en.wikipedia.org/w/index.php?title=HTTP_response_codes
I am using HTTP Post method and initiating a synchronous request.
[NSURLConnection sendSynchronousRequest: ..]
For HTTP POST requests, the default time out is happening at 75 seconds as discussed in many threads.
But during that time out period of 75 seconds, Multiple web service requests are getting initiated for us for the same request raised with all the same parameters.
Please let us know What causes this multiple requests to get initiated? Is this due to HTTP POST in general or because of Synchronous request?
#iOS Sample Code
[body appendData:[[NSString stringWithFormat:#"\r\n--%#--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];
[request addValue:[NSString stringWithFormat:#"%d", body.length] forHTTPHeaderField: #"Content-Length"];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
NSURLResponse *response;
response = nil;
urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(urlData)
{
NSString *responseString = [[NSString alloc] initWithData:urlData encoding:NSASCIIStringEncoding];
[self parseStringInformation:responseString infoDict:informationDictionary];
//NSLog(#"%#",responseString);
}
Without the server's request-response logs there are several possibilities.
Programmer Error: Have you already gone through all of "gotchya" type situations?
Have you put a logging message right before your "urlData = [NSURLConnection sendSynchronousRequest: ..." line to make sure your code is only calling it once?
Are you calling this function from within your main GUI thread, if you are that's not supported/recommended, which means it could be causing side-effects like what you're describing. https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html
Are you sure your Request is set up as a POST and has the proper headers like "Content-type: multipart/form-data, boundary=X"
Web Server Responses: Without the web servers logs (or the code for the service your POSTing to) it's hard to say...
Perhaps your server is sending cyclical redirects to the client. If you don't implement a "connection:willSendRequest" then the redirects could be followed for ?X? amount of times for one request. http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/urlloadingsystem/Articles/RequestChanges.html
API Error: You've found some corner case which is causing unwanted side effects. Maybe apple has an bug tracker or developer support forum?
If this is the case you'll have to work around the bug, until it's fixed. I suggest implementing the Asynchronous call chain. "Loading Data Asynchronously" https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html