Neru-J
Neru-J

Reputation: 1623

Restkit JSON error response message from server

I have used many hours on how to solve this issue. Im using Restkit 0.9.3 with Object Mapping 2.0. All data is in JSON. I can make GET, POST, PUT and DELETE operations correctly, it's the response body I dont catch and map corretly..

So my problem is that my restful api is returning errors when something goes wrong. I want to map those errors with restkit, fx this error is returned:

{
  "code": "401",
  "message": "Unauthorized"
}

How do I map this JSON correctly? I have tried lots of things and could use some guidance - or please give an example of this.

Upvotes: 14

Views: 7339

Answers (4)

Francesco Vadicamo
Francesco Vadicamo

Reputation: 5542

Using Restkit v0.2x you can map all JSON attributes you want to the already existing RKErrorMessage.userInfo Dictionary property in this [Swift] way:

let errorMapping = RKObjectMapping(forClass: RKErrorMessage.self);
errorMapping.addPropertyMapping(RKAttributeMapping(fromKeyPath: nil, toKeyPath: "userInfo"));

let errorResponseDescriptor = RKResponseDescriptor(
    mapping: errorMapping,
    method: RKRequestMethod.Any,
    pathPattern: nil,
    keyPath: "error", //or nil, according to your json response
    statusCodes: RKStatusCodeIndexSetForClass(UInt(RKStatusCodeClassClientError)))
);

So, you can map an error JSON response like this one:

{
    "error": {
        "message": "Error message",
        "cause": "...",
        "code": "my_error_code",
        "url": "..."
        ...
    }
}

And retrieve the RKErrorMessage with all attributes, in a failure closure, as follows:

failure: { (operation, error) -> Void in
    if let errorMessage = error.userInfo?[RKObjectMapperErrorObjectsKey]?.firstObject as? RKErrorMessage{
        let message = errorMessage.userInfo["message"] as! String;
        let code = errorMessage.userInfo["code"] as! String;
        ...
    }
}

I hope this can be helpful to someone!

Upvotes: 5

Brenden
Brenden

Reputation: 7908

For RestKit v0.20

assuming your HTTP body is:

{"error": "..."}

you create an the mapping and descriptor:

RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];

[errorMapping addPropertyMapping: [RKAttributeMapping attributeMappingFromKeyPath:@"error" toKeyPath:@"errorMessage"]];
RKResponseDescriptor *errorResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:errorMapping pathPattern:nil keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)];
[objectManager addResponseDescriptor:errorResponseDescriptor];

and then you can access it from your failure block:

failure:^(RKObjectRequestOperation *operation, NSError *error) {
                NSLog(@"errorMessage: %@", [[error userInfo] objectForKey:RKObjectMapperErrorObjectsKey]);
            }

This uses the built-in RKErrorMessage class, though you can create your own custom class with additional fields.

Upvotes: 29

fphilipe
fphilipe

Reputation: 10054

For version 0.10.0 this response error can be mapped as follows:

RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping mapKeyPath:@"message" toAttribute:@"errorMessage"];
[[[RKObjectManager sharedManager] mappingProvider] setErrorMapping:errorMapping];

Requests that return an error will call the following delegate method:

- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error {
    NSArray *errorMessages = [[error userInfo] objectForKey:RKObjectMapperErrorObjectsKey];
    RKErrorMessage *errorMessage = [errorMessages objectAtIndex:0]; // First and only object in your case.
    NSString *message = [errorMessage errorMessage];
    NSInteger code = [[objectLoader response] statusCode];
    NSLog(@"ERROR: [%d] %@", code, message); // => ERROR: [401] Unauthorized
}

Upvotes: 10

kj13ennett
kj13ennett

Reputation: 319

if an object is returned your delegate should call this method:

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {}

in this method you should call an instance of your "error" class and map 'code' and 'message' as necessary.

An easier way to handle errors though would be to use:

- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {

if(objectLoader.response.statusCode == 401)
{ ...

And show the necessary error message in that method.

Upvotes: 0

Related Questions