I am using AFNetworking shared client in order to make request to the REST server.
My code for delete is:
NSMutableDictionary* params = [[NSMutableDictionary alloc] init];
[[ApiClient sharedClient] deletePath:[NSString stringWithFormat:#"users/%#/venues/%#/", appDelegate.currentUser.userId, venue.venueId] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"venue deleted from saved");
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(error.description);
}];
When I am configuring the client, I am adding:
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
[self setDefaultHeader:#"Content-Type" value:#"application/json"];
[self setParameterEncoding:AFJSONParameterEncoding];
POST request is working fine, but there is a problem with the DELETE request. Where am I wrong?
You will need to look at the error and check what is going wrong. If there error is not helpful, then you will need to use a proxy (perhaps Charles) and see exactly what is happening with the request and why the server does not like it.
Related
Does anyone know how to set ticket inside AFHTTPSessionOperation?
This is the previous call using AFNetworking framework 1.0
NSURLRequest* request = [self.myClient requestWithMethod:#"POST" path:[NSString stringWithFormat:#"%#/%#", controller, action] parameters:parameters];
AFHTTPRequestOperation* operation = [self.myClient HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.mirrorClient enqueueHTTPRequestOperation:operation];
The ticket is stored inside the self.myClient. self.myClient.ticket
But I'm not sure how to implement that in the following call using AFHTTPSessionOperation with AFNetworking framework 3.1.
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] init];
AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer = manager.requestSerializer;
[requestSerializer setValue:[NSString stringWithFormat:#"%#", self.myClient.ticket] forHTTPHeaderField:#"Authorization"];
NSOperation *operation = [AFHTTPSessionOperation operationWithManager:manager HTTPMethod:#"POST"
URLString:urlString parameters:parameters
uploadProgress:nil downloadProgress: nil
success:success failure:failure];
Thank you
This code looks basically correct. You could simplify the requestSerializer configuration a tad, and I might not instantiate a new session for every request, but the following worked fine for me:
- (void)performRequest:(NSString *)urlString
parameters:(id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask *task, NSError *error))failure {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager.requestSerializer setValue:self.myClient.ticket forHTTPHeaderField:#"Authorization"];
NSOperation *operation = [AFHTTPSessionOperation operationWithManager:manager
HTTPMethod:#"POST"
URLString:urlString
parameters:parameters
uploadProgress:nil
downloadProgress:nil
success:success
failure:failure];
[self.queue addOperation:operation];
}
I watched it in Charles, and the ticket, 12345678 appeared in my request header, as expected:
I suspect your problem rests elsewhere. This code does set the Authorization header to ticket. Make sure this is the right place to set the ticket. Also, make sure the ticket is what you think it is.
I am trying to make a PUT request using AFNetworking. There is a JSON on a remote location, which I want to update with the request.
I have managed to serialize my object into JSON, and I have put it an NSDictionary object:
NSDictionary *container = [NSDictionary dictionaryWithObject:[self notifications] forKey:#"notifications"];
This way when I print out the container using NSLog, I get precisely the JSON I want to send.
Then I try to use AFNetworking for my request:
AFHTTPClient *putClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://creativemind.appspot.com"]];
[putClient setParameterEncoding:AFJSONParameterEncoding];
NSMutableURLRequest *putRequest = [putClient requestWithMethod:#"PUT"
path:#"/notifications"
parameters:container];
AFHTTPRequestOperation *putOperation = [[AFHTTPRequestOperation alloc] initWithRequest:putRequest];
[putClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[putOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response: %#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[putOperation start];
When I try this solution, I get the following error message:
2013-08-10 16:10:40.741 NotificationReader[1132:c07] Error: Error
Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in
(200-299), got 400" UserInfo=0x75c89e0
{NSLocalizedRecoverySuggestion={"cause":null,"class":"java.lang.NumberFormatException","localizedMessage":"null","message":"null"},
AFNetworkingOperationFailingURLRequestErrorKey=http://creativemind.appspot.com/notifications>,
NSErrorFailingURLKey=http://creativemind.appspot.com/notifications,
NSLocalizedDescription=Expected status code in (200-299), got 400,
AFNetworkingOperationFailingURLResponseErrorKey=}
I think it's important to note that when I try the same request with nil parameters, I get te same error message. I also tried to perform a GET request on the same JSON to check if I can reach it and it works perfectly. I really have no idea what I could do. Any help would be appreciated
I am building an iphone app with a rails-backed server. I am using the devise gem. I am having trouble with user logins on the client-side (everything works on the web side, and even in the terminal with CURL).
On Xcode I can create a user and I can login. After logging in
(and recieving this in the log: "User logged in!")
I am then pushed to the indexViewController- and here I receive an error that the posts don't load. The reason is because on the post_controller.rb I have a
before_filter :authenticate_user!
preventing the posts from loading. The problem is, that the auth_token which was generated upon a successful login, is not being stored and passed along to the different views. So then, in the log I get:
'You need to sign in before continuing.'
As if the first part of what I just explained never happened..
In the indexViewController viewDidLoad method I have:
if (![[APIClient sharedClient] isAuthorized]) {
LoginViewController *loginViewController = [[LoginViewController alloc] init];
[self.navigationController pushViewController:loginViewController animated:YES];
}
isAuthorized is a BOOL in the APIClient that checks if userID>0
In the user model this is the code that creates a login session
+ (void)loginUser:(NSString *)signature
email:(NSString *)email
password:(NSString *)password
block:(void (^)(User *user))block
{
NSDictionary *parameters = #{ #"user": #{
// #"signature": signature,
#"email": email,
#"password": password
}
};
[[APIClient sharedClient] postPath:#"/users/sign_in" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
User *user = [[User alloc] initWithDictionary:responseObject];
if (block) {
block(user);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
if (block) {
block(nil);
}
}];
}
I am guessing it is here that I am missing some auth_token implementation? Since it is generated automatically by devise- I am not sure how to tell xcode to remember it. The auth_token is a string that has a column in the user table on the db. Should I add auth_token as param to the dictionary that holds the user's email and username? Or how do I get the token to persist?
Any ideas would be helpful.
Not being intimately familiar with AFNetworking this is a stab in the dark, but presumably you need to set the token for subsequent requests. Assuming APIClient is a wrapper you've added around AFHTTPClient, here's a quick idea of what that might look like after reviewing the code here - AFHTTPClient.
+ (void)loginUser:(NSString *)signature
email:(NSString *)email
password:(NSString *)password
block:(void (^)(User *user))block {
NSDictionary *parameters = #{ #"user": #{ #"email": email,
#"password": password } };
[[APIClient sharedClient] postPath:#"/users/sign_in"
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
User *user = [[User alloc] initWithDictionary:responseObject];
// retrieve and save auth token
NSString *token = [responseObject objectForKey:#"authToken"];
[[APIClient sharedClient] setAuthorizationHeaderWithToken:token];
if (block) {
block(user);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
if (block) {
block(nil);
}
}];
}
A colleague and me have stumbled upon bug in both of our codes. We happen to both be working on a login page. I am using the networking library AFNetworking to connect to an outside source, which tells if the user has actually registered. The friend likewise (but without any outside library). (Code is for ios.)
We are both running into the same problem: after making a valid call, everything goes as expected. But if we make another call that is invalid (e.g., wrong username and/or password) I am still able to login.
Why? We are not saving any information.
Here is some code:
BasicAuth * manger = [BasicAuth sharedManager];
manger = nil;
[[BasicAuth sharedManager] setUsername:#"bad_username" andPassword:#"wrong"];
[[BasicAuth sharedManager] getPath:#"/users/tokens"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"operation = %#", [responseObject description]);
NSError *error = nil;
/*
*/
if (error) {
NSLog(#"Error serializing %#", error);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.response.statusCode == 500) {
} else {
NSData *jsonData = [operation.responseString dataUsingEncoding:NSUTF8StringEncoding];
// NSString *errorMessage = [ objectForKey:#"error"];
NSLog(#"We have an issue %#", [error description]);
}
}]; return YES;
If I call this same code but with a correct username/password AND THEN call the above code, then I can still login in and the information I get back is the same as when I sent the "good" request. Also, when I try to make login requests using curl from my laptop I do not run into the same issues, which is making me think that, for some reason, the information being sent is not being updated. But I can't see how or why. Again, this is whether 3rd party library is being used or not.
Thoughts, suggestions?
note: BasicAuth (see above) is a subclass of AFHTTPCLIENT
Your server may be sending some cookie and iOS is store it.
Try to delete the cookies before setting the new username and password.
Here you have a code snippet that deletes all cookies. Keep in mind that in iOS the cookies are application private, so deleting them is not going to affect other apps.
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [storage cookies];
for (NSHTTPCookie *cookie in cookies)
{
[storage deleteCookie:cookie];
}
I am converting my app routines from ASIHTTP to AFNetworking due to the unfortunate discontinuation of work on that project ... and what I found out later to be the much better and smaller codebase of AFNetworking.
I am finding several issues. My code for ASIHTTPRequest is built as a method. This method takes a few parameters and posts the parameters to a url ... returning the resulting data. This data is always text, but in the interests of making a generic method, may sometimes be json, sometimes XML or sometimes HTML. Thus I built this method as a standalone generic URL downloader.
My issue is that when the routine is called I have to wait for a response. I know all the "synchronous is bad" arguments out there...and I don't do it a lot... but for some methods I want synchronous.
So, here is my question. My simplified ASIHTTP code is below, followed by the only way i could think of coding this in AFNetworking. The issue I have is that the AFNetworking sometimes does not for the response before returning from the method. The hint that #mattt gave of [operation waitUntilFinished] totally fails to hold the thread until the completion block is called... and my other method of [queue waitUntilAllOperationsAreFinished] does not necessarily always work either (and does NOT result in triggering the error portion of the [operation hasAcceptableStatusCode] clause). So, if anyone can help, WITHOUT The ever-present 'design it asynchronously', please do.
ASIHTTP version:
- (NSString *) queryChatSystem:(NSMutableDictionary *) theDict
{
NSString *response = [NSString stringWithString:#""];
NSString *theUrlString = [NSString stringWithFormat:#"%#%#",kDataDomain,kPathToChatScript];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:theUrlString]];
for (id key in theDict)
{
[request setPostValue:[theDict objectForKey:key] forKey:key];
}
[request setNumberOfTimesToRetryOnTimeout:3];
[request setAllowCompressedResponse:YES];
[request startSynchronous];
NSError *error = [request error];
if (! error)
{
response = [request responseString];
}
return response;
}
AFNetworking version
- (NSString *) af_queryChatSystem:(NSMutableDictionary *) theDict
{
NSMutableDictionary *theParams = [NSMutableDictionary dictionaryWithCapacity:1];
for (id key in theDict)
{
[theParams setObject:[theDict objectForKey:key] forKey:key];
}
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:kDataDomain]];
NSMutableURLRequest *theRequest = [httpClient requestWithMethod:#"POST" path:[NSString stringWithFormat:#"/%#",kPathToChatScript] parameters:theParams];
__block NSString *responseString = [NSString stringWithString:#""];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:theRequest] autorelease];
operation.completionBlock = ^ {
if ([operation hasAcceptableStatusCode]) {
responseString = [operation responseString];
NSLog(#"hasAcceptableStatusCode: %#",responseString);
}
else
{
NSLog(#"[Error]: (%# %#) %#", [operation.request HTTPMethod], [[operation.request URL] relativePath], operation.error);
}
};
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[queue addOperation:operation];
[queue waitUntilAllOperationsAreFinished];
[httpClient release];
return responseString;
}
Thanks very much for any ideas.
- (void)af_queryChatSystem:(NSMutableDictionary *) theDict block:(void (^)(NSString *string))block {
...
}
Now within the completionBlock do:
block(operation.responseString);
block will act as the delegate for the operation. remove
-waitUntilAllOperationsAreFinished
and
return responseString
You call this like:
[YourInstance af_queryChatSystem:Dict block:^(NSString *string) {
// use string here
}];
Hope it helps. You can refer to the iOS example AFNetworking has
I strongly recommend to use this opportunity to convert to Apple's own NSURLConnection, rather than adopt yet another third party API. In this way you can be sure it won't be discontinued. I have found that the additional work required to get it to work is minimal - but it turns out to be much more robust and less error prone.
My solution is manually to run the current thread runloop until the callback have been processed.
Here is my code.
- (void)testRequest
{
MyHTTPClient* api = [MyHTTPClient sharedInstance]; // subclass of AFHTTPClient
NSDictionary* parameters = [NSDictionary dictionary]; // add query parameters to this dict.
__block int status = 0;
AFJSONRequestOperation* request = [api getPath:#"path/to/test"
parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// success code
status = 1;
NSLog(#"succeeded");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failure
status = 2;
NSLog(#"failed");
}];
[api enqueueHTTPRequestOperation:request];
[api.operationQueue waitUntilAllOperationsAreFinished];
while (status == 0)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate date]];
}
STAssertEquals(status, 1, #"success block was executed");
}