The failure callback for AFHTTPSessionManager.GET has the following signature:
func failure(task: NSURLSessionDataTask!, error: NSError!) {
The NSURLSessionDataTask simply has no property for accessing the response data, only metadata.
Contrast this with AFHTTPRequestOperationManager.GET whose failure callback has the signature:
void (^)(AFHTTPRequestOperation *operation, NSError *error)
The AFHTTPRequestOperation class has all the responseObject property available which is also auto-deserialized using the responseSerializer which is super convenient.
There should be a way to obtain the deserialized response from the failure callbacks for AFHTTPSessionManager's methods as well. The alternative is to implement the various session data delegate ourselves which defeat the entire purpose of using AFNetworking in the first place.
I have wasted a few hours searching for a solution but there's a chance that I might be missing something, if so, please let me know.
After further digging, found this line https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFURLSessionManager.m#L178 which seems to be the place where response object are deserialized.
It does not seems too complicated to move that up and also supply responseObject to the failure block but we'll probably have to change the callback signature to allow passing it all the way to the lib's consumer's failure block.
Not sure about your policy on backward compat. Would you accept a PR for this?
As an aside, the raw response data is available in the error.
error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey]
Is that available in the failure block? From reading at the code it doesn't seem userInfo is even passed there.
From using the code, yes it is.
Ok I have tried enumerating the userInfo dictionary and this is what I get:
NSLocalizedDescription: Request failed: internal server error (500)
NSErrorFailingURLKey: http://example.com/_panic
com.alamofire.serialization.response.error.data: <7b226572 726f7222 3a225445 53542045 52524f52 2046524f 4d204445 42554720 434f4e54 524f4c4c 4552222c 22737461 636b223a 22227d>
com.alamofire.serialization.response.error.response: <NSHTTPURLResponse: 0x7fa313d524c0> { URL: http://example.com/_panic } { status code: 500, headers {
Connection = close;
"Content-Length" = 55;
"Content-Type" = "application/json; charset=UTF-8";
Date = "Mon, 17 Nov 2014 07:27:00 GMT";
Server = "nginx/1.4.6 (Ubuntu)";
} }
So yeah, you were right but that is still just raw bytes. I still believe the serialized object should be passed along as well. But passing it via userInfo is a better idea.
As discussed in previous related issues, the intended strategy is for API consumers to create their own response serializers to handle failures in such a way that populates the resulting NSError in the failure case with all necessary information to either communicate or recover from that particular error.
If, for example, an API were to send an error message as JSON, the response serializer might populate the localizedDescription and/or localizedFailureReason from fields in that document. This also allows for things like 500 errors, which are often rendered as raw HTML, to be handled in a parallel code path, without triggering a JSON decoding error.
Alternatively, as mentioned by @OrangeDog, the raw NSData is provided in the AFNetworkingOperationFailingURLResponseDataErrorKey key of the default NSError userInfo.
@matt Please could you elaborate on how a failure response serializer is set on an AFHTTPSession manager rather that using AFNetworkingOperationFailingURLResponseDataErrorKey?
Hello Guys I Have answer Please follow step :
NSString* ErrorResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
NSLog(@"%@",ErrorResponse);
You get 100% Response.
Thanking You,
Savankumar Vaishnav.
Facebook : Savankumar Vaishnav.
I am using the error's userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as well like Savankumar. Unclear if there's a cleaner way to do this in the manager as @samskiter asks, but in my case it's preferred to _not_ do it for every failing API, as I only need to parse the error's JSON in certain special cases.
@mattt how would this be done in general (for example, I know my API's AFNetworkingOperationFailingURLResponseDataErrorKey will always parse to JSON and I wish to always have it available in my failure block)
Here is my quick hack (keyword: hack) to add a new key with the NSData serialized without much mucking around. Just attach it to your manager and modify the key as you wish.
manager.responseSerializer = [CYCJSONResponseSerializer serializer];
@interface CYCJSONResponseSerializer : AFJSONResponseSerializer
@end
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = [super validateResponse:response data:data error:error];
if (!responseIsValid) {
NSError *errorPointer = *error;
NSData *responseErrorData = errorPointer.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
NSString *responseErrorString = nil;
if (responseErrorData) {
responseErrorString = [[NSJSONSerialization JSONObjectWithData:responseErrorData options:0 error:nil] description];
}
NSMutableDictionary *mutableUserInfo = [errorPointer.userInfo mutableCopy];
if (responseErrorString) {
mutableUserInfo[@"com.cyc.serialization.response.error"] = responseErrorString;
}
*error = [NSError errorWithDomain:errorPointer.domain code:errorPointer.code userInfo:[mutableUserInfo copy]];
}
return responseIsValid;
}
Note: Edited original answer.
@kgaidis I think you'll get an exception in that case if errorPointer.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] is nil, but otherwise looks good. Seems to be parsed at the appropriate layer.
For posterity: I just added a category to AFHTTPRequestOperation that allows me to get the error body as NSDictionary if needed (idempotently parses iff requested) which saves a few cycles for all the times I don't actually need it. Probably more of a hack than @kgaidis 's solution but it's working for my purposes.
@tsheaff
I modified it to be a little more safe and also more pretty.
Edit: just modified original answer.
I have found this solution and posted here http://stackoverflow.com/a/35723726/3463712
there is an answer: http://stackoverflow.com/a/35083647/1338479
It seems AFNetworkingOperationFailingURLResponseDataErrorKey no longer exists? How can I get the body of an error response now?
Building on top of @kgaidis solution I came up with:
@interface ResponseSerializer : AFJSONResponseSerializer
@end
@implementation ResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)errorPointer
{
id responseObject = [super responseObjectForResponse:response data:data error:errorPointer];
if (*errorPointer) {
NSError *error = *errorPointer;
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
userInfo[@"responseObject"] = responseObject;
*errorPointer = [NSError errorWithDomain:error.domain code:error.code userInfo:[userInfo copy]];
}
return responseObject;
}
@end
It is very inconvenient that we can't get the response from the fail callback. The response may contain many useful information. @sebastianludwig ,nice work. Thanks.
I got hit by this problem.
Any updates on this?
Most helpful comment
Hello Guys I Have answer Please follow step :
NSString* ErrorResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding]; NSLog(@"%@",ErrorResponse);You get 100% Response.
Thanking You,
Savankumar Vaishnav.
Facebook : Savankumar Vaishnav.