I accidentally mistyped the post path and noticed that although it's being wrong, the success block is called:
[[APIClient sharedInstance]
postPath:#"api_url"
parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Result: Success %#",[responseObject description]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//handle error
NSLog(#"Result: Failure + %#",error.userInfo);
}];
Of course the data are not being sent to server and the transaction is not processed, but I want to know why it's not the failure block which is supposed to be called in case the path is wrong? Thanx.
Failure is called if the requestOperation has an associated error after finishing. Reasons for an error include the response having the incorrect Content-Type, not having an acceptable status code (2XX range, by default), or an error processing the downloaded data.
Why your server returned a 200 response with the correct content type is a question only something you can determine.
Related
[[RKObjectManager sharedManager] getObjectsAtPath:#"/mypath/objects" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// Use objects that are returned.
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// Handle error
}];
This method of RestKit will perform a request, mapping, saving to core data, and performing the success callback AFTER saving. What I want to know is the method I can use to get the response and perform a callback BEFORE RestKit automatically saves it in core data. I still want to be able to use the ability of RestKit to do the mapping and saving but I want to intercept or at least get a callback BEFORE it performs saving of data.
I found a way to do it by implementing the callback block of the RKManagedObjectRequestOperation before the mapping begins since this will be called after a successful HTTP response and before the automatic saving to core data:
[managedObjectRequestOperation setWillMapDeserializedResponseBlock:^id(id deserializedResponseBody) {
//do something
return deserializedResponseBody;
}];
I'm writing a small iOS client for a server protected with OAuth2.
I'm wondering if is it possible using AFOAuth2Manager [here] auto-refreshing the expired token.
The idea is that the logic for refreshing the client when the server responds with a 401, or raise an error when the refresh method returns a 401 should be quite common, so probably it is integrated in some library.
I created a subclass of AFOAuth2Manager
In this subclass I override this method:
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
return [self HTTPRequestOperationWithRequest:request
success:success
failure:failure
checkIfTokenIsExpired:YES];
}
calling a custom method with an additional parameter: checkIfTokenIsExpired. This is required in order to avoid infinite loops.
The implementation of this method is straigth forward: if we don't need to check the token just call the super class.
if (!checkIfTokenIsExpired) {
return [super HTTPRequestOperationWithRequest:request
success:success
failure:failure];
}
otherwise we perform the request with a custom failure block
else {
return [super HTTPRequestOperationWithRequest:request
success:success
failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.response.statusCode == ERROR_CODE_UNAUTHORIZED) { //1
[self reauthorizeWithSuccess: ^{ //2
NSURLRequest *req = [self.requestSerializer requestByAddingHeadersToRequest:request]; //3
AFHTTPRequestOperation *moperation = [self HTTPRequestOperationWithRequest:req //4
success:success
failure:failure
checkIfTokenIsExpired:NO];
[self.operationQueue addOperation:moperation]; //5
} failure: ^(NSError *error) {
failure(nil, error);
}];
}
else {
failure(operation, error); //6
}
}];
}
//1: check the http status code, if 401 try to automatically re-authorize.
//2: reauthorize is a private mathod that uses AFOAuthManager to refresh the token.
//3: In this case we are re-authorized with success and we want to resubmit a copy of the previous request. The method requestByAddingHeadersToRequest: just copy all the header fields from the previous request.
//4: Create a copy of the previous request, but this time the last parameter is false because we don't want check again! The successBlock and failureBlock are the same of the previous request.
//5: Add the operation to the queue.
//6: If the reauthorize method fails just call the failure block.
Unfortunately I didn't found any framework for solve this problem so I wrote a short wrapper around AFNetworking (if someone is interested I can publish on github)
The logic is to execute the request, and in case of http response 401, try to refresh the auth-token and when it's done to re-execute the previous request.
I was searching an answer for this problem and "Matt", the creator of AFNetworking, suggest this:
the best solution I've found for dealing with this is to use dependent
NSOperations to check for a valid, un-expired token before any
outgoing request is allowed to go through. At that point, it's up to
the developer to determine the best course of action for refreshing
the token, or acquiring a new one in the first place.
Simple, but effective?, trying now, will edit with report...
Swift solution with Alamofire 4.0. Based on RequestAdapter and RequestRetrier protocols: example link
I am trying to figure out how long my round trip to the server is before any mapping occurs. I need to get at the RKObjectRequestOperation, but it is only available in the success and fail blocks.
I see that RestKit 2 does send a notification:
[[NSNotificationCenter defaultCenter] postNotificationName:RKObjectRequestOperationDidStartNotification object:weakSelf];
But there is no user info sent along.
Any ideas on how I can do this? I was thinking of an associated object onto the operation queue but that is causing crashes.
What I did:
self.op = self.objectManager.operationQueue.operations.lastObject;
objc_setAssociatedObject(self, &kRetrieverRequestOperationKey, self.op, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Added the above right after:
[self.objectManager getObjectsAtPath:resourcePath parameters:parmsDictionary success:^(RKObjectRequestOperation *requestOperation, RKMappingResult *mappingResult)
{
[weakSelf didLoadOperation:requestOperation result:mappingResult isFromCache:NO];
[weakSelf requestDidEnd:requestOperation];
} failure:^(RKObjectRequestOperation *requestOperation, NSError *error) {
[weakSelf requestOperation:requestOperation didFailWithError:error];
[weakSelf requestDidEnd:requestOperation];
}];
Then when RestKit posted its notification I was able to get at the RKObjectRequestOperation.
Not ideal, but seems to work.
With AFNetworking 2, when you're handling a failure in the failure block, how do you access the content returned from the server? In my case I'm posting to a Rails app that is returning:
{"number":["is already taken"]}
That's what I get if I use curl like this: curl -X POST -d "sales_order[number]=12345" http://localserver.dev/api/v1/sales_orders.json
I'm trying to get the same JSON within AFNetworking 2. After reading all over SO I managed to get access to some response header information by examining [error userInfo] inside of my failure block.
Does anyone know how I can access the {"number":["is already taken"]} from inside of the failure block?
This is my block currently:
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Sales Order Failure");
NSDictionary *userInfo = [error userInfo];
for(NSString *key in [userInfo allKeys]) {
NSLog(#"%# - %#", key, [userInfo objectForKey:key]);
}
}];
This is a known design deficiency in AFNetworking 2.x, and has been discussed here with some workarounds.
I am trying to make a put request to be able to change the parameters I'm passing in and I'm getting a 400 error.
Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in (200-299),
got 400" UserInfo=0x8a7d7d0 {NSLocalizedRecoverySuggestion={"messages":{"error":
[{"code":400,"message":"Server can not understand Content-Type HTTP header media type
\"application\/x-www-form-urlencoded\""}]}},
AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest
http://test.example.com/api/rest/customers/841>,
NSErrorFailingURLKey=http://test.example.com/api/rest/customers/841,
NSLocalizedDescription=Expected status code in (200-299), got 400,
AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x8a7e1e0>}
This is the code I have for my request.
ExampleHTTPClient *sharedClient = [ExampleHTTPClient sharedClient];
NSDictionary *parameter = #{#"email":#"example#gmail.com",#"firstname":#"Bob", #"lastname":#"Jones"};
[sharedClient setDefaultHeader:#"Content-Type" value:#"application/json"];
[sharedClient putPath:#"/api/rest/customers/841" parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response: %#",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#",error);
}];
I'm new to working with web service so my problem is understanding what is going on.
Any help or thoughts are appreciated and thank you.
While I can't explain your issue when I setup a AFNetworking client I used the following:
[self registerHTTPOperationClass: [AFJSONRequestOperation class]];
[self setDefaultHeader: #"Accept"
value: #"application/json"];
where self was an instance of a subclass of AFHTTPClient.
The HTTP Status Code of 400 is: 'Bad Request - The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.' Do you have access to the server logs to see that structure of the request?