In the RestKit app I'm making the requests and responses are working happily, but now I'm trying to add error handling. I'm following the example in the RestKit readme and similar examples from the net, but run into some strange behaviour.
The error mapping is added with
// Error JSON looks like {"errors": "Some Error Has Occurred"}
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
// The entire value at the source key path containing the errors maps to the message
[errorMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:#"errorMessage"]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError);
// Any response in the 4xx status code range with an "errors" key path uses this mapping
RKResponseDescriptor *errorDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping method:RKRequestMethodAny pathPattern:nil keyPath:#"errors" statusCodes:statusCodes];
[objectManager addResponseDescriptor:errorDescriptor];
When I make my service simulate an error instead of returning data, the following happens. The response of my service is
HTTP/1.1 401 Unauthorized
Server: ASP.NET Development Server/10.0.0.0
Date: Tue, 26 May 2015 19:01:21 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: application/json; charset=utf-8
Connection: Close
{"errors":"This is a test exception"}
after which the connection is closed. On the client side, however, the failure block of getObjectsAtPath: isn't executed immediately. Instead the function waits for a timeout and doesn't return an error object. The error is [Updated. Replaced with log including trace logging]:
2015-05-27 14:08:25.968 Olive Oil[37455:37884261] I restkit:RKLog.m:49 RestKit logging initialized...
2015-05-27 14:08:26.275 Olive Oil[37455:37884261] 320.000000
2015-05-27 14:08:26.415 Olive Oil[37455:37884261] T restkit.network:RKObjectRequestOperation.m:148 GET 'http://192.168.69.88/OliveOil.svc/recipies':
request.headers={
"Accept-Language" = "en;q=1";
Authorization = "Basic XXXXXXXXXXXXX==";
"User-Agent" = "Olive Oil/1 (iPad Simulator; iOS 8.3; Scale/2.00)";
}
request.body=(null)
2015-05-27 14:09:33.559 Olive Oil[37455:37885022] E restkit.network:RKObjectRequestOperation.m:544 Object request failed: Underlying HTTP request operation failed with error: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x7f91f36ad440 {NSUnderlyingError=0x7f91f37271e0 "The request timed out.", NSErrorFailingURLStringKey=http://192.168.69.88/OliveOil.svc/recipies, NSErrorFailingURLKey=http://192.168.69.88/OliveOil.svc/recipies, NSLocalizedDescription=The request timed out.}
2015-05-27 14:09:33.559 Olive Oil[37455:37885022] E restkit.network:RKObjectRequestOperation.m:209 GET 'http://192.168.69.88/OliveOil.svc/recipies' (401 Unauthorized / 0 objects) [request=67.1418s mapping=0.0000s total=67.2429s]:error=Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x7f91f36ad440 {NSUnderlyingError=0x7f91f37271e0 "The request timed out.", NSErrorFailingURLStringKey=http://192.168.69.88/OliveOil.svc/recipies, NSErrorFailingURLKey=http://192.168.69.88/OliveOil.svc/recipies, NSLocalizedDescription=The request timed out.}
2015-05-27 14:09:33.560 Olive Oil[37455:37885022] D restkit.network:RKObjectRequestOperation.m:210 response.body=(null)
So apparently the status 401 gets communicated (2nd error), but something makes the RestKit wait for more data. What could be the cause of this behaviour? Some misconfiguration? Is the service response malformed? Is there something wrong with the error mapping?
On the service side, the connection wasn't closed after the data was sent.
After making the appropriate changes, the app processes the error information as expected. Thanks, Wain, for pointing me in the right direction.
Related
I am using the following code to get address from lat and long:
NSLog(#">>>>%f,%f",location.latitude,location.longitude);
[[GMSGeocoder geocoder] reverseGeocodeCoordinate:location completionHandler:^(GMSReverseGeocodeResponse *resp, NSError *error)
{
NSLog(#"response%#",resp);
}];
When I try to do geocode with a correct lat and long, for a particular location, I get null as response and error is this. This is not happening with my android device.
I can see response as nil and error is:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSErrorFailingURLStringKey=https://clients4.google.com/glm/mmap, _kCFStreamErrorCodeKey=-2102, NSErrorFailingURLKey=https://clients4.google.com/glm/mmap, NSLocalizedDescription=The request timed out., _kCFStreamErrorDomainKey=4, NSUnderlyingError=0x1659ae80 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "The request timed out." UserInfo={_kCFStreamErrorCodeKey=-2102, NSErrorFailingURLStringKey=https://clients4.google.com/glm/mmap, NSErrorFailingURLKey=https://clients4.google.com/glm/mmap, NSLocalizedDescription=The request timed out., _kCFStreamErrorDomainKey=4}}}
This issue arises because of your API key is expired, if you regenerate the API key in google console it will work and get your response
API key example: AIzaSyDleduRwellp3OwLMAOzLt4MZWsJMWqMvM
Also check, if you're using simulator, that your simulator location setting is set to none. You can check via selecting your simulator and go to:
Debug > Location > none
Hope it helps.
I am using AFNetworking 2 for an iOS project consuming a REST API. When requests fail, I am not able to get the body response.
I have seen this SO answer that says it can be retrieved from the userInfo dictionary. Unfortunately in my case I am not getting the NSLocalizedRecoverySuggestion key-value in my userInfo dictionary with the response body. Instead I see a AFNetworkingOperationFailingURLResponseErrorKey key.
My NSError log is
Error Domain=AFNetworkingErrorDomain Code=-1011 "Request failed: bad request (400)" UserInfo=0xbe471a0 {NSErrorFailingURLKey=http://***.com/api/users, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0xbc60e80> { URL: http://***.com/api/users } { status code: 400, headers {
Connection = "keep-alive";
"Content-Length" = 85;
"Content-Type" = "application/json; charset=utf-8";
Date = "Thu, 24 Apr 2014 21:36:57 GMT";
"X-Powered-By" = Express;
} }, NSLocalizedDescription=Request failed: bad request (400)}
As you can see from the headers, I am getting "Content-Type" = "application/json; charset=utf-8"; so this other answer doesn't apply either.
Actually the body response in Postman looks like this:
{"errorCode":100,"description":"There is already a registered user with that email."}
The backend is implemented by myself in Node + Express. I am not very experienced in backend development though, so maybe there is something I am missing or that I could change.
Does anyone know why I am not getting the response body in userInfo?
Quoting AFNetworking owner:
(...) the recommended approach is to create a custom response serializer that translates response data in failing cases into NSError objects as appropriate.
You can see this answer on another AFNetworking issue to see how to integrate it with your session manager, and this repository to see how to implement it.
I am using RestKit 0.2, and I am getting the following error.
E restkit.network:RKObjectRequestOperation.m:237 GET 'http://some.url.com/o/3134' (200 OK / 0 objects) [request=3.2563s mapping=0.0000s total=3.2955s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (400-499), got 200" UserInfo=0x87712e0 {NSLocalizedRecoverySuggestion={"d":"2013-07-15T02:30:00.000Z","t":16.1,"at":13.8}, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest http://some.url.com/o/3134>, NSErrorFailingURLKey=http://some.url.com/o/3134, NSLocalizedDescription=Expected status code in (400-499), got 200, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x9647260>}
I use nodejs and restify as the server end. And I can get the json data by using Google Chrome, and I can see the Content-Type of response is application/json in Chrome.
But when I used RestKit, I got this error about 'Expected status code in (400-499), got 200', I think 200 is fine, why the RestKit is expecting 400 - 499 rather than 200? And I can see the json object in the error message too. which is {"d":"2013-07-15T02:30:00.000Z","t":16.1,"at":13.8}.
Thanks.
When you define the response descriptor for a request, you need to set the status code to RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful) if you expect a response with http status code 2xx
Example:
[RKResponseDescriptor responseDescriptorWithMapping:yourMapping method:RKRequestMethodAny pathPattern:yourPattern keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
UPDATE:
Make sure that the pathPattern of the response descriptor matches the request path. Otherwise RestKit will use the error response descriptor (if you define it), and expect a response with a different status code.
I'm getting a weird behavior where using sendSynchronousRequest or sendAsynchronousRequest with invalid credential will make the nsurlresponse nil. But the old way with the [[NSURLConnection alloc] initWithRequest:request delegate:self]; I get the 401 response code.
The error value using the sendSynchronousRequest or sendAsynchronousRequest is
Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be
completed. (NSURLErrorDomain error -1012.)" UserInfo=0x756ecb0
{NSErrorFailingURLKey=myurl,
NSErrorFailingURLStringKey=myurl,
NSUnderlyingError=0x75704d0 "The operation couldn’t be completed.
(kCFErrorDomainCFNetwork error -1012.)"
Does someone have a clue as to why it is like that? Any information on this is appreciated. I was expecting to get a 401 response from sendSynchronousRequest or sendAsynchronousRequest
Thanks,
This error is caused when a WWW-Authenticate: HTTP header is returned with the 401 response, asking for user-interaction to enter valid credentials. The NSURLConnection processes the HTTP headers and the response body, returning the response data correctly as an NSData object, but leaving the returningResponse object as nil.
According to Apple's Foundation Constants Reference, the error -1012 is:
NSURLErrorUserCancelledAuthentication
Returned when an asynchronous request for authentication is cancelled by the user.
This is typically incurred by clicking a “Cancel” button in a username/password dialog, rather than the user making an attempt to
authenticate.
I am speculating that it automatically treats the WWW-Authenticate: header as a request for credentials that was cancelled by the user (since it is headless) and generates the error. Then some other part of the code-path that should set the response is not executed because there was an error. Personally I think this is either a bug or bad design. It should be ok to get a valid HTTP response and an error at the same time, either way, you get a valid HTTP response so the returningResponse should be set. Bad Apple!
I'm using tastypie for a REST interface in django, from Xcode I can call the server and get data successfully using RestKit however I cannot POST data. I know it's hitting the right area as I had a 401 then turned on authentication() in django and that problem was solved, now I'm receiving a 501 Not Implemented Error. I've also manually added the detail and list_allowed_methods to allow all forms POST/GET/ETC. I've Googled and overflowed to no avail! Help!
Here's the code I'm using for POSTING (I'm new to RestKit so be gentle)
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://127.0.0.1:8000/api/v1/"]];
RKObjectMapping *lotRequestMapping = [RKObjectMapping requestMapping];
[lotRequestMapping addAttributeMappingsFromDictionary:#{#"status" : #"parking_availability"}];
RKResponseDescriptor *lotResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:lotRequestMapping
pathPattern:#"parkinglot/:primaryKey/"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
Screenshot -
Here's the Error
See Screenshot
Text from Screenshot
2012-12-19 13:23:26.145 Parkable[36586:4b07] I
restkit.network:RKHTTPRequestOperation.m:143 POST
'http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json' 2012-12-19
13:23:26.158 Parkable[36586:3807] E
restkit.network:RKHTTPRequestOperation.m:156 POST
'http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json' (501 Not
Implemented): Error Domain=AFNetworkingErrorDomain Code=-1011
"Expected status code in (200-299,400-499), got 501"
UserInfo=0x9170260
{AFNetworkingOperationFailingURLRequestErrorKey=http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json>,
NSErrorFailingURLKey=http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json,
NSLocalizedDescription=Expected status code in (200-299,400-499), got
501,
AFNetworkingOperationFailingURLResponseErrorKey=} 2012-12-19 13:23:26.158 Parkable[36586:4b07] E
restkit.network:RKObjectRequestOperation.m:271 Object request failed:
Underlying HTTP request operation failed with error: Error
Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in
(200-299,400-499), got 501" UserInfo=0x9470f10
{AFNetworkingOperationFailingURLRequestErrorKey=http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json>,
NSErrorFailingURLKey=http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json,
NSLocalizedDescription=Expected status code in (200-299,400-499), got
501,
AFNetworkingOperationFailingURLResponseErrorKey=} 2012-12-19 13:23:26.159 Parkable[36586:c07] failure -
Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code
in (200-299,400-499), got 501" UserInfo=0x9470f10
{AFNetworkingOperationFailingURLRequestErrorKey=http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json>,
NSErrorFailingURLKey=http://127.0.0.1:8000/api/v1/parkinglot/22/?format=json,
NSLocalizedDescription=Expected status code in (200-299,400-499), got
501,
AFNetworkingOperationFailingURLResponseErrorKey=}
To answer my own question the 501 error went away after adding in the
class UserResource(ModelResource):
to the resources.py file and registering it with urls.py
v1_api.register(UserResource())