Ben Copsey

Added support for application/x-www-form-urlencoded form posts in ASIFormDataRequest

@@ -9,6 +9,12 @@ @@ -9,6 +9,12 @@
9 #import <Foundation/Foundation.h> 9 #import <Foundation/Foundation.h>
10 #import "ASIHTTPRequest.h" 10 #import "ASIHTTPRequest.h"
11 11
  12 +typedef enum _ASIPostFormat {
  13 + ASIMultipartFormDataPostFormat = 0,
  14 + ASIURLEncodedPostFormat = 1
  15 +
  16 +} ASIPostFormat;
  17 +
12 @interface ASIFormDataRequest : ASIHTTPRequest { 18 @interface ASIFormDataRequest : ASIHTTPRequest {
13 19
14 // Parameters that will be POSTed to the url 20 // Parameters that will be POSTed to the url
@@ -17,6 +23,7 @@ @@ -17,6 +23,7 @@
17 // Files that will be POSTed to the url 23 // Files that will be POSTed to the url
18 NSMutableDictionary *fileData; 24 NSMutableDictionary *fileData;
19 25
  26 + ASIPostFormat postFormat;
20 } 27 }
21 28
22 #pragma mark setup request 29 #pragma mark setup request
@@ -36,4 +43,6 @@ @@ -36,4 +43,6 @@
36 // Same as above, but you can specify the content-type and file name 43 // Same as above, but you can specify the content-type and file name
37 - (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; 44 - (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key;
38 45
  46 +
  47 +@property (assign) ASIPostFormat postFormat;
39 @end 48 @end
@@ -11,6 +11,8 @@ @@ -11,6 +11,8 @@
11 11
12 // Private stuff 12 // Private stuff
13 @interface ASIFormDataRequest () 13 @interface ASIFormDataRequest ()
  14 +- (void)buildMultipartFormDataPostBody;
  15 +- (void)buildURLEncodedPostBody;
14 @property (retain) NSMutableDictionary *postData; 16 @property (retain) NSMutableDictionary *postData;
15 @property (retain) NSMutableDictionary *fileData; 17 @property (retain) NSMutableDictionary *fileData;
16 @end 18 @end
@@ -21,7 +23,9 @@ @@ -21,7 +23,9 @@
21 23
22 + (id)requestWithURL:(NSURL *)newURL 24 + (id)requestWithURL:(NSURL *)newURL
23 { 25 {
24 - return [[[self alloc] initWithURL:newURL] autorelease]; 26 + ASIFormDataRequest *request = [[[self alloc] initWithURL:newURL] autorelease];
  27 + [request setPostFormat:ASIMultipartFormDataPostFormat];
  28 + return request;
25 } 29 }
26 30
27 - (void)dealloc 31 - (void)dealloc
@@ -106,8 +110,19 @@ @@ -106,8 +110,19 @@
106 if ([[self fileData] count] > 0) { 110 if ([[self fileData] count] > 0) {
107 [self setShouldStreamPostDataFromDisk:YES]; 111 [self setShouldStreamPostDataFromDisk:YES];
108 } 112 }
109 -  
110 113
  114 + if ([self postFormat] == ASIURLEncodedPostFormat) {
  115 + [self buildURLEncodedPostBody];
  116 + } else {
  117 + [self buildMultipartFormDataPostBody];
  118 + }
  119 +
  120 + [super buildPostBody];
  121 +}
  122 +
  123 +
  124 +- (void)buildMultipartFormDataPostBody
  125 +{
111 // Set your own boundary string only if really obsessive. We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does. 126 // Set your own boundary string only if really obsessive. We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does.
112 NSString *stringBoundary = @"0xKhTmLbOuNdArY"; 127 NSString *stringBoundary = @"0xKhTmLbOuNdArY";
113 128
@@ -137,10 +152,10 @@ @@ -137,10 +152,10 @@
137 id file = [fileInfo objectForKey:@"data"]; 152 id file = [fileInfo objectForKey:@"data"];
138 NSString *contentType = [fileInfo objectForKey:@"contentType"]; 153 NSString *contentType = [fileInfo objectForKey:@"contentType"];
139 NSString *fileName = [fileInfo objectForKey:@"fileName"]; 154 NSString *fileName = [fileInfo objectForKey:@"fileName"];
140 - 155 +
141 [self appendPostData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, fileName] dataUsingEncoding:NSUTF8StringEncoding]]; 156 [self appendPostData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
142 [self appendPostData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]]; 157 [self appendPostData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType] dataUsingEncoding:NSUTF8StringEncoding]];
143 - 158 +
144 if ([file isKindOfClass:[NSString class]]) { 159 if ([file isKindOfClass:[NSString class]]) {
145 [self appendPostDataFromFile:file]; 160 [self appendPostDataFromFile:file];
146 } else { 161 } else {
@@ -155,11 +170,33 @@ @@ -155,11 +170,33 @@
155 170
156 [self appendPostData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; 171 [self appendPostData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
157 172
158 - [super buildPostBody];  
159 } 173 }
160 174
  175 +- (void)buildURLEncodedPostBody
  176 +{
  177 + // We can't post binary data using application/x-www-form-urlencoded
  178 + if ([[self fileData] count] > 0) {
  179 + [self setPostFormat:ASIMultipartFormDataPostFormat];
  180 + [self buildMultipartFormDataPostBody];
  181 + return;
  182 + }
  183 +
  184 + [self addRequestHeader:@"Content-Type" value:@"application/x-www-form-urlencoded"];
  185 +
  186 +
  187 + NSEnumerator *e = [[self postData] keyEnumerator];
  188 + NSString *key;
  189 + int i=0;
  190 + int count = [[self postData] count]-1;
  191 + while (key = [e nextObject]) {
  192 + NSString *data = [NSString stringWithFormat:@"%@=%@%@",[key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],[[[self postData] objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],(i<count ? @"&" : @"")];
  193 + [self appendPostData:[data dataUsingEncoding:NSUTF8StringEncoding]];
  194 + i++;
  195 + }
  196 +}
161 197
162 -@synthesize fileData;  
163 @synthesize postData; 198 @synthesize postData;
  199 +@synthesize fileData;
  200 +@synthesize postFormat;
164 201
165 @end 202 @end
@@ -15,4 +15,5 @@ @@ -15,4 +15,5 @@
15 - (void)testPostWithFileUpload; 15 - (void)testPostWithFileUpload;
16 - (void)testEmptyData; 16 - (void)testEmptyData;
17 - (void)testSubclass; 17 - (void)testSubclass;
  18 +- (void)testURLEncodedPost;
18 @end 19 @end
@@ -103,6 +103,22 @@ @@ -103,6 +103,22 @@
103 GHAssertTrue(success,@"Convenience constructor failed to return an instance of the correct class"); 103 GHAssertTrue(success,@"Convenience constructor failed to return an instance of the correct class");
104 } 104 }
105 105
  106 +- (void)testURLEncodedPost
  107 +{
  108 + ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://asi/ASIHTTPRequest/tests/url-encoded-post"]];
  109 + [request setPostValue:@"value1" forKey:@"value1"];
  110 + [request setPostValue:@"(%20 ? =)" forKey:@"value2"];
  111 + [request setPostValue:@"£100.00" forKey:@"value3"];
  112 + [request setPostValue:@"" forKey:@"value4"];
  113 + [request setShouldStreamPostDataFromDisk:YES];
  114 + [request setPostFormat:ASIURLEncodedPostFormat];
  115 + [request start];
  116 +
  117 +
  118 + BOOL success = ([[request responseString] isEqualToString:@"value1: value1\r\nvalue2: (%20 ? =)\r\nvalue3: £100.00\r\nvalue4: "]);
  119 + GHAssertTrue(success,@"Failed to send the correct post data");
  120 +}
  121 +
106 122
107 123
108 @end 124 @end