Browse Source

Replacing NSMutableURLRequest category method with more flexible constructor in AFRestClient

Adding AFRestClient -multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:

Defining AFMultipartFormDataProxy protocol

Replacing constant value for string encoding with @property with default of NSUTF8Encoding
Mattt Thompson 14 years ago
parent
commit
8b8605185b

+ 20 - 4
AFNetworking/AFRestClient.h

@@ -23,7 +23,7 @@
 #import <Foundation/Foundation.h>
 #import "AFHTTPRequestOperation.h"
 
-#import "NSMutableURLRequest+AFNetworking.h"
+@protocol AFMultipartFormDataProxy;
 
 @interface AFRestClient : NSObject {
 @private
@@ -112,6 +112,12 @@
 - (NSMutableURLRequest *)requestWithMethod:(NSString *)method 
                                       path:(NSString *)path parameters:(NSDictionary *)parameters;
 
+- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
+                                                   path:(NSString *)path
+                                             parameters:(NSDictionary *)parameters
+                              constructingBodyWithBlock:(void (^)(id <AFMultipartFormDataProxy>formData))block;
+
+
 ///--------------------------------
 /// @name Enqueuing HTTP Operations
 ///--------------------------------
@@ -132,9 +138,9 @@
 ///---------------------------
 /// @name Making HTTP Requests
 ///---------------------------
-- (void)getPath:(NSString *)path 
-     parameters:(NSDictionary *)parameters 
-        success:(void (^)(id response))success 
+- (void)getPath:(NSString *)path
+     parameters:(NSDictionary *)parameters
+        success:(void (^)(id response))success
         failure:(void (^)(NSError *error))failure;
 
 - (void)postPath:(NSString *)path 
@@ -152,3 +158,13 @@
            success:(void (^)(id response))success 
            failure:(void (^)(NSError *error))failure;
 @end
+
+#pragma mark -
+
+@protocol AFMultipartFormDataProxy <NSObject>
+- (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body;
+- (void)appendPartWithFormData:(NSData *)data name:(NSString *)name;
+- (void)appendPartWithFile:(NSURL *)fileURL fileName:(NSString *)fileNameOrNil;
+- (void)appendData:(NSData *)data;
+- (void)appendString:(NSString *)string;
+@end

+ 138 - 0
AFNetworking/AFRestClient.m

@@ -23,6 +23,31 @@
 #import "AFRestClient.h"
 #import "AFJSONRequestOperation.h"
 
+static NSString * const kAFMultipartFormLineDelimiter = @"\r\n"; // CRLF
+static NSString * const kAFMultipartFormBoundary = @"Boundary+0xAbCdEfGbOuNdArY";
+
+static NSString * AFMultipartFormEncapsulationBoundary() {
+    return [NSString stringWithFormat:@"--%@", kAFMultipartFormBoundary];
+}
+
+static NSString * AFMultipartFormFinalBoundary() {
+    return [NSString stringWithFormat:@"--%@--", kAFMultipartFormBoundary];
+}
+
+@interface AFMutableMultipartFormData : NSObject <AFMultipartFormDataProxy> {
+@private
+    NSStringEncoding _stringEncoding;
+    NSMutableArray *_lines;
+}
+
+- (id)initWithStringEncoding:(NSStringEncoding)encoding;
+
+- (NSData *)data;
+
+@end
+
+#pragma mark -
+
 static NSString * AFBase64EncodedStringFromString(NSString *string) {
     NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string length]];
     NSUInteger length = [data length];
@@ -161,6 +186,42 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS
 	return request;
 }
 
+- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
+                                                   path:(NSString *)path
+                                             parameters:(NSDictionary *)parameters
+                              constructingBodyWithBlock:(void (^)(id <AFMultipartFormDataProxy>formData))block
+{
+    if (!([method isEqualToString:@"POST"] || [method isEqualToString:@"PUT"] || [method isEqualToString:@"DELETE"])) {
+        [NSException raise:@"Invalid HTTP Method" format:@"%@ is not supported for multipart form requests; must be either POST, PUT, or DELETE", method];
+        return nil;
+    }
+    
+    NSMutableURLRequest *request = [self requestWithMethod:method path:path parameters:nil];
+    __block AFMutableMultipartFormData *formData = [[AFMutableMultipartFormData alloc] init];
+    
+    id key = nil;
+	NSEnumerator *enumerator = [parameters keyEnumerator];
+	while ((key = [enumerator nextObject])) {
+        id value = [parameters valueForKey:key];
+        if (![value isKindOfClass:[NSData class]]) {
+            value = [value description];
+        }
+        
+        [formData appendPartWithFormData:[value dataUsingEncoding:self.stringEncoding] name:[key description]];
+	}
+    
+    if (block) {
+        block(formData);
+    }
+    
+    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", kAFMultipartFormBoundary] forHTTPHeaderField:@"Content-Type"];
+    [request setHTTPBody:[formData data]];
+    
+    [formData autorelease];
+    
+    return request;
+}
+
 - (void)enqueueHTTPOperation:(AFHTTPRequestOperation *)operation {
     [self.operationQueue addOperation:operation];
 }
