My AFJSONRequestOperation is hitting the failure block on a 200 response. Is this because I have additional JSON?
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *d = (NSDictionary *)responseObject;
bool required = [d[#"payment_required"] boolValue];
[self.delegate paymentRequired:required];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
int statusCode = operation.response.statusCode;
NSLog(#"status code: %d response: %#", statusCode, operation.responseString);
if (operation.response.statusCode == 402) {
[self.delegate paymentRequired:true];
return ;
}
[self handleOperationFailed:operation action:^{
[self determinePaymentRequired];
}];
}];
yields in console
status code: 200 response: {'payment_required':'false'}
Why is this happening?
Based on my experience, there are 3 condition you need to meet to hit the success block:
200 response code
a proper JSON object included in the response
the Content-Type of the response is set to application/json
Hope this can help you.
Your JSON is not valid. It has single quotes ({'payment_required':'false'}), but JSON format needs them to be double ones:
{
"payment_required" : false
}
PS: I also removed quotes on false, because false is a valid value (and it's preferred).
Related
only some api call needs the token. and when 401 occurs refresh token call will be taken place.and for each call the token is refreshing. how to execute more than 1 api synchronously when 401 occurs
This is upto you, how you design the flow but I did almost same problem like following in Objective C
Call method of like in my case userProfileGETRequest
Before calling userProfileGETRequest Check date if token gets expired(in your case may be status code == 401)
If Token not expired simply call API in my case userProfileAPI with last token
If Token Expired then Call Refresh token and with Success and Failure Callback
If successful refresh token, call the userProfileAPI API with updated refresh token.
+ (void) userProfileGETRequest:(NSDictionary *)headerParams urlQuery: (NSString*)action parameters:(NSDictionary*)params docOpenPassword: (NSString*)password docOpenOtp: (NSString*)otp
onComplete:(void (^)(id json, id code, id url))successBlock
onError:(void (^)(id error, id code, id url))errorBlock {if ([[SingletonSDK sharedInstance] isTokenExpired:[NSDate date]]) {[self refereshToken:nil :^(id json, id code) {
[[SingletonSDK sharedInstance] handleLoginResponseObject:json];
[self userProfileAPI:headerParams urlQuery:action parameters:params
onComplete:^(id json, id code, id url) {
successBlock(json, code, url);
} onError:^(id error, id code, id url) {
errorBlock(error, code, url);
}];
} onError:^(id error, id code) {
[[SingletonSDK sharedInstance] hideProgessHud];
return ;
}];
}
} else {
[self userProfileAPI:headerParams urlQuery:action parameters:params
onComplete:^(id json, id code, id url) {
successBlock(json, code, url);
} onError:^(id error, id code, id url) {
errorBlock(error, code, url);
}];
}}
//userProfileAPI Methods
+ (void) userProfileAPI:(NSDictionary *)headerParams urlQuery: (NSString*)action parameters:(NSDictionary*)params
onComplete:(void (^)(id json, id code, id url))successBlock
onError:(void (^)(id error, id code, id url))errorBlock
{
NSString *authorizationValue = [self setAuthorizationValue:action];
NSString *language = [self editedLanguageNameAsApiRequired];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//set headers values
[manager.requestSerializer setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[manager.requestSerializer setValue:language forHTTPHeaderField:#"Accept-Language"];
[manager.requestSerializer setValue:authorizationValue forHTTPHeaderField:#"authorization"];
[manager GET:action parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"getRequest response success");
NSString *url = [[[operation response] URL] absoluteString];
NSInteger statusCode = [operation.response statusCode];
NSNumber *statusObject = [NSNumber numberWithInteger:statusCode];
successBlock(responseObject, statusObject, url);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSString *url = [[[operation response] URL] absoluteString];
NSInteger statusCode = [operation.response statusCode];
NSNumber *statusObject = [NSNumber numberWithInteger:statusCode];
if ([self takeDesiredActionIfAccessTokenExpired:statusCode]) {
return ;
}
id responseObject = operation.responseData;
id json = nil;
id errorMessage = nil;
if ([statusObject integerValue] == 404) {
errorMessage = [[SingletonSDK sharedInstance] getStringValueFromLanguageKey: COMMON_ERROR_SHARED_PREFERENCES];//NSLocalizedString(COMMON_ERROR_RESOURCE_NOT_FOUND, nil);
} else {
if (responseObject) {
json = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
errorMessage = [(NSDictionary*)json objectForKey:#"Message"];
}else{
json = [error.userInfo objectForKey:NSLocalizedDescriptionKey];
errorMessage = json;
}
}
if(![errorMessage isKindOfClass:[NSString class]]){
errorMessage = [[SingletonSDK sharedInstance] getStringValueFromLanguageKey: COMMON_ERROR_MSG] ; //NSLocalizedString(COMMON_ERROR_MSG, nil);
}
}];
}
I have a method, for authorizing user. I need Basic authorization.
NSString *url = [NSString stringWithFormat:#"%#/rest/api/person/auth", host];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager setRequestSerializer:[AFHTTPRequestSerializer serializer]];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername:_loginField.text password:_passwordField.text];
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self parseResponseForUser:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error %# ",error);
}];
The main problem here is determining error type. I may have error for authorization and error for network connection problem (host is not reachable).
When login and password don't match criteria, failure block runs. For example, If I put wrong password and login I take this error message.:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be
completed. (Cocoa error 3840.)" (JSON text did not start with array or
object and option to allow fragments not set.)
How should i catch error types?
Finally found answer, may be it will be helpful for someone. I just needed to use:
NSInteger statusCode = operation.response.statusCode;
And i can catch it like:
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"response:%#", responseObject);
[self parseResponseForUser:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = operation.response.statusCode;
if(statusCode == 401) {
} else if (statusCode == 404) {
}
}];
In AFNetworking 3.0+ and in the case of an error, you can access the status code in the failure block's error.userInfo object:
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSHTTPURLResponse *response = error.userInfo[AFNetworkingOperationFailingURLResponseErrorKey];
NSInteger statusCode = response.statusCode;
// Do something with the status code
}];
you can give a try to get code from error and then show messages accordingly.
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = error.code;
if(statusCode == -1001) {
// request timed out
} else if (statusCode == -1009 || statusCode || -1004) {
// no internet connectivity
}
}];
similarly you can check for other code.
Here's how I do it.
[self.httpClient GET:#"someAPI"
parameters:parametersDictionary
success:^(NSURLSessionDataTask *task, id responseObject) {
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
NSInteger statusCode = [response statusCode];
switch (statusCode) {
case 404:
break;
default:
break;
}
}];
Improved response of alok srivastava by using NSURLError enum:
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = error.code;
if(statusCode == NSURLErrorTimedOut) {
// request timed out
} else if (statusCode == NSURLErrorNotConnectedToInternet || statusCode || NSURLErrorCannotConnectToHost) {
// no internet connectivity
}}];
Here is how to do it in Swift
((NSURLSessionDataTask, NSError) -> Void) = { (sessionDataTask :NSURLSessionDataTask, responseError : NSError) -> Void in
let response = sessionDataTask.response as! NSHTTPURLResponse
switch (statusCode) {
case 404:
// do stuff
case 401:
// do stuff
default:
break;
}
}
It looks like your server might respond with HTML, or it might respond with JSON. But when you type:
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];
You're telling AFNetworking to expect JSON.
Instead, try telling it to handle a regular HTTP response if it's not JSON:
NSArray *serializers = #[[AFJSONResponseSerializer serializer], [AFHTTPResponseSerializer serializer]];
AFCompoundSerializer *compoundResponseSerializer = [AFCompoundSerializer compoundSerializerWithResponseSerializers:serializers];
[manager setResponseSerializer:compoundResponseSerializer];
Now, if the JSON serializer fails, the request will be passed to the AFHTTPResponseSerializer, which should call your failure block with the appropriate HTTP error code instead of the JSON parsing error.
Incidentally, AFHTTPResponseSerializer is subclass-able, so feel free to take a look at that option if you want more specific behavior.
It happens frequently, that servers may send a response in a different content type than requested IF they are sending an error.
For example when sending a request with JSON as Content-Type and expecting a JSON response from the server, one would specify the following request headers:
Content-Type: application/json
Accept: application/json
When the request fails due to an authentication error, the server may send you a status code of 401 (Unauthorized) plus an optional response which contains relevant diagnostic information.
Strictly, web servers should respect the Accept header, but unfortunately, some don't and will send a "standard" error response in text/html for example. The details should be specified in the API, though.
Your implementation should handle that case gracefully. That is, your response handler must encode (or parse) the response data according the Content-Type of the response, say text/html or ignore it if suitable. More precisely, you always should query the HTTP status code AND the content type and then make an informed decision how you want to treat the response.
See Aron Brager's answer how to solve that issue with AFN.
your Json must be:
{
"access" : "1",
"admin" : "0",
"code" : "constantine2",
...
"positions" : [
{
"departmentID" : "992c93ee-2fa7-4e53-be5f-4e32a45ba5e6",
"departmentName" : "Dev-C++ resources page (libraries, sources, updates...)",
....
}
],
"userid" : "3b660c13-b856-41fa-a386-814a7b43bacc"
}
I'm sending a NSMutalbeDictionary User with the correct information.
I have sending post using these two methods.
+ (void)loginUsers:(NSMutableDictionary*)user {
NSString *endpoint = #"api/users/sign_in";
[self postEndpoint:endpoint params:user completionBlock:^(TSystemsResponse *response) {
}];
}
+ (void)postEndpoint:(NSString *)endpoint params:(NSMutableDictionary *)params completionBlock:(void (^)(TSystemsResponse *response))completionBlock {
[[TSystemsAPIClient sharedClient] postPath:[NSString stringWithFormat:#"/%#", endpoint] parameters:params success:^(AFHTTPRequestOperation *request, id responseObject) {
TSystemsResponse *resp = [[TSystemsResponse alloc] initWithDictionary:responseObject];
completionBlock(resp);
} failure:^(AFHTTPRequestOperation *request, NSError *error) {
NSLog(#"Request to /%# FAILED: %#", endpoint, error);
completionBlock(nil);
}];
}
I'm getting a response from the server that says the following
{
"success": false,
"message": "Missing user parameter"
}
So i'm thinking that I'm not sending the information through correctly. The API call works in Post man.
Double check the params variable actually has the key "user" and not uppercased:
NSLog(#"%#", params);
[self postEndpoint:endpoint params:params completionBlock:nil];
If that is not the case you have to show us the innards of TSystemsAPIClient postPath:parameters:success: method.
Trying to migrate my code from ASIHttpRequest to AFNetworking. It seems similar questions has been asked but couldnt find solution to my problem.
My code was working fine with ASIHttpRquest.
I send a simple post request to my server and listen http responses. If http response is 200 everything works fine but if I send another status code >400 AFNetworking block fails.
Server side response:
$rc = $stmt->fetch();
if ( !$rc ) {
// echo "no such record\n";
$isrecordExist=0; //false does not exists
sendResponse(403, 'Login Failed');
return false;
}
else {
// echo 'result: ', $result, "\n";
$sendarray = array(
"user_id" => $result,
);
sendResponse(200, json_encode($sendarray));
}
IOS Part:
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:
[NSURL URLWithString:server]];
client.allowsInvalidSSLCertificate=YES;
[client postPath:loginForSavingCredientials parameters:params success:^(AFHTTPRequestOperation *operation, id response) {
if (operation.response.statusCode == 500) {}
else if (operation.response.statusCode == 403) {}
else if (operation.response.statusCode == 200) {//able to get results here NSError* error;
NSString *responseString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary* json = [NSJSONSerialization JSONObjectWithData: [responseString dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure %#", [error localizedDescription]);
}];
NSLOG:
failure Expected status code in (200-299), got 403
How can I fix this?
When AFNetworking gets a 2xx (success) status code, it calls the success block.
When it gets a 4xx (client error) or 5xx (server error) status code, it calls the failure block because something went wrong.
So all you should need to do is move your check for a 500 or 403 status code to the failure block.
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:server]];
client.allowsInvalidSSLCertificate=YES;
[client postPath:loginForSavingCredientials parameters:params success:^(AFHTTPRequestOperation *operation, id response) {
if (operation.response.statusCode == 200) {//able to get results here NSError* error;
NSString *responseString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary* json = [NSJSONSerialization JSONObjectWithData: [responseString dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure %#", [error localizedDescription]);
if (operation.response.statusCode == 500) {}
else if (operation.response.statusCode == 403) {}
}];
When you create the request operation you need to tell it which response status codes are acceptable (mean success). By default this is codes in the range 200 -> 299.
Setup before you start using the client:
AFHTTPRequestOperation.acceptableStatusCodes = ...;
[client postPath:
Docs are here.
Anyone?): I'm having a problem that has made me scratch my head for the last 2 hours, and it most likely a very simple stupid thing I'm missing. I Keep getting a building error when I Call the response string from the operation # AFNetworking... Like there is NO SUCH PROPERTY....
Please Take a look at my code and Explain me what did I Mess up This time :p.. THanks :)
NSDictionary* paramDict = [NSDictionary dictionaryWithObjectsAndKeys:WebServicemd5Value, WebSermd5Variable, nil]
;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:webServiceURL]];
[httpClient defaultValueForHeader:#"Accept"];
[httpClient postPath:#"method" parameters:paramDict success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response data: %#", responseObject);
NSLog(#"Reponse String: %#", operation);
// Printing operation will show me the operation Dictionary, including the reponse field, // but when I Directly call operation.response, the Compiler won't Build, stating that // "Property not found for AFHTTPRequestOperation".... WEIRDEST THING EVER, right?
NSString* responseString = [NSString stringWithUTF8String:[responseObject bytes]];
//.. Rest o f my Code....
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error retrieving data: %#", error);
}];
Hernan, if you expect an NSDictionary from a JSON response you should consider using AFJSONRequestOperation, because you get a JSON dictionary in your success callback. Anyway, if you want to get a dictionary from your responseObject, try to use the following code:
NSError *error = nil;
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:&error];
if (error) {
NSLog(#"Error serializing %#", error);
}
NSLog(#"Dictionary %#", JSON);
I believe the response string is inside the "operation" object, so something like:
...
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error retrieving data: %#", operation.responseString);
}];
While attempting to retrieve content from meetup.com api using AFNetworking (kudos to Mattt T. for a great framework, btw), ran into the same error - "The operation couldn't be completed. (Cocoa error 3840)". Realized that the issue I was having was with the response data containing a Swedish character 'Ø', resulting in the parsing error. The solution was to include the header 'Accept-Charset: utf-8' in the initialization of the AFNetworking client. Fixed!
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
[self setDefaultHeader:#"Accept-Charset" value:#"utf-8"];
return self;
}