I'm trying to create a user acount on my rails backend via json from an iPhone app. Here is what is currently being posted to the server:
Started POST "/patients" for 127.0.0.1 at 2011-11-27 20:52:29 -0800
Processing by PatientsController#create as HTML
Parameters: {"patient"=>"{\"password\":\"password\",\"password_confirmation\":\"password\",\"email\":\"testagain\"}"}
WARNING: Can't verify CSRF token authenticity
Completed 500 Internal Server Error in 1ms
NoMethodError (undefined method `stringify_keys' for #<String:0x00000104a354f8>):
app/controllers/patients_controller.rb:43:in `new'
app/controllers/patients_controller.rb:43:in `create'
By posting straight from the browser these are the paramaters that are submitted:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"H2iYdzdfokQs91AAozb+taMTdV2y5xLRaCni5XKQN4w=", "patient"=>{"email"=>"test", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create Patient"}
From what I have read elsewhere the stringify_keys means that the actions expects a hash (which I thought I almost reconstructed) since I am using this code to create a new user:
#patient = Patient.new(params[:patient])
I also believe that the authenticity token doesn't matter if I'm posting using JSON format... does it matter?
Over all question: Is this the right approach to be posting to a rails backend from an iphone app? Recreating the parameters hash? Would appreciate any nudges in the right direction.
For completeness sake here is the code snippet I'm using to post from my iOS app:
NSDictionary *json = [self createSignUpDictionary];
NSURL *url = [NSURL URLWithString:#"http://localhost:3000/patients"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request addRequestHeader:#"Accepts" value:#"application/json"];
[request setPostValue:[json JSONString] forKey:#"patient"];
[request startAsynchronous];
You will most likely have to disable the authentication token verification for your action.
Just put the following line in your controller and everything should work.
protect_from_forgery :except => :index
However if you do that make sure that you have some form of custom protection on your #create function. You can read this for more info: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html
Related
I am using twitter API version V1.0 for my new iphone application .I am successfully able to do this using twitters v1.0 of the API and all works perfectly. Simply making a request to http://api.twitter.com/1/statuses/user_timeline.json?screen_name=userid retrieves all the information that I require.
since v1.0 has been deprecated and V1.1 requires authentication for each request, I get a bad authorization error (HTTP response status: 400)using this API.
What are the Changes i need to do in my appication .how to generate OAuth request headers,Do i need register my application ?How can i get authentication for new version?
I hope the above makes sense and any help would be appreciated.
Thanks in advance.
Visit https://dev.twitter.com/docs/ios/making-api-requests-slrequest
https://dev.twitter.com/docs/oauth/xauth
u can get the authentication using following method:
NSString *base64 = #"Basic NFRLWGlZY3l1aHJ4OVJaUWI5RW5BOmdVMXlvcVV6YzBNZUhUQmFXdVRZU2NtZHlIWDFOZXhwZmxqRE16bm01aw==";
//oauth_consumer_secret,oauth_consumer_key
NSURL *urlAPI = [NSURL URLWithString:[NSString stringWithFormat:#"https://api.twitter.com/oauth2/token"]];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:urlAPI];
[request setDelegate:self];
[request setRequestMethod:#"POST"];
[request addRequestHeader:#"Authorization" value:base64];
[request addRequestHeader:#"Content-Type" value:#"application/x-www-form-urlencoded;charset=UTF-8"];
[request setPostValue:#"client_credentials" forKey:#"grant_type"];
In response u will get the authtoken for your application like this
{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAMddRQAAAAAAXRP6Axur1RS%2Fv9YFtJ7TijyPAGo%3DpmNowwOGf3dZTHDiH2gnCcv7qNIyGZcyV2IW5YFTBs"}
i want to like a post in Facebook, i have post_id , and i am not able to find the FQL Query for liking particular post from the Facebook developer page in IOS SDK.
From Facebook developer page, it says that you can like a post with the used of HTTP POST method it means we can't use GraphAPI or fql.query to like a post.
Can anyone please share HTTP POST URL to like a post in Facebook.
is anyone here who develop the like button functionality for Facebook post using custom button in iOS.
Thanks in advance.
Here is an example if you are using Facebook SDK in iOS:
[FBRequestConnection startWithGraphPath:[NSString stringWithFormat:#"%#/likes", post_id]
parameters:[NSDictionary dictionary]
HTTPMethod:#"POST"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error)
{
if (error)
{
NSLog(#"Error: %#", [error localizedDescription]);
}
else
{
NSLog(#"Result: %#", result);
}
}];
I see you are asking for fields(parameters) for HTTP POST URL. An HTTP POST request does not (usually) contain parameters on which you are probably used to when you pass them in a classic GET request such as ?param1=value¶m2=value after the script name in some URL.
POST request sends data to the server inside the message body, check out: http://en.wikipedia.org/wiki/POST_(HTTP)
Now that you know that, this is what you can do:
You CAN get the number of likes with a classic GET request, an URL that you can paste into any web browser and get the response, for example:
https://graph.facebook.com/260895413924000_605362559477282/likes?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
This url will give you a response with all the people who liked that post/photo.
You can leave out the ?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx if you know the post/photo is public as this one is (https://www.facebook.com/photo.php?fbid=605362542810617&set=a.260905783922963.82517.260895413924000).
If it is not you need to generate one actual access_token(also for posting you NEED to generate one) and for testing you can do it here: https://developers.facebook.com/tools/explorer/
Now if you want to actually like the photo you can't simply form an URL that you can copy/paste inside your browser and which will trigger the like action. That's because browsers do not do POST requests, you need to do it trough code as Ivo Patrick Tudor Weiss suggested or eventually for testing purposes you can do it with curl utility from console like this:
curl --data "access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" https://graph.facebook.com/260895413924005362559477282/likes
and you can undo the like with HTTP DELETE ... like this:
curl --data "access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -X DELETE https://graph.facebook.com/260895413924000_605362559477282/likes
- UPDATE, for additional questions made by OP in the comments:
It is of course possible to use ASIHTTPRequest to make GET, POST and DELETE HTTP requests. However I would not advise the use of that library for your case. One reason is that the author of ASIHTTPRequest has stopped working on the library, and the other reason is that Facebook SDK for iOS is a better choice since with it you have many other things already taken care for you.
That being said here are the examples:
First type either one of these three combinations depending on what you want:
Get all people who liked the specific post:
(for simplicity I omitted the access_token here but you can append it to the URL if needed)
NSURL *url = [NSURL URLWithString:#"https://graph.facebook.com/260895413924000_605362559477282/likes"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
Like the specific post yourself:
NSURL *url = [NSURL URLWithString:#"https://graph.facebook.com/260895413924000_605362559477282/likes"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request appendPostData:[#"access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" dataUsingEncoding:NSUTF8StringEncoding]];
//[request setRequestMethod:#"POST"]; // <--- NOT NEEDED since it is the default if you previously called appendPostData
Unlike the post:
NSURL *url = [NSURL URLWithString:#"https://graph.facebook.com/260895413924000_605362559477282/likes"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request appendPostData:[#"access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" dataUsingEncoding:NSUTF8StringEncoding]];
[request buildPostBody];
[request setRequestMethod:#"DELETE"];
Then execute the actual request:
[request startSynchronous];
NSString *response = [request responseString];
NSLog(#"Response: %#", response);
Remember synchronous request is OK for testing but your GUI is going to be unresponsive if you use it on the main thread in an actual app. Learn how to do an asynchronous request here: http://allseeing-i.com/ASIHTTPRequest/How-to-use
As for your iOS example. It would be too much to write all the code here. And you already got the answer from Ivo Patrick Tudor Weiss which is perfectly correct. The only thing that is missing is the boilerplate code that you need to have to authenticate on Facebook and establish an FBSession.
I would advise you to go over this material here: https://developers.facebook.com/docs/getting-started/facebook-sdk-for-ios/
Download the latest SDK which contains also the sample code, and follow the tutorial on Facebook web. Then when you get the basics configured, get back to the answer you got from Ivo.
You can use Graph API to post a like to Facebook post. As it said in documentation here:
http://developers.facebook.com/docs/reference/api/post/
To create a like you need to issue a HTTP POST request to the POST_ID/likes connection with the publish_stream permission. You can suppress the notification created when liking a Post by passing a notify parameter with value of false.
I'm using the ASIHTTPRequest lib in my iOS app to make RESTful requests to my Rails 3 web app. I seeing a weird and somewhat consistent error the 1st time I try to make a POST request to my web app, but then the POST request works fine the on the second attempt. The exact error is...
Error Domain=ASIHTTPRequestErrorDomain Code=1 "A connection failure occurred" UserInfo=0xb513740 {NSUnderlyingError=0xb5135a0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1005.)", NSLocalizedDescription=A connection failure occurred}
And here's my ASIHTTPRequest code for making the POST request...
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://myrails3app.heroku.com/tournaments/%d/register.json", tid]];
__block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setRequestMethod:#"POST"];
[request addPostValue:username forKey:#"username"];
[request setCompletionBlock:^
{
NSData *responseData = [request responseData];
NSLog(#"Success!");
}];
// Set the code to be called when the request fails
[request setFailedBlock:^
{
NSError *error = [request error];
NSLog(#"Error: %#", [error localizedDescription]);
}];
// Start the request
[request startAsynchronous];
It's worth mentioning that when it errors out, it errors out incredibly quickly! Also, for what it's worth, my Rail 3 app that I'm making the POST request to is hosted on Heroku. Your thoughts?
Thanks so much for your wisdom!
This issue I had a lot of hard time to figure out why. The problem resides in ASIHTTPRequest itself (iOS), not the rails code.
To make a long story short, the problem is specific to the use of persistent connection for every request sent by ASIHTTPRequest.
While this is good for GET requests, most server implementation does not allow persistent connection to be used with POST request.
I didn't really have time to investigate it deeply on the server side of things but I think that the problem resides with the 100-Continue header that should be sent (and which isn't) with request that has body attached to it (hence PUT/POST). If you want to have a deeper look at what I'm talking about go have a read at the spec sheet: http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html
So the persistent connection used by ASIHTTPRequest wait for a 100 response to be sent, which is never sent. so it ends up being timed out.
A fix is to set persistentConnection to NO with your post requests like the following:
ASIHTTPRequest *req = [ASIHTTPRequest requestWithURL:url];
req.shouldAttemptPersistentConnection = NO;
Secure Connection Failed
An error occurred during a connection to login.yahoo.com.
SSL received a record with an incorrect Message Authentication Code.
(Error code: ssl_error_bad_mac_read)
The page you are trying to view can not be shown because the authenticity of the received data could not be verified.
Please contact the web site owners to inform them of this problem. Alternatively, use the command found in the help menu to report this broken site.
On iOS 5.1, even after upgrading ASIHTTPRequest to ASIHTTPRequestVersion = #"v1.8.1-61 2011-09-19" still needed:
ASIHTTPRequest *req = [ASIHTTPRequest requestWithURL:url];
req.shouldAttemptPersistentConnection = NO;
i also had to set response setHeader Connection to "close" in by Java servlet
response.setHeader("Connection", "close");
I have a cocoa class set up that I want to use to connect to a RESTful web service I'm building. I have decided to use HTTP Basic Authentication on my PHP backend like so…
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
//Stuff that users will see if they click 'Cancel'
exit;
}
else {
//Validation Code
echo "You entered info.";
}
?>
At this point I'm using a synchronous NSURLConnection, which I understand the Apple documentation states has less support for Authentication.
But is it even possible at all? I can do cookie authentication very easily sans NSURLProtectionSpaces or NSURLCredentials or any of the authentication classes. Also, are there any resources where I can read more about the Cocoa Authentication classes?
Thanks.
UPDATE: mikeabdullahuk
The code you supplied (the second example) is almost identical to what I had written. I have done some more investigating, and discovered that the NSURLConnection is returning an error…
Error Domain=NSURLErrorDomain Code=-1012 UserInfo=0x1a5170 "Operation could not be completed. (NSURLErrorDomain error -1012.)"
The code corresponds to NSURLErrorUserCancelledAuthentication. So apparently my code is not accessing the NSURLCredentialStorage and instead is canceling the authentication. Could this have anything to do with the PHP HTTP Authentication functions? I'm quite confused at this point.
A synchronous NSURLConnection will absolutely work with NSURLCredentialStorage. Here's how things usually work:
NSURLConnection requests the page from the server
The server replies with a 401 response
NSURLConnection looks to see what credentials it can glean from the URL
If the URL did not provide full credentials (username and password), NSURLConnection will also consult NSURLCredentialStorage to fill in the gaps
If full credentials have still not been determined, NSURLConnection will send the -connection:didReceiveAuthenticationChallenge: delegate method asking for credentials
If the NSURLConnection now finally has full credentials, it retries the original request including authorization data.
By using the synchronous connection method, you only lose out on step 5, the ability to provide custom authentication. So, you can either pre-provide authentication credentials in the URL, or place them in NSURLCredentialStorage before sending the request. e.g.
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://user:pass#example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
or:
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"user"
password:#"pass"
persistence:NSURLCredentialPersistenceForSession];
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:#"example.com"
port:0
protocol:#"http"
realm:nil
authenticationMethod:nil];
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential
forProtectionSpace:protectionSpace];
[protectionSpace release];
NSURLRequest *request =
[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://example.com"]];
[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];
In a situation where a 401 or other authentication challenge is unacceptable/impossible, I sometimes use a dummy CFHTTPMessage to generate the authetication line, then copy that back into the NSURLRequest:
// assume NSString *username and *password exist and NSURLRequest *urlRequest
// exists and is fully configured except for HTTP Basic Authentication..
CFHTTPMessageRef dummyRequest =
CFHTTPMessageCreateRequest(
kCFAllocatorDefault,
CFSTR("GET"),
(CFURLRef)[urlRequest URL],
kCFHTTPVersion1_1);
CFHTTPMessageAddAuthentication(
dummyRequest,
nil,
(CFStringRef)username,
(CFStringRef)password,
kCFHTTPAuthenticationSchemeBasic,
FALSE);
authorizationString =
(NSString *)CFHTTPMessageCopyHeaderFieldValue(
dummyRequest,
CFSTR("Authorization"));
CFRelease(dummyRequest);
[urlRequest setValue:authorizationString forHTTPHeaderField:#"Authorization"];
This may seem completely a bizarre way to do it but it is tolerant of situations where the username/password aren't URL clean and where NSURLRequest refuses to consult the NSURLCredentialStorage because the server isn't actually sending a HTTP 401 (for example it sends a regular page instead).
I would note mikeabdullahuk's answer is good but also if you use NSURLCredentialPersistencePermanent instead of per session it will store the credentials in the users keychain so next time you can check NSURLCredentialStorage for a non nil value for the default credentials for a protection space and if you get a non nil value you can just pass the credentials in. I am using this method right now for a delicious.com client I am writing and it works very well in my tests.
Set your credential as the default credential for the protectionspace:
// Permananent, session, whatever.
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence: NSURLCredentialPersistencePermanent];
// Make sure that if the server you are accessing presents a realm, you set it here.
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:#"blah.com" port:0 protocol:#"http" realm:nil authenticationMethod:NSURLAuthenticationMethodHTTPBasic];
// Store it
[[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace];
At this point, any subsequent NSURLConnection that is challenged using a protection space that matches what you set will use this credential
I'm working on an IPhone application that works with a Google App Engine application. I manage to get logged by using a google account and I get the authentication token. I'm also able to GET data from the GAE service (I did it after reading another question written here) but now I need to POST data so I need to send the authentication token in the header of the POST request. I tried several options but none of them worked.
Here is the code I use to put that auth into the header:
NSString* urlStr = [NSString stringWithFormat:#"%#%#", HOST, url];
NSMutableURLRequest* urlPost = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
NSString* authStr = [NSString stringWithFormat:#"GoogleLogin auth=%#", token];
[urlPost addValue:authStr forHTTPHeaderField:#"Authorization"];
but it doesn't work.
Any help?
You need to use [request setHTTPMethod: #"POST"] and [request setHTTPBody: postdata] to properly configure the POST components. See the NSMutableURLRequest docs for more details.
Whenever I'm troubleshooting a problem related to HTTP, the first tool I'll grab is Charles HTTP Proxy. It will show you the entire request and response for closer examination.
If you're authenticating against an App Engine app, you need to obtain and send an authentication cookie, rather than using the GoogleLogin authentication. The source of appengine_rpc.py in the Python SDK demonstrates how.