@@ -209,3 +270,80 @@ static NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSS
 }
 
 @end
+
+#pragma mark -
+
+// multipart/form-data; see http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2
+@interface AFMutableMultipartFormData ()
+@property (readwrite, nonatomic, assign) NSStringEncoding stringEncoding;
+@property (readwrite, nonatomic, retain) NSMutableArray *lines;
+
+- (void)appendBlankLine;
+@end
+
+@implementation AFMutableMultipartFormData
+@synthesize stringEncoding = _stringEncoding;
+@synthesize lines = _lines;
+
+- (id)initWithStringEncoding:(NSStringEncoding)encoding {
+    self = [super init];
+    if (!self) {
+        return nil;
+    }
+    
+    self.stringEncoding = encoding;
+    self.lines = [NSMutableArray array];
+    
+    return self;
+}
+
+- (void)dealloc {
+    [_lines release];
+    [super dealloc];
+}
+
+- (NSData *)data {
+    NSLog(@"DATA: %@", [[self.lines componentsJoinedByString:kAFMultipartFormLineDelimiter] stringByAppendingString:AFMultipartFormFinalBoundary()]);
+    
+    return [[[[self.lines componentsJoinedByString:kAFMultipartFormLineDelimiter] stringByAppendingString:AFMultipartFormFinalBoundary()] stringByAppendingString:kAFMultipartFormLineDelimiter] dataUsingEncoding:self.stringEncoding];
+}
+
+#pragma mark - AFMultipartFormDataProxy
+
+- (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body {
+    for (NSString *field in [headers allKeys]) {
+        [self appendString:[NSString stringWithFormat:@"%@: %@", field, [headers valueForKey:field]]];
+    }
+    
+    [self appendBlankLine];
+    [self appendData:body];
+}
+
+- (void)appendPartWithFormData:(NSData *)data name:(NSString *)name {
+    [self appendPartWithHeaders:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"form-data; name=\"%@\"", name] forKey:@"Content-Disposition"] body:data];
+}
+
+- (void)appendPartWithFile:(NSURL *)fileURL fileName:(NSString *)fileNameOrNil {
+    if (![fileURL isFileURL]) {
+        [NSException raise:@"Invalid fileURL value" format:@"%@ must be a valid file URL", fileURL];
+        return;
+    }
+    
+    NSData *data = [NSData dataWithContentsOfFile:[fileURL absoluteString]];
+    NSString *fileName = fileNameOrNil ? fileNameOrNil : [[fileURL lastPathComponent] stringByAppendingPathExtension:[fileURL pathExtension]];
+    [self appendPartWithHeaders:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"file; filename=\"%@\"", fileName] forKey:@"Content-Disposition"] body:data];
+}
+
+- (void)appendData:(NSData *)data {
+    [self appendString:[[[NSString alloc] initWithData:data encoding:self.stringEncoding] autorelease]];
+}
+
+- (void)appendString:(NSString *)string {
+    [self.lines addObject:string];
+}
+
+- (void)appendBlankLine {
+    [self appendString:@""];
+}
+
+@end

+ 0 - 33
AFNetworking/NSMutableURLRequest+AFNetworking.h

@@ -1,33 +0,0 @@
-// NSMutableURLRequest+AFNetworking.h
-//
-// Copyright (c) 2011 Gowalla (http://gowalla.com/)
-// 
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-// 
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#import <Foundation/Foundation.h>
-
-@interface NSMutableURLRequest (AFNetworking)
-
-- (void)setHTTPBodyWithData:(NSData *)data 
-                   mimeType:(NSString *)mimeType 
-          forParameterNamed:(NSString *)parameterName 
-                 parameters:(NSDictionary *)parameters
-         useGzipCompression:(BOOL)useGzipCompression;
-
-@end

+ 0 - 76
AFNetworking/NSMutableURLRequest+AFNetworking.m

