Override error implementation to correct the NSLocalizedDescription key for RKHTTPRequestOperation objects. fixes #1070

This commit is contained in:
Blake Watters
2012-12-11 23:12:20 -05:00
parent 138bbddfcd
commit 69c65ef8ab
6 changed files with 114 additions and 4 deletions

View File

@@ -28,6 +28,8 @@
#undef RKLogComponent
#define RKLogComponent RKlcl_cRestKitNetwork
NSString *RKStringFromIndexSet(NSIndexSet *indexSet); // Defined in RKResponseDescriptor.m
static BOOL RKLogIsStringBlank(NSString *string)
{
return ([[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0);
@@ -163,10 +165,13 @@ static NSString *RKStringDescribingStream(NSStream *stream)
@end
@interface AFURLConnectionOperation () <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
@property (readwrite, nonatomic, strong) NSError *HTTPError;
@end
@implementation RKHTTPRequestOperation
@dynamic HTTPError;
- (BOOL)hasAcceptableStatusCode
{
return self.acceptableStatusCodes ? [self.acceptableStatusCodes containsIndex:[self.response statusCode]] : [super hasAcceptableStatusCode];
@@ -177,6 +182,32 @@ static NSString *RKStringDescribingStream(NSStream *stream)
return self.acceptableContentTypes ? RKMIMETypeInSet([self.response MIMEType], self.acceptableContentTypes) : [super hasAcceptableContentType];
}
- (NSError *)error
{
// The first we are invoked, we need to mutate the HTTP error to correct the Content Types and Status Codes returned
if (self.response && !self.HTTPError) {
NSError *error = [super error];
if ([error.domain isEqualToString:AFNetworkingErrorDomain]) {
if (![self hasAcceptableStatusCode] || ![self hasAcceptableContentType]) {
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
if (error.code == NSURLErrorBadServerResponse) {
// Replace the NSLocalizedDescriptionKey
NSUInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? (NSUInteger)[self.response statusCode] : 200;
[userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected status code in (%@), got %d", nil), RKStringFromIndexSet([self acceptableStatusCodes]), statusCode] forKey:NSLocalizedDescriptionKey];
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo];
} else if (error.code == NSURLErrorCannotDecodeContentData) {
// Because we have shifted the Acceptable Content Types and Status Codes
[userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected content type %@, got %@", nil), [self acceptableContentTypes], [self.response MIMEType]] forKey:NSLocalizedDescriptionKey];
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
}
}
}
}
return [super error];
}
- (BOOL)wasNotModified
{
return [(NSString *)[[self.response allHeaderFields] objectForKey:@"Status"] isEqualToString:@"304 Not Modified"];

View File

@@ -312,7 +312,7 @@ RKMappingResult, RKRequestDescriptor, RKResponseDescriptor;
@property (nonatomic, strong) NSString *requestSerializationMIMEType;
/**
Sets a default header on the HTTP cleitn for the HTTP "Accept" header to specify the preferred serialization format for retrieved data.
Sets a default header on the HTTP client for the HTTP "Accept" header to specify the preferred serialization format for retrieved data.
This method is a convenience method whose implementation is equivalent to the following example code:

View File

@@ -57,7 +57,7 @@ static inline NSString *RKDescriptionForRequest(NSURLRequest *request)
return [NSString stringWithFormat:@"%@ '%@'", request.HTTPMethod, [request.URL absoluteString]];
}
static NSIndexSet *RKObjectRequestOperationAcceptableMIMETypes()
static NSIndexSet *RKObjectRequestOperationAcceptableStatusCodes()
{
static NSMutableIndexSet *statusCodes = nil;
if (! statusCodes) {
@@ -136,7 +136,7 @@ static NSString *RKStringDescribingURLResponseWithData(NSURLResponse *response,
self.responseDescriptors = responseDescriptors;
self.HTTPRequestOperation = requestOperation;
self.HTTPRequestOperation.acceptableContentTypes = [RKMIMETypeSerialization registeredMIMETypes];
self.HTTPRequestOperation.acceptableStatusCodes = RKObjectRequestOperationAcceptableMIMETypes();
self.HTTPRequestOperation.acceptableStatusCodes = RKObjectRequestOperationAcceptableStatusCodes();
}
return self;

View File

@@ -367,6 +367,8 @@
2548AC6E162F5E00009E79BF /* RKManagedObjectRequestOperationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2548AC6C162F5E00009E79BF /* RKManagedObjectRequestOperationTest.m */; };
2549D646162B376F003DD135 /* RKRequestDescriptorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2549D645162B376F003DD135 /* RKRequestDescriptorTest.m */; };
2549D647162B376F003DD135 /* RKRequestDescriptorTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2549D645162B376F003DD135 /* RKRequestDescriptorTest.m */; };
2551338F167838590017E4B6 /* RKHTTPRequestOperationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2551338E167838590017E4B6 /* RKHTTPRequestOperationTest.m */; };
25513390167838590017E4B6 /* RKHTTPRequestOperationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2551338E167838590017E4B6 /* RKHTTPRequestOperationTest.m */; };
25565956161FC3C300F5BB20 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25565955161FC3C300F5BB20 /* CoreServices.framework */; };
25565959161FC3CD00F5BB20 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25565958161FC3CD00F5BB20 /* SystemConfiguration.framework */; };
25565965161FDD8800F5BB20 /* RKResponseMapperOperationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25565964161FDD8800F5BB20 /* RKResponseMapperOperationTest.m */; };
@@ -827,6 +829,7 @@
2548AC6C162F5E00009E79BF /* RKManagedObjectRequestOperationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKManagedObjectRequestOperationTest.m; sourceTree = "<group>"; };
2549D645162B376F003DD135 /* RKRequestDescriptorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKRequestDescriptorTest.m; sourceTree = "<group>"; };
254A62BF14AD591C00939BEE /* RKPaginatorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKPaginatorTest.m; sourceTree = "<group>"; };
2551338E167838590017E4B6 /* RKHTTPRequestOperationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKHTTPRequestOperationTest.m; sourceTree = "<group>"; };
25565955161FC3C300F5BB20 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/CoreServices.framework; sourceTree = DEVELOPER_DIR; };
25565958161FC3CD00F5BB20 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; };
25565964161FDD8800F5BB20 /* RKResponseMapperOperationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKResponseMapperOperationTest.m; sourceTree = "<group>"; };
@@ -1454,6 +1457,7 @@
2549D645162B376F003DD135 /* RKRequestDescriptorTest.m */,
2548AC6C162F5E00009E79BF /* RKManagedObjectRequestOperationTest.m */,
2536D1FC167270F100DF9BB0 /* RKRouterTest.m */,
2551338E167838590017E4B6 /* RKHTTPRequestOperationTest.m */,
);
name = Network;
path = Logic/Network;
@@ -2353,6 +2357,7 @@
2546A95816628EDD0078E044 /* RKConnectionDescriptionTest.m in Sources */,
2543A25D1664FD3100821D5B /* RKResponseDescriptorTest.m in Sources */,
2536D1FD167270F100DF9BB0 /* RKRouterTest.m in Sources */,
2551338F167838590017E4B6 /* RKHTTPRequestOperationTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2502,6 +2507,7 @@
2546A95916628EDD0078E044 /* RKConnectionDescriptionTest.m in Sources */,
2543A25E1664FD3200821D5B /* RKResponseDescriptorTest.m in Sources */,
2536D1FE167270F100DF9BB0 /* RKRouterTest.m in Sources */,
25513390167838590017E4B6 /* RKHTTPRequestOperationTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,44 @@
//
// RKHTTPRequestOperationTest.m
// RestKit
//
// Created by Blake Watters on 12/11/12.
// Copyright (c) 2012 RestKit. All rights reserved.
//
#import "RKTestEnvironment.h"
#import "RKHTTPRequestOperation.h"
@interface RKHTTPRequestOperationTest : SenTestCase
@end
@implementation RKHTTPRequestOperationTest
- (void)testThatLoadingAnUnexpectedContentTypeReturnsCorrectErrorMessage
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/XML/channels.xml" relativeToURL:[RKTestFactory baseURL]]];
RKHTTPRequestOperation *requestOperation = [[RKHTTPRequestOperation alloc] initWithRequest:request];
requestOperation.acceptableContentTypes = [NSSet setWithObject:@"application/json"];
requestOperation.acceptableStatusCodes = [NSIndexSet indexSetWithIndex:200];
[requestOperation start];
[requestOperation waitUntilFinished];
expect(requestOperation.error).notTo.beNil();
expect([requestOperation.error localizedDescription]).to.equal(@"Expected content type {(\n \"application/json\"\n)}, got application/xml");
}
- (void)testThatLoadingAnUnexpectedStatusCodeReturnsCorrectErrorMessage
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/503" relativeToURL:[RKTestFactory baseURL]]];
RKHTTPRequestOperation *requestOperation = [[RKHTTPRequestOperation alloc] initWithRequest:request];
requestOperation.acceptableContentTypes = [NSSet setWithObject:@"text/xml"];
requestOperation.acceptableStatusCodes = [NSIndexSet indexSetWithIndex:200];
[requestOperation start];
[requestOperation waitUntilFinished];
expect(requestOperation.error).notTo.beNil();
expect([requestOperation.error localizedDescription]).to.equal(@"Expected status code in (200), got 503");
}
@end

View File

@@ -334,7 +334,6 @@
- (void)testMappingResponseWithDynamicMatchForResponseDescriptorPathPattern
{
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestComplexUser class]];
[userMapping addAttributeMappingsFromArray:@[@"firstname"]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:@"/JSON/:name\\.json" keyPath:@"data.STUser" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
@@ -545,4 +544,34 @@
expect(user.email).to.equal(@"blake@restkit.org");
}
- (void)testThatLoadingAnUnexpectedContentTypeReturnsCorrectErrorMessage
{
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestComplexUser class]];
[userMapping addAttributeMappingsFromArray:@[@"firstname"]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:@"/JSON/ComplexNestedUser.json" keyPath:@"data.STUser" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/XML/channels.xml" relativeToURL:[RKTestFactory baseURL]]];
RKObjectRequestOperation *requestOperation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]];
[requestOperation start];
[requestOperation waitUntilFinished];
expect(requestOperation.error).notTo.beNil();
expect([requestOperation.error localizedDescription]).to.equal(@"Expected content type {(\n \"application/json\",\n \"application/x-www-form-urlencoded\"\n)}, got application/xml");
}
- (void)testThatLoadingAnUnexpectedStatusCodeReturnsCorrectErrorMessage
{
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestComplexUser class]];
[userMapping addAttributeMappingsFromArray:@[@"firstname"]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping pathPattern:@"/JSON/ComplexNestedUser.json" keyPath:@"data.STUser" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"/503" relativeToURL:[RKTestFactory baseURL]]];
RKObjectRequestOperation *requestOperation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]];
[requestOperation start];
[requestOperation waitUntilFinished];
expect(requestOperation.error).notTo.beNil();
expect([requestOperation.error localizedDescription]).to.equal(@"Expected status code in (200-299,400-499), got 503");
}
@end