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).
Related
I have tried several StackOverflow questions, and I caanot find the correct answer on this. I am using the POSTMAN plugin for Chrome to check my REST calls and I cannot figure out why I cannot read the response. In the comments you will see all the different attempts I have made to get the response.
NSDictionary* session_params = #{SESSION_USERNAME_KEY:SESSION_USERNAME_VALUE, SESSION_PASSWORD_KEY:SESSION_PASSWORD_VALUE};
NSURL* url = [NSURL URLWithString:SESSION_URL];
RKObjectManager* objectManager = [RKObjectManager managerWithBaseURL:url];
//GET THE **** THING TO INTERPRET A TEXT response
//[RKMIMETypeSerialization registerClass:[RKXMLReaderSerialization class] forMIMEType:RKMIMETypeTextXML];
//[objectManager setAcceptHeaderWithMIMEType:#"text/html"];
//[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeTextXML];
//[RKMIMETypeSerialization registerClass:[RKXMLReaderSerialization class] forMIMEType:#"text/html"];
//[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"text/html"];
//[objectManager setRequestSerializationMIMEType:#"text/html"];
//END
NSMutableURLRequest* request = [objectManager requestWithObject:nil method:RKRequestMethodPOST path:SESSION_URL parameters:session_params];
RKObjectRequestOperation* operation = [objectManager
objectRequestOperationWithRequest:request success:^(RKObjectRequestOperation* operation, RKMappingResult* result)
{
NSLog(#"RESULT [%#]", result);
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"ERROR [%#]", error);
}];
[operation start];
I think the most irritating thing is that the stuff I need is contained in the NSLocalizedRecoverySuggestion value. It is a session key I require.
OUTPUT:
E restkit.network:RKObjectRequestOperation.m:547 Object request failed: Underlying HTTP request operation failed with error: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1016 "Expected content type {(
"application/x-www-form-urlencoded",
"application/json"
)}, got text/html" UserInfo=0x1c52aed0 {NSLocalizedRecoverySuggestion=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJCbG8uUmVnQWxlcnQuQnJva2VyIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdC9CbG8uUmVnQWxlcnQuQVBJL2FwaSIsIm5iZiI6MTM5MjY0MTY2MSwiZXhwIjoxMzkyNjQ1MjYxLCJ1bmlxdWVfbmFtZSI6IkJ1dHRvbnMiLCJyb2xlIjoiUmVnQWxlcnRDb25zdW1lciJ9.JCTMGJRKlOxEtNrcGodpce-tqsRS4zlApNisKQW6iSw, AFNetworkingOperationFailingURLRequestErrorKey=, NSErrorFailingURLKey=http://..., NSLocalizedDescription=Expected content type {(
"application/x-www-form-urlencoded",
"application/json"
)}, got text/html, AFNetworkingOperationFailingURLResponseErrorKey=}
2014-02-17 14:54:20.808 AppName[5600:6403] E restkit.network:RKObjectRequestOperation.m:213 POST 'http://...' (200 OK / 0 objects) [request=0.0000s mapping=0.0000s total=0.1925s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1016 "Expected content type {(
"application/x-www-form-urlencoded",
"application/json"
)}, got text/html" UserInfo=0x1c52aed0 {NSLocalizedRecoverySuggestion=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJCbG8uUmVnQWxlcnQuQnJva2VyIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdC9CbG8uUmVnQWxlcnQuQVBJL2FwaSIsIm5iZiI6MTM5MjY0MTY2MSwiZXhwIjoxMzkyNjQ1MjYxLCJ1bmlxdWVfbmFtZSI6IkJ1dHRvbnMiLCJyb2xlIjoiUmVnQWxlcnRDb25zdW1lciJ9.JCTMGJRKlOxEtNrcGodpce-tqsRS4zlApNisKQW6iSw, AFNetworkingOperationFailingURLRequestErrorKey=, NSErrorFailingURLKey=http://..., NSLocalizedDescription=Expected content type {(
"application/x-www-form-urlencoded",
"application/json"
)}, got text/html, AFNetworkingOperationFailingURLResponseErrorKey=}
CODE THAT WORKED
Thanks to Wain for pointing me on the correct path there. I am a little disappointed that RestKit cannot handle such a simple request, and I need RestKit because this is just a session token to calling the other methods, but whatever works I guess:
NSDictionary* session_params = #{SESSION_USERNAME_KEY:SESSION_USERNAME_VALUE, SESSION_PASSWORD_KEY:SESSION_PASSWORD_VALUE};
NSURL* url = [NSURL URLWithString:SESSION_URL];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:SESSION_URL parameters:session_params];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString* response = [operation responseString];
NSLog(#"response: %#",response);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", [operation error]);
}];
[operation start];
This bit:
"Expected content type {( "application/x-www-form-urlencoded", "application/json" )}, got text/html"
tells you that you have told RestKit to expect form-urlencoded or json, but that the server is returning html.
You would probably want to use setAcceptHeaderWithMIMEType with JSON mime type to tell the server what you want back. But, in this case you probably just shouldn't be using RestKit.
RestKit is for mapping arbitrary JSON / XML data into your data model. You just have a key coming back. No mapping is required. So, don't use RestKit, use AFNetworking instead (which you have full access to because RestKit uses it internally.
Thanks to Wain and Quintin, this was quite useful to me :)
I think some names changed in more recent versions of Restkit or AFNetworking. I used AFNetworking as explained in other answers since the server did not return json but empty plain/text instead. This was only on a particular endpoint where I was looking for a token in the headers of the response.
Sharing my piece of code here too:
-(void) find_some_token_with_success:(void (^)(AFRKHTTPRequestOperation *operation, id responseObject))success failure:(void (^)(AFRKHTTPRequestOperation *operation, NSError *error))failure {
NSURL *baseURL = [NSURL URLWithString:#"https://example.com"];
AFRKHTTPClient *client = [AFRKHTTPClient clientWithBaseURL:baseURL];
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
[client setDefaultHeader:#"some_custom_header" value:#"some_custom_value"];
NSMutableURLRequest *request = [client requestWithMethod:#"GET" path:#"/api/v1/some_non_json_endpoint" parameters:nil];
AFRKHTTPRequestOperation *operation = [[AFRKHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:success failure:failure];
[operation start];
}
Then I used something like this to get the header I was looking for:
-(void) get_the_token:(void (^)(NSString *token))withTokenCallback failure:(void (^)(AFRKHTTPRequestOperation *operation, NSError *error))failure {
[self xsrftoken_with_success:^(AFRKHTTPRequestOperation *operation, id responseObject) {
NSString *token = [self get_the_token_from_response:[operation response]];
withTokenCallback(token);
} failure:failure];
}
-(NSString *) get_the_token_from_response: (NSHTTPURLResponse *) response;
{
NSDictionary *headerDictionary = response.allHeaderFields;
NSString *token = [headerDictionary objectForKey:#"SOME-TOKEN-KEY"];
return token;
}
So all of this can simply be used like this:
- (void)testGetSometokenInARequest
{
XCTestExpectation *expectation = [self expectationWithDescription:#"Query timed out."];
[[SomeRequestWithoutJsonResponse alloc]
get_the_token:^(NSString *token) {
[expectation fulfill];
NSLog(#"token: %#", token);
// this token should be 100 characters long
XCTAssertTrue([token length] == 100);
}
failure:^(AFRKHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", [operation error]);
}];
[self waitForExpectationsWithTimeout:10.0 handler:nil];
}
In other words, get_the_token takes a callback with the desired token and a failure callback.
Make sure you still include <RestKit/RestKit> so you have access to Restkit's AFNetowkring :)
Alternative working solution using restkit:
RestKit: How to handle empty response.body?
And you register a serializer for that kind of Mimetype like this:
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"text/plain"];
This is my code:
NSURL *url = [NSURL URLWithString:#"http://wspublisherv2.skygiraffe.com/WSpublisherV2.svc/Authenticate"];
AFHTTPClient *client = [[AFHTTPClient alloc]initWithBaseURL:url];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:#"john#sgdemo.com", #"UserName", #"123", #"Password", nil];
NSLog(#"%#", parameters);
[client postPath:nil parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"success: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure: %#", error);
}];
It always triggers the failure block and I get the "Expected status code in (200-299), got 404" message.
When I try it through Fiddler it works.
You need more information. Use a proxy like Charles Proxy to watch the traffic between your device and the server. That'll let you see the actual request. You can compare that to a request that works, and the difference should give a pretty good idea of what's wrong. At the very least, it'll make your question much more specific.
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.
Problem
My app lets users upload photos. This works great.
Now, I am trying to implement a "retry" function if the photo upload fails, for example due to a slow connection.
Here's my retry code:
self.operation = [self.operation copy]; // Creates a new operation with the same NSURLRequest
[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// do success stuff
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog("%#", error);
}];
[[MyAFHTTPClient sharedClient] enqueueHTTPRequestOperation:self.operation];
Upon starting, the failure block is called, outputting:
$0 = 0x12636b50 Error Domain=NSURLErrorDomain Code=-1021 "request body stream exhausted" UserInfo=0x12637810 {NSErrorFailingURLStringKey=https://my/long/url/, NSErrorFailingURLKey=https://my/long/url/, NSLocalizedDescription=request body stream exhausted, NSUnderlyingError=0x13046bb0 "request body stream exhausted"}
Question
How do I change my code to restart the image upload correctly?
What I've tried
I think the issue is that operation.request.HTTPBodyStream is an NSInputStream, which cannot be restarted.
The method -[AFURLConnectionOperation connection:needNewBodyStream:] appears to provide a copy of the input stream. I set a breakpoint in there; it's not called when copying or starting the operation, and I'm not sure how to trigger it.
There's some discussion on a similar issue on the AFNetworking GitHub page, but that relates to retrying after authentication failure.
Other info
My URL Request object is created using -[AFHTTPClient multipartFormRequestWithMethod:
path:
parameters:
constructingBodyWithBlock:]
I would try something like this :
-(void)uploadImage:(NSData *)imageData retry:(BOOL)retry
{
AFHTTPClient *myClient = [[AFHTTPClient alloc] initWithBaseUrl:myBaseURL];
NSURLRequest *request = [myClient multipartFormRequestWithMethod:#"POST"
path:myPath
parameters:myParametersDictionary
constructingBodyWithBlock:^(id <AFMultipartFormData> formData){
[formData appendPartWithFileData:imageData
name:myImageName
fileName:myFileName
mimeType:#"image/jpg"];
}];
AFHTTPRequestOperation *operation = [myClient HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// do success stuff
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog("%#", error);
if (retry) {
[self uploadImage:imageData
retry:NO];
}
}];
[myClient enqueueHTTPRequestOperation:operation];
}
Of course the first time you would call it with retry:YES
I am trying to post to a URL using AFNetworking and no matter what I do I keep getting the error:
Error Code: -1011 - Expected status code in (200-299), got 404
My code is as follows:
NSString *baseurl = #"http://mysiteurl";
NSString *path = #"/user/register/";
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseurl]];
[client registerHTTPOperationClass:[AFJSONRequestOperation class]];
//[client setAuthorizationHeaderWithUsername:#"myusername" password:#"mypassword"];
[client postPath:path parameters:[NSDictionary dictionaryWithObjectsAndKeys:_userName,#"user", _email, #"email",_password,#"password", nil] success:^(AFHTTPRequestOperation *operation, id JSON) {
//NSLog(#"sjson: %#", [JSON valueForKeyPath:#"entries"]);
NSLog(#"sjson: %#", JSON);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error Code: %i - %#",[error code], [error localizedDescription]);
}];
When I go to http://mysiteurl/user/register/ directly I am able to see JSON.
What am I doing wrong?
When I go to http://mysiteurl/user/register/ directly I am able to see JSON.
If you're doing this through a browser, you are making a GET request, whereas in your code, you are making a POST request.
A 404 is not just the visible address, it includes the HTTP method as well. You need to make sure that your server responds to a POST at http://mysiteurl/user/register/. Depending on your framework (e.g. Rails), you may have to add [client setDefaultHeader:#"Accept" value:#"text/json"] to get the correct route.
i usually use [client getPath: parameters:params success:] not Post but i guess this will work for both cases since your response is in a JSON format too
add these two lines
[client.parameterEncoding = AFJSONParameterEncoding;
[client setDefaultHeader:#"Accept" value:#"text/json"];
before
[client registerHTTPOperationClass:[AFJSONRequestOperation class]];