Google App Engine. Google app engine seeing POST requests as a GET - ios

I'm having the strangest issue right now with google app engine. I'm sending a POST request from iOS and google app engine instead invokes the GET handler.
I've sandboxed this one situation for testing and can't get it figured out. I have an iOS app that just sends a request. And I've commented out everything on GAE except for the service. The service only logs a parameter and returns.
The iOS app I've tried using two different ways of sending the request. Neither works.
iOS Code:
/*
NSURL * url = [NSURL URLWithString:#"http://beermonster-gngrwzrd.appspot.com/TestParameter"];
ASIFormDataRequest * _fdrequest = [[ASIFormDataRequest alloc] initWithURL:url];
[_fdrequest setPostValue:#"hello" forKey:#"testkey"];
[_fdrequest startAsynchronous];
*/
NSURL * __url = [NSURL URLWithString:#"http://beermonster-gngrwzrd.appspot.com/TestParameter"];
NSMutableURLRequest * __request = [NSMutableURLRequest requestWithURL:__url];
[__request setHTTPMethod:#"POST"];
NSString * post = [NSString stringWithFormat:#"testkey=hello"];
[__request setHTTPBody:[post dataUsingEncoding:NSUTF8StringEncoding]];
[NSURLConnection sendSynchronousRequest:__request returningResponse:nil error:nil];
My App engine handler:
class TestParameter(webapp.RequestHandler):
def post(self):
logging.debug(self.request.get("testkey"))
self.response.out.write(self.request.get("testkey"))
print self.request.get("testkey")
def get(self):
logging.debug("get")
logging.debug(self.request.get("testkey"))
self.response.out.write(self.request.get("testkey"))
The output in the GAE logs shows the "get" code path which isn't correct.
Any ideas why POST requests would come into GAE as a GET? Is there some configuration in GAE that I missed?
Thanks!

Check the entry in app.yaml for the script that handles "/TestParameter". Does it specify "secure: always"? If it does and you make a non-secure connection you will get a 302 redirecting to the secure version.
To fix this either make your post over HTTPS or remove "secure: always" from the entry in app.yaml.

From what I can tell if you want to send POST requests to GAE. Make sure you do it on https. If you make the request on a non-https attempt, it sends back a 302 redirect to the https version of the request. But if whatever you're using to send the request doesn't correctly handle 302's it might resend the request incorrectly.

Related

Upload image from iOS to Django

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.

Authentication Issue with REST call for iOS

I am currently trying to make a REST call from an iOS device. My code is below
NSString *restCallString = [NSString stringWithFormat:#"MyURL"];
NSURL *url = [NSURL URLWithString:restCallString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60];
[request setHTTPMethod:#"GET"];
[request addValue:Value1 forHTTPHeaderField:#"Header1"];
[request addValue:Value2 forHTTPHeaderField:#"Header2"];
[request setURL:[NSURL URLWithString:restCallString]];
#try{
_currentConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
#catch(NSError *e){
NSLog(#"%#", e.description);
}
Whenever this is called, I get the following error: Authentication credentials were not provided. However, what confuses me is that if I send an identical GET request via a HTTP web console, it works perfectly. In other words, using the same URL and the same 2 header-value pairs, I get a valid response on a web console, and see no authentication errors. What could be causing this?
You are setting the HTTP headers. This won't work, because the HTTP header is not contained in $_GET or $_POST because they're are not content, but description of the content expected.
Try this instead:
NSURL *url = [NSURL URLWithString:[restCallString stringByAppendingFormat:#"?Header1=%#&Header2=%#", Value1, Value2]];
Of cause you have to be aware that the URL is RFC 1738 compliant.
if I send an identical GET request via a HTTP web console, it works perfectly
I suspect your web console is leveraging SessionAuthentication — i.e. If you're already logged in to your site in your browser the API will authenticate you based on your session cookie.
Django Rest Framework provides various authentication methods and there are third-party options too. The simplest to get going is probably the provided Token Auth method.
Make sure this is enabled. Create a token in the admin (or via the provided view) and make sure you've set the Authorization header. It needs to look like this:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
So your Objective-C will go something like:
[request addValue:[NSString stringWithFormat:#"Token %#", yourToken]
forHTTPHeaderField:#"Authorization"];
Hopefully that gets you started.

ios - Restkit and SSL certificate error

I have a tomcat server that uses a self signed SSL certificate and is running a web service. I am trying to connect to the web service with Restkit. However, I am getting an error related to certificate validity. Here is my code:
NSURL *url=[NSURL URLWithString:baseURL];
RKClient *client = [RKClient clientWithBaseURL:url];
client.disableCertificateValidation=YES;
RKRequest *request = [client requestWithResourcePath:[NSString stringWithFormat:#"/addEvent?deviceID=%#&eventID=%#",deviceID,eventID]];
request.disableCertificateValidation=YES;
request.delegate=self;
RKResponse *response = [request sendSynchronously];
This request fails with the following error:
2013-01-09 15:11:53.931 Mobile_ACPL[5761:907] The certificate for this server is invalid. You might be connecting to a server that is pretending to be “notify.acpl.lib.in.us” which could put your confidential information at risk.
I get this error even though I have set disableCertificateValidation to YES. How can I get this working?
EDIT: I attempted adding the certificate as shown here: https://github.com/RestKit/RestKit/pull/131
I still get the same result.
EDIT 2: It looks like the error message is being set at this line in RKRequest.m:
payload = [NSURLConnection sendSynchronousRequest:_URLRequest returningResponse:&URLResponse error:&error];
NSURLConnection does not cater for authentication challenges in synchronous calls. You need to make asynchronous calls for this to work.
In my case, I was setting disableCertificateValidation on RKClient but I was using a RKObjectManager which used a different RKClient. The following line, placed after the RKObjectManager initialization, did the trick:
[RKObjectManager sharedManager].client.disableCertificateValidation = YES;
if you are using RestKit using
client.allowsInvalidSSLCertificate = YES;
won't work, instead do this:
if you added rest kit manually to your project, click on RestKit.xcodeproj go to project > Build Settings > Preprocessor Macros
and add _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_=1
thats finished.

How to handle redirecting urls with get parameters in the request?

I am trying to get an html response from a server(aspx).
NSString *urlstr = [[NSString alloc] initWithFormat:#"http://www.someSiteThatRedirectsWithGetParameters.com?parameter1=1&parameter2=2"];
NSURL *url = [ NSURL URLWithString:urlstr];
NSMutableURLRequest *urlRequest = [ NSURLRequest requestWithURL: url];
NSData data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
NSURL *lastURL=[response URL];
NSLog(#"Last url %#", lastURL);
I am getting lastURL as www.someSiteThatRedirectsWithGetParameters.com and therefore not getting the html page for the original requested url.
Why is this so and how can I solve this?
EDIT
This is all what I have tried so far : Get the last redirected url in iOS 5?
but I am still not able to achieve what I am trying to i.e : get the html page source of the original GET request(going through redirections) having parameters in the url ?
I am getting lastURL as www.someSiteThatRedirectsWithGetParameters.com
and therefore not getting the html page for the original requested
url.
If your request is being redirected, it's because the server to which you're connecting is telling you that there is no page at the URL to which you first made the request, and you should use the redirected URL instead. That's the whole point of a redirect response.
If the response that you get is a HTTP 303, the new request will properly be changed from POST to GET. This is sometimes done to avoid cases where refreshing the page would resubmit the original request with undesirable consequences. In this case, you POST your request to some URL, and the server accepts the request but responds by redirecting the client to a different URL which ultimately displays the server's response.
Why is this so and how can I solve this?
I've explained the 'why' above. The "solution" is to accept the response from the server and follow the redirect to the URL that contains the server's response.
I would try the approach of sending two requests. Send the first request, then the redirectURL will be the response. Then make a new URL of the response, sending a request to that url with the GET-parameters. I haven't tried it, but I suppose it could work.

Using asihttprequest to fetch data based on http status code

I am trying to connect my iOS app to a GET data from my web service everytime something changes. My current implementation is to use NSTimer and do a ASIHttpRequest but I don't like that polling implementation. Is there a better way to do it?
I am considering starting the request and lets it keep trying until the web service status code turns to OK (status 200) or perhaps a status code of "modified". How would I do this in a view controller?
Here is what I have so far:
self.asiRequest = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:self.barcodeUrl]];
[self.asiRequest setDelegate:self];
[self.asiRequest startAsynchronous];
[self.asiRequest addRequestHeader:#"Accept" value:#"application/json"];
[self.asiRequest addRequestHeader:#"Content-Type" value:#"application/json"];
int statusCode = [self.asiRequest responseStatusCode];
I know that if the request is successful, the delegate method - (void)requestFinished:(ASIHTTPRequest *)request
I guess my question is, how do I keep an open ASIHttpRequest such that when the web service returns "modified", it calls a GET request and then keep an open connection again? This has to be asynchronous and not running on a UI thread as I don't want to keep my app hanging.
Thanks much!
The easiest way to do this without having to do much on the client-side is to get your web service to send a Location: header through when it returns "modified", pointing to the URL you want to GET with the new content. If the URL to monitor is different depending on e.g. user ID or the area of the app you are in, make a monitor script where you can pass in GET params to configure what exact URL should be returned to you when "modified" is returned.
The ASIHTTPRequest will then automatically GET the content at the new Location: and you can reschedule it in requestFinished: to the monitor URL again, knowing that the response (if not empty/"Not Modified") will ALWAYS be new data since the redirect to new data happens behind-the-scenes.

Resources