@@ -1,76 +0,0 @@
-// NSMutableURLRequest+AFNetworking.m
-//
-// Copyright (c) 2011 Gowalla (http://gowalla.com/)
-// 
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-// 
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#import "NSMutableURLRequest+AFNetworking.h"
-#import "NSData+AFNetworking.h"
-
-@implementation NSMutableURLRequest (AFNetworking)
-
-- (void)setHTTPBodyWithData:(NSData *)data 
-                   mimeType:(NSString *)mimeType 
-          forParameterNamed:(NSString *)parameterName 
-                 parameters:(NSDictionary *)parameters
-         useGzipCompression:(BOOL)useGzipCompression
-{
-	if ([[self HTTPMethod] isEqualToString:@"GET"]) {
-		[self setHTTPMethod:@"POST"];
-	}
-    
-    NSString *filename = [[NSString stringWithFormat:@"%d", [[NSDate date] hash]] stringByAppendingPathExtension:[mimeType lastPathComponent]];
-	
-	static NSString * const boundary = @"----Boundary+0xAbCdEfGbOuNdArY";
-	[self setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
-	
-	NSMutableData *mutableData = [NSMutableData data];
-	[mutableData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
-	
-	id key;
-	NSEnumerator *enumerator = [parameters keyEnumerator];
-	while ((key = [enumerator nextObject])) {
-		[mutableData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
-		[mutableData appendData:[[NSString stringWithFormat:@"%@", [parameters valueForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];
-		[mutableData appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
-	}
-	
-	[mutableData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"", parameterName, filename, nil] dataUsingEncoding:NSUTF8StringEncoding]];
-    [mutableData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
-	[mutableData appendData:[[NSString stringWithFormat:@"Content-Type: %@", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
-    [mutableData appendData:[@"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
-	[mutableData appendData:data];
-	[mutableData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
-	
-    if (useGzipCompression) {
-        NSError *error = nil;
-        NSData *compressedData = [mutableData dataByGZipCompressingWithError:&error];
-        
-        if (!error && compressedData) {
-            [self setHTTPBody:compressedData];
-            
-            // Content-Encoding HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
-            [self setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
-        }
-    } else {
-        [self setHTTPBody:mutableData];
-    }
-}
-
-@end

+ 0 - 6
Example/AFNetworking Example.xcodeproj/project.pbxproj

@@ -7,7 +7,6 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		F85CE2DC13EC4A4200BFAE01 /* NSMutableURLRequest+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = F85CE2DB13EC4A4200BFAE01 /* NSMutableURLRequest+AFNetworking.m */; };
 		F874B5D913E0AA6500B28E3E /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5C913E0AA6500B28E3E /* AFHTTPRequestOperation.m */; };
 		F874B5DA13E0AA6500B28E3E /* AFImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5CA13E0AA6500B28E3E /* AFImageCache.m */; };
 		F874B5DB13E0AA6500B28E3E /* AFImageRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F874B5CB13E0AA6500B28E3E /* AFImageRequestOperation.m */; };
@@ -34,8 +33,6 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
-		F85CE2DA13EC4A4200BFAE01 /* NSMutableURLRequest+AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableURLRequest+AFNetworking.h"; path = "../AFNetworking/NSMutableURLRequest+AFNetworking.h"; sourceTree = "<group>"; };
-		F85CE2DB13EC4A4200BFAE01 /* NSMutableURLRequest+AFNetworking.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableURLRequest+AFNetworking.m"; path = "../AFNetworking/NSMutableURLRequest+AFNetworking.m"; sourceTree = "<group>"; };
 		F874B5C913E0AA6500B28E3E /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFHTTPRequestOperation.m; path = ../AFNetworking/AFHTTPRequestOperation.m; sourceTree = "<group>"; };
 		F874B5CA13E0AA6500B28E3E /* AFImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFImageCache.m; path = ../AFNetworking/AFImageCache.m; sourceTree = "<group>"; };
 		F874B5CB13E0AA6500B28E3E /* AFImageRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AFImageRequestOperation.m; path = ../AFNetworking/AFImageRequestOperation.m; sourceTree = "<group>"; };
@@ -101,8 +98,6 @@
 		F85CE2D613EC47BC00BFAE01 /* Categories */ = {
 			isa = PBXGroup;
 			children = (
-				F85CE2DA13EC4A4200BFAE01 /* NSMutableURLRequest+AFNetworking.h */,
-				F85CE2DB13EC4A4200BFAE01 /* NSMutableURLRequest+AFNetworking.m */,
 				F874B5D713E0AA6500B28E3E /* UIImage+AFNetworking.h */,
 				F874B5CF13E0AA6500B28E3E /* UIImage+AFNetworking.m */,
 				F874B5D813E0AA6500B28E3E /* UIImageView+AFNetworking.h */,
@@ -348,7 +343,6 @@
 				F874B5DE13E0AA6500B28E3E /* AFRestClient.m in Sources */,
 				F874B5DF13E0AA6500B28E3E /* UIImage+AFNetworking.m in Sources */,
 				F874B5E013E0AA6500B28E3E /* UIImageView+AFNetworking.m in Sources */,
-				F85CE2DC13EC4A4200BFAE01 /* NSMutableURLRequest+AFNetworking.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};