Is there a possibility that AFNetworking takes API status code in consideration as well?
Call returns 200 HTTP code that it is ok, but status code is 406 which indicates failure.
This results to AFNetworking success block being called, even though API call failed.
Is there a solution to this?
Code:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSString *APICallURL = [NSString stringWithFormat:#"%#/%#", SERVER_URL, #"something.php"];
[manager GET:APICallURL parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success();
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (failure) {
failure(error);
}
}];
X-API-Status-Code is a totally non-standard header (as any header starting with X-... actually, by definition).
That header is a proprietary way that your specific server chose to report the API status code. It could have picked any other proprietary header to do so, maybe calling it X-API-ReturnCodeor whatnot.
So I doubt that AFNetworking will ever support this natively.
But you can probably create your own AFURLResponseSerialization subclass and implement validateResponse, so that your code check for this proprietary header and act accordingly.
It will not reach failure block for any failure status code from server. With respect to AFNetworking, network call is successful with a error code from server.
It will reach failure block only when we are not able to successfully finish the network call, like
1) Time out error
2) Server down and not able to reach
3) Network loss etc
Solution is we should check the status code of network call in success block and identify error responses from server
Related
I'm trying to run my iOS app, having it pull data from the node.js server I have running locally.
The problem I'm having is that when I use AFHTTPSessionManager's GET method, it keeps on timing out.
I've tried doing the request from my browser and my locally running node server is returning the json as expected. I've also tried curling from the cl and it also returns as expected.
Is there something I'm missing?
Here's my test code:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:#"http://192.168.33.327/api/v1/item" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(#"Error: %#", error);
}];
I've also tried initializing the manager with a base url, then just passing in the path to GET but it still times out. (the error I get in error says the error code is -1001)
Also you'll notice there is no port number on the code I've pasted. at first I had it on port 8080, but thought that might be causing a problem, so I ran node on port 80 instead, but it still didn't help.
Thanks!
I'm using AFNetworking to process HTTP requests in my iOS app. I have run into a stumbling block. I cannot be certain of what the response content type will be, but you have to set the response serializer BEFORE the request is processed. This means I could do an API request, expecting an image back, but actually there's some authentication error, so the server returns a JSON-formatted response instead.
Here's my code:
AFHTTPRequestOperation* op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[op setResponseSerializer:[AFJSONResponseSerializer serializer]]; // ??????
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSDictionary* result = (NSDictionary*) responseObject;
onSuccess((NSArray*) result);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
onFailure(error);
}];
[op start];
As you can see, I've had to set the expected content type implicitly by setting the responseSerializer to [AFJSONResponseSerializer serializer]. So if I get something else back, it causes an error, even though I may still wish to parse an process that response when dealing with the error.
So my question is, should I just use the standard AFHTTPResponseSerializer, examine the response status code and then process the response body manually (as json, xml, html an image etc)?
Set the accepted content types you want on the serialiser with acceptableContentTypes:
AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];
serializer.acceptableContentTypes = [NSSet setWithArray:#[#"text/plain", #"text/html"]];
[op setResponseSerializer:serializer];
From the docs:
By default, AFJSONSerializer accepts the following MIME types, which
includes the official standard, application/json, as well as other
commonly-used types:
application/json
text/json
You don't have to use AFJSONResponseSerializer, as you can create your own serializer as long as it conforms to the AFURLResponseSerialization protocol.
If you have JSON responses but XML error responses, you could just subclass AFHTTPResponseSerializer and do your own processing in there.
You could also use AFCompoundResponseSerializer to parse different response types just going through the serializers you give it.
Your API is a little unusual: if you aren't authorized, it should just use an HTTP 401 response, not JSON. But there's plenty of unusual API's out there and I bet you don't have control over this one.
The fix is straightforward:
Make an implementation of AFURLResponseSerialization that just acts as a proxy, and assign that one as the serializer for your request. When the response comes in, have it take a quick look at the data and then instantiate and call the right serializer.
We ran into the following issue with our app that uses AFNetworking 2.0.
When using AFHTTPRequestOperationManager's GET method, we got an error NSURLErrorDomain code -1012. The request used HTTPS and the server does not require user authentication. The request never reached the server by the way.
We have run several tests and this is the first time the error was produced and we are wondering how this error can get produced because it does not seem relevant.
Setup of AFHTTPRequestOperationManager :
httpOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:
[NSURL URLWithString: HTTPS_URL)]];
httpOperationManager.responseSerializer =
[AFXMLParserResponseSerializer serializer];
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled: YES];
GET REQUEST
AFHTTPRequestOperation *op =[httpOperationManager GET:
[NSString stringWithFormat:SOME_PATH]
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//code to setup NSXMLParser ...
}
failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error %#", [error localizedDescription]);
}];
I think you already solved the problem, but if you are trying to authenticate in a server that doesn't have a valid certificate you have to set YES for property allowInvalidCertificates in your AFHTTPRequestOperationManager object:
[yourManager.requestSerializer setAuthorizationHeaderFieldWithUsername:#"your_username" password:#"your_password"];
[yourManager.securityPolicy setAllowInvalidCertificates:YES];
Also, as #a1phanumeric said, it can be necessary to include this line:
[yourManager.securityPolicy setValidatesDomainName:NO];
Cheers.
NSURLErrorDomain -1012 is NSURLErrorUserCancelledAuthentication. (See the error code list and search for -1012.)
You state, "the server does not require user authentication". But this error would not be called if that were true.
Possible causes:
Your server is erroneously requesting authorization (a server bug)
The URL formed with HTTPS_URL and SOME_PATH is not what you expect, and some other server is requesting authorization
Some intermediary (like a proxy server, or an access point) is requiring authorization.
Some debugging tips:
Set breakpoints inside the AFNetworking implementation to see which URL is being hit
Configure AFHTTPRequestOperationLogger so you can see the actual request body and response in your console log
Make the same request with curl or Advanced Rest Client and observe the server's response
Side note: I think [NSString stringWithFormat:SOME_PATH] is pointless - why not just use SOME_PATH?
I am building my first iOS app.
I have got the backend code done, but I am struggling with the Objective-C part of it.
I have a signup / login page.
But I don't know how to send that data to my server using Objective C.
I have read that AFNetworking is good, but I was wondering how I could use that for user login .
I have downloaded and added AFNetworking to my XCode Project and set up headers.
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://examplewebsite.com]];
[client setDefaultHeader:#"key" value:#"value"];
[client setAuthorizationHeaderWithUsername:#"username" password:#"password"];
[client setAuthorizationHeaderWithToken:#"token"];
NSURLRequest *request = [client requestWithMethod:#"someMethod" path:#"somePath" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
but I am still lost.
Since you're trying to login to your own API, you don't want setAuthorization stuff. That's for basic HTTP auth. Instead you want to use getPath:parameters:success:failure or the postPath version, depending on if your backend is expecting HTTP GET or HTTP POST.
Pass your userid / password in the parameters argument. You should set parameterEncoding to be the correct format. You're probably using HTTP Forms url encoding, or JSON. Whatever your backend expects.
You don't want to set the authorization headers in this case, since this is for "basic access HTTP authentication", which is a method for a HTTP user agent to provide a user name and password when making a request to a server.
You want to use your own API and interact with a restful server and therefore, I would recommend, that you subclass AFHTTPClient -> interact with an API, Web Service, or Application. - Take a look at the examples in the AFNetworking zip archive, if you have difficulties in subclassing AFHTTPClient.
Since you want to create an app with user login, the app needs to send these information to your server, and the server should return if the login was succesful or not.
This can be done like so - HTTP POST.
- (void)login {
// Login information from UITextFields
id params = #{
#"username": self.usernameField.text,
#"password": self.passwordField.text
};
//Call the AFHTTP subclass client, with post block. postPath = the path of the url, where the parameters should be posted.
[[theAuthAPIClient sharedClient] postPath:#"/login"
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//handle succesful response from server.
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// handle error - login failed
}
}];
}
You need to pass the parameters in the right format, depending on what format your server expects. This can be done by setting the right encoding in your AFHTTPClient subclass -> ParameterEncoding
Since I came here while searching for a working solution for AFNetworking 2.0, not knowing that AFHTTPClient was removed from the Framework, I will post the new way to establish this connection here:
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:#"http://examplewebsite.com"]];
[manager setRequestSerializer:[AFHTTPRequestSerializer serializer]];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername:#"userName" password:#"password"];
The API I'm developing against requires me to present an authentication token in a custom HTTP header. This token expires every few minutes, and could happen while the user is still within the app as long as they have been idle long enough. When the token has expired I receive a 403 response but I only find out after attempting a request.
What's the best way to get RestKit to automatically reauthenticate and retry the request so I don't have to put in this logic everywhere I make a request? Responses to similar questions have suggested using the RKRequestDelegate protocol or the RKObjectLoaderDelegate protocol but unfortunately these are no longer part of RestKit (as of 0.20).
Any idea what the "correct" approach should be now? Should I be subclassing RKObjectManager and tacking on a retry to each of the request operations or should I provide a custom HTTPOperation or HTTPClient class or is there some better approach altogether? Thanks!
Catch it in Failure block , and check for the status code and re-do the authentication
RKObjectRequestOperation *requestOp = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[getObjResp]];
[requestOp setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
....
}
} failure:^(RKObjectRequestOperation *operation, NSError *error){
// Here your status code check
// Here your retry-code
}