How to get status code in AFNetworking - ios

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"
}

Related

How to intercept the response of AFNetworking request in Objective-C?

As titled, and how to check HTTP status of response?
For example, if the server returns http status code 403, I need to send a recall mail request again to take new access token.
Take a look at the below. In the failure block, retry/resend your query X number of times. Be sure to add logic to end retries at some point, so you don't end up with an infinite loop.
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager POST:url
parameters:object
success:^(AFHTTPRequestOperation *operation, id responseObject) {
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.response.statusCode == 403) {
// retry
}
}
];
This answer may also be helpful.

AFNetworking request header missing after being set

I'm attempting to set custom headers on a per-request basis using AFNetworking, but occasionally the headers will seemingly disappear after being set. Below is the code used to make a request...
+ (void) getWithURI: (NSString*) uri header: (NSDictionary*) header success: (NSString*) successCallback failure: (NSString*)errorCallback dispatch: (NSString*)dispatchedId
{
createManagerInstance();
AFHTTPRequestOperation* operation = [manager GET:uri
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[RestWrapper succeededWithJson:operation.responseString dispatchedId:dispatchedId successCallback:successCallback];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[RestWrapper failedWithJson:operation.responseString dispatchedId:dispatchedId errorCallback:errorCallback];
}];
NSMutableURLRequest* request = (NSMutableURLRequest*)operation.request;
for (NSString* key in header)
{
if([request valueForHTTPHeaderField:key] != nil)
{
[request setValue:header[key] forHTTPHeaderField:key];
}
else
{
[request addValue:header[key] forHTTPHeaderField:key];
}
}
NSLog(#"Headers: %#", request.allHTTPHeaderFields);
[operation start];
}
For 95% of my requests, they go through as anticipated. Sporadically, however, some will fail and indicate a header is missing. This has been confirmed by capturing the requests in question using Fiddler and seeing that the headers are actually missing. Despite this, the console log of request.allHTTPHeaderFields always shows the headers in place.
The only other thing I noticed is that in general Fiddler reports the caching policy as "max-age=0, private, must-revalidate" for each request. However, whenever a request loses the custom headers, it's caching policy is "no-cache".
This is because you're adding HTTP Header fields in wrong way.
You should add it before request. You may try something like this :
+ (void) getWithURI: (NSString*) uri header: (NSDictionary*) header success: (NSString*) successCallback failure: (NSString*)errorCallback dispatch: (NSString*)dispatchedId{
createManagerInstance();
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:header[key] forHTTPHeaderField:key];
AFHTTPRequestOperation* operation = [manager GET:uri
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[RestWrapper succeededWithJson:operation.responseString dispatchedId:dispatchedId successCallback:successCallback];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[RestWrapper failedWithJson:operation.responseString dispatchedId:dispatchedId errorCallback:errorCallback];
}];
[operation start];
}

AFJSONRequestOperation hitting failed block on 200 response.statusCode

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).

How to send a post request to api with user information without getting blank response.

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.

AFHTTPClient Expected status code in (200-299), got 409

I have strange issue with AFHTTPClient, I am sending POST request like
NSURL *u = [NSURL URLWithString:HTTP_SERVER];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL: u];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
[httpClient postPath:REGISTER
parameters:params
success:^(AFHTTPRequestOperation *operation, id JSON) {
int statusCode = [operation.response statusCode];
if(statusCode == 201){
[self performSegueWithIdentifier:#"register" sender:self];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"[HTTPClient Error]: %#", error.localizedDescription);
}];
And server works fine ( in some cases server needs to answer me with code 409, API is made in that way), but I get error in XCode like Expected status code in (200-299), got 409
How to solve this problem ( my hands are tied, I cannot change API and error code) ?
Based on this:
You've got two options:
add status code 409 to the list of acceptable status codes, and receive it in the success block (bad)
deal with status code 409 in the failure block (good)
For the latter, if your [operation.response statusCode] is 409, you would use the responseData from AFURLConnectionOperation (superclass of AFHTTPRequestOperation, you would need to import its header for this to work).

Resources