Add new methods to allow ASIFormDataRequests to send multiple values for the same key
Showing
4 changed files
with
142 additions
and
42 deletions
@@ -19,10 +19,10 @@ typedef enum _ASIPostFormat { | @@ -19,10 +19,10 @@ typedef enum _ASIPostFormat { | ||
19 | @interface ASIFormDataRequest : ASIHTTPRequest <NSCopying> { | 19 | @interface ASIFormDataRequest : ASIHTTPRequest <NSCopying> { |
20 | 20 | ||
21 | // Parameters that will be POSTed to the url | 21 | // Parameters that will be POSTed to the url |
22 | - NSMutableDictionary *postData; | 22 | + NSMutableArray *postData; |
23 | 23 | ||
24 | // Files that will be POSTed to the url | 24 | // Files that will be POSTed to the url |
25 | - NSMutableDictionary *fileData; | 25 | + NSMutableArray *fileData; |
26 | 26 | ||
27 | ASIPostFormat postFormat; | 27 | ASIPostFormat postFormat; |
28 | 28 | ||
@@ -41,15 +41,30 @@ typedef enum _ASIPostFormat { | @@ -41,15 +41,30 @@ typedef enum _ASIPostFormat { | ||
41 | #pragma mark setup request | 41 | #pragma mark setup request |
42 | 42 | ||
43 | // Add a POST variable to the request | 43 | // Add a POST variable to the request |
44 | +- (void)addPostValue:(id <NSObject>)value forKey:(NSString *)key; | ||
45 | + | ||
46 | +// Set a POST variable for this request, clearing any others with the same key | ||
44 | - (void)setPostValue:(id <NSObject>)value forKey:(NSString *)key; | 47 | - (void)setPostValue:(id <NSObject>)value forKey:(NSString *)key; |
45 | 48 | ||
46 | // Add the contents of a local file to the request | 49 | // Add the contents of a local file to the request |
50 | +- (void)addFile:(NSString *)filePath forKey:(NSString *)key; | ||
51 | + | ||
52 | +// Same as above, but you can specify the content-type and file name | ||
53 | +- (void)addFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; | ||
54 | + | ||
55 | +// Add the contents of a local file to the request, clearing any others with the same key | ||
47 | - (void)setFile:(NSString *)filePath forKey:(NSString *)key; | 56 | - (void)setFile:(NSString *)filePath forKey:(NSString *)key; |
48 | 57 | ||
49 | // Same as above, but you can specify the content-type and file name | 58 | // Same as above, but you can specify the content-type and file name |
50 | - (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; | 59 | - (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; |
51 | 60 | ||
52 | // Add the contents of an NSData object to the request | 61 | // Add the contents of an NSData object to the request |
62 | +- (void)addData:(NSData *)data forKey:(NSString *)key; | ||
63 | + | ||
64 | +// Same as above, but you can specify the content-type and file name | ||
65 | +- (void)addData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; | ||
66 | + | ||
67 | +// Add the contents of an NSData object to the request, clearing any others with the same key | ||
53 | - (void)setData:(NSData *)data forKey:(NSString *)key; | 68 | - (void)setData:(NSData *)data forKey:(NSString *)key; |
54 | 69 | ||
55 | // Same as above, but you can specify the content-type and file name | 70 | // Same as above, but you can specify the content-type and file name |
@@ -15,8 +15,8 @@ | @@ -15,8 +15,8 @@ | ||
15 | - (void)buildURLEncodedPostBody; | 15 | - (void)buildURLEncodedPostBody; |
16 | - (void)appendPostString:(NSString *)string; | 16 | - (void)appendPostString:(NSString *)string; |
17 | 17 | ||
18 | -@property (retain) NSMutableDictionary *postData; | 18 | +@property (retain) NSMutableArray *postData; |
19 | -@property (retain) NSMutableDictionary *fileData; | 19 | +@property (retain) NSMutableArray *fileData; |
20 | 20 | ||
21 | #if DEBUG_FORM_DATA_REQUEST | 21 | #if DEBUG_FORM_DATA_REQUEST |
22 | - (void)addToDebugBody:(NSString *)string; | 22 | - (void)addToDebugBody:(NSString *)string; |
@@ -65,23 +65,38 @@ | @@ -65,23 +65,38 @@ | ||
65 | 65 | ||
66 | #pragma mark setup request | 66 | #pragma mark setup request |
67 | 67 | ||
68 | -- (void)setPostValue:(id <NSObject>)value forKey:(NSString *)key | 68 | +- (void)addPostValue:(id <NSObject>)value forKey:(NSString *)key |
69 | { | 69 | { |
70 | if (![self postData]) { | 70 | if (![self postData]) { |
71 | - [self setPostData:[NSMutableDictionary dictionary]]; | 71 | + [self setPostData:[NSMutableArray array]]; |
72 | } | 72 | } |
73 | - [[self postData] setValue:[value description] forKey:key]; | 73 | + [[self postData] addObject:[NSDictionary dictionaryWithObjectsAndKeys:[value description],@"value",key,@"key",nil]]; |
74 | } | 74 | } |
75 | 75 | ||
76 | -- (void)setFile:(NSString *)filePath forKey:(NSString *)key | 76 | +- (void)setPostValue:(id <NSObject>)value forKey:(NSString *)key |
77 | { | 77 | { |
78 | - [self setFile:filePath withFileName:nil andContentType:nil forKey:key]; | 78 | + // Remove any existing value |
79 | + NSUInteger i; | ||
80 | + for (i=0; i<[[self postData] count]; i++) { | ||
81 | + NSDictionary *val = [[self postData] objectAtIndex:i]; | ||
82 | + if ([[val objectForKey:@"key"] isEqualToString:key]) { | ||
83 | + [[self postData] removeObjectAtIndex:i]; | ||
84 | + i--; | ||
85 | + } | ||
86 | + } | ||
87 | + [self addPostValue:value forKey:key]; | ||
79 | } | 88 | } |
80 | 89 | ||
81 | -- (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key | 90 | + |
91 | +- (void)addFile:(NSString *)filePath forKey:(NSString *)key | ||
92 | +{ | ||
93 | + [self addFile:filePath withFileName:nil andContentType:nil forKey:key]; | ||
94 | +} | ||
95 | + | ||
96 | +- (void)addFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key | ||
82 | { | 97 | { |
83 | if (![self fileData]) { | 98 | if (![self fileData]) { |
84 | - [self setFileData:[NSMutableDictionary dictionary]]; | 99 | + [self setFileData:[NSMutableArray array]]; |
85 | } | 100 | } |
86 | 101 | ||
87 | // If data is a path to a local file | 102 | // If data is a path to a local file |
@@ -96,33 +111,72 @@ | @@ -96,33 +111,72 @@ | ||
96 | if (!fileName) { | 111 | if (!fileName) { |
97 | fileName = [(NSString *)data lastPathComponent]; | 112 | fileName = [(NSString *)data lastPathComponent]; |
98 | } | 113 | } |
99 | - | 114 | + |
100 | // If we were given the path to a file, and the user didn't specify a mime type, we can detect it from the file extension | 115 | // If we were given the path to a file, and the user didn't specify a mime type, we can detect it from the file extension |
101 | if (!contentType) { | 116 | if (!contentType) { |
102 | contentType = [ASIHTTPRequest mimeTypeForFileAtPath:data]; | 117 | contentType = [ASIHTTPRequest mimeTypeForFileAtPath:data]; |
103 | } | 118 | } |
104 | } | 119 | } |
105 | 120 | ||
106 | - NSDictionary *fileInfo = [NSDictionary dictionaryWithObjectsAndKeys:data, @"data", contentType, @"contentType", fileName, @"fileName", nil]; | 121 | + NSDictionary *fileInfo = [NSDictionary dictionaryWithObjectsAndKeys:data, @"data", contentType, @"contentType", fileName, @"fileName", key, @"key", nil]; |
107 | - [[self fileData] setObject:fileInfo forKey:key]; | 122 | + [[self fileData] addObject:fileInfo]; |
108 | } | 123 | } |
109 | 124 | ||
110 | -- (void)setData:(NSData *)data forKey:(NSString *)key | 125 | + |
126 | +- (void)setFile:(NSString *)filePath forKey:(NSString *)key | ||
111 | { | 127 | { |
112 | - [self setData:data withFileName:@"file" andContentType:nil forKey:key]; | 128 | + [self setFile:filePath withFileName:nil andContentType:nil forKey:key]; |
113 | } | 129 | } |
114 | 130 | ||
115 | -- (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key | 131 | +- (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key |
132 | +{ | ||
133 | + // Remove any existing value | ||
134 | + NSUInteger i; | ||
135 | + for (i=0; i<[[self fileData] count]; i++) { | ||
136 | + NSDictionary *val = [[self fileData] objectAtIndex:i]; | ||
137 | + if ([[val objectForKey:@"key"] isEqualToString:key]) { | ||
138 | + [[self fileData] removeObjectAtIndex:i]; | ||
139 | + i--; | ||
140 | + } | ||
141 | + } | ||
142 | + [self addFile:data withFileName:fileName andContentType:contentType forKey:key]; | ||
143 | +} | ||
144 | + | ||
145 | +- (void)addData:(NSData *)data forKey:(NSString *)key | ||
146 | +{ | ||
147 | + [self addData:data withFileName:@"file" andContentType:nil forKey:key]; | ||
148 | +} | ||
149 | + | ||
150 | +- (void)addData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key | ||
116 | { | 151 | { |
117 | if (![self fileData]) { | 152 | if (![self fileData]) { |
118 | - [self setFileData:[NSMutableDictionary dictionary]]; | 153 | + [self setFileData:[NSMutableArray array]]; |
119 | } | 154 | } |
120 | if (!contentType) { | 155 | if (!contentType) { |
121 | contentType = @"application/octet-stream"; | 156 | contentType = @"application/octet-stream"; |
122 | } | 157 | } |
123 | 158 | ||
124 | - NSDictionary *fileInfo = [NSDictionary dictionaryWithObjectsAndKeys:data, @"data", contentType, @"contentType", fileName, @"fileName", nil]; | 159 | + NSDictionary *fileInfo = [NSDictionary dictionaryWithObjectsAndKeys:data, @"data", contentType, @"contentType", fileName, @"fileName", key, @"key", nil]; |
125 | - [[self fileData] setObject:fileInfo forKey:key]; | 160 | + [[self fileData] addObject:fileInfo]; |
161 | +} | ||
162 | + | ||
163 | +- (void)setData:(NSData *)data forKey:(NSString *)key | ||
164 | +{ | ||
165 | + [self setData:data withFileName:@"file" andContentType:nil forKey:key]; | ||
166 | +} | ||
167 | + | ||
168 | +- (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key | ||
169 | +{ | ||
170 | + // Remove any existing value | ||
171 | + NSUInteger i; | ||
172 | + for (i=0; i<[[self fileData] count]; i++) { | ||
173 | + NSDictionary *val = [[self fileData] objectAtIndex:i]; | ||
174 | + if ([[val objectForKey:@"key"] isEqualToString:key]) { | ||
175 | + [[self fileData] removeObjectAtIndex:i]; | ||
176 | + i--; | ||
177 | + } | ||
178 | + } | ||
179 | + [self addData:data withFileName:fileName andContentType:contentType forKey:key]; | ||
126 | } | 180 | } |
127 | 181 | ||
128 | - (void)buildPostBody | 182 | - (void)buildPostBody |
@@ -178,12 +232,10 @@ | @@ -178,12 +232,10 @@ | ||
178 | 232 | ||
179 | // Adds post data | 233 | // Adds post data |
180 | NSString *endItemBoundary = [NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary]; | 234 | NSString *endItemBoundary = [NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary]; |
181 | - NSEnumerator *e = [[self postData] keyEnumerator]; | ||
182 | - NSString *key; | ||
183 | NSUInteger i=0; | 235 | NSUInteger i=0; |
184 | - while (key = [e nextObject]) { | 236 | + for (NSDictionary *val in [self postData]) { |
185 | - [self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key]]; | 237 | + [self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",[val objectForKey:@"key"]]]; |
186 | - [self appendPostString:[[self postData] objectForKey:key]]; | 238 | + [self appendPostString:[val objectForKey:@"value"]]; |
187 | i++; | 239 | i++; |
188 | if (i != [[self postData] count] || [[self fileData] count] > 0) { //Only add the boundary if this is not the last item in the post body | 240 | if (i != [[self postData] count] || [[self fileData] count] > 0) { //Only add the boundary if this is not the last item in the post body |
189 | [self appendPostString:endItemBoundary]; | 241 | [self appendPostString:endItemBoundary]; |
@@ -191,21 +243,17 @@ | @@ -191,21 +243,17 @@ | ||
191 | } | 243 | } |
192 | 244 | ||
193 | // Adds files to upload | 245 | // Adds files to upload |
194 | - e = [fileData keyEnumerator]; | ||
195 | i=0; | 246 | i=0; |
196 | - while (key = [e nextObject]) { | 247 | + for (NSDictionary *val in [self fileData]) { |
197 | - NSDictionary *fileInfo = [[self fileData] objectForKey:key]; | 248 | + |
198 | - id file = [fileInfo objectForKey:@"data"]; | 249 | + [self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", [val objectForKey:@"key"], [val objectForKey:@"fileName"]]]; |
199 | - NSString *contentType = [fileInfo objectForKey:@"contentType"]; | 250 | + [self appendPostString:[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", [val objectForKey:@"contentType"]]]; |
200 | - NSString *fileName = [fileInfo objectForKey:@"fileName"]; | ||
201 | - | ||
202 | - [self appendPostString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, fileName]]; | ||
203 | - [self appendPostString:[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", contentType]]; | ||
204 | 251 | ||
205 | - if ([file isKindOfClass:[NSString class]]) { | 252 | + id data = [val objectForKey:@"data"]; |
206 | - [self appendPostDataFromFile:file]; | 253 | + if ([data isKindOfClass:[NSString class]]) { |
254 | + [self appendPostDataFromFile:data]; | ||
207 | } else { | 255 | } else { |
208 | - [self appendPostData:file]; | 256 | + [self appendPostData:data]; |
209 | } | 257 | } |
210 | i++; | 258 | i++; |
211 | // Only add the boundary if this is not the last item in the post body | 259 | // Only add the boundary if this is not the last item in the post body |
@@ -241,12 +289,10 @@ | @@ -241,12 +289,10 @@ | ||
241 | [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@",charset]]; | 289 | [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@",charset]]; |
242 | 290 | ||
243 | 291 | ||
244 | - NSEnumerator *e = [[self postData] keyEnumerator]; | ||
245 | - NSString *key; | ||
246 | NSUInteger i=0; | 292 | NSUInteger i=0; |
247 | NSUInteger count = [[self postData] count]-1; | 293 | NSUInteger count = [[self postData] count]-1; |
248 | - while (key = [e nextObject]) { | 294 | + for (NSDictionary *val in [self postData]) { |
249 | - NSString *data = [NSString stringWithFormat:@"%@=%@%@", [self encodeURL:key], [self encodeURL:[[self postData] objectForKey:key]],(i<count ? @"&" : @"")]; | 295 | + NSString *data = [NSString stringWithFormat:@"%@=%@%@", [self encodeURL:[val objectForKey:@"key"]], [self encodeURL:[val objectForKey:@"value"]],(i<count ? @"&" : @"")]; |
250 | [self appendPostString:data]; | 296 | [self appendPostString:data]; |
251 | i++; | 297 | i++; |
252 | } | 298 | } |
@@ -23,7 +23,7 @@ | @@ -23,7 +23,7 @@ | ||
23 | 23 | ||
24 | 24 | ||
25 | // Automatically set on build | 25 | // Automatically set on build |
26 | -NSString *ASIHTTPRequestVersion = @"v1.6.2-8 2010-05-01"; | 26 | +NSString *ASIHTTPRequestVersion = @"v1.6.2-9 2010-05-10"; |
27 | 27 | ||
28 | NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; | 28 | NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; |
29 | 29 |
@@ -227,5 +227,44 @@ | @@ -227,5 +227,44 @@ | ||
227 | [request2 release]; | 227 | [request2 release]; |
228 | } | 228 | } |
229 | 229 | ||
230 | +- (void)testMultipleValuesForASingleKey | ||
231 | +{ | ||
232 | + ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/multiple-values"]]; | ||
233 | + [request addPostValue:@"here" forKey:@"test_value[]"]; | ||
234 | + [request addPostValue:@"are" forKey:@"test_value[]"]; | ||
235 | + [request addPostValue:@"some" forKey:@"test_value[]"]; | ||
236 | + [request addPostValue:@"values" forKey:@"test_value[]"]; | ||
237 | + | ||
238 | + NSString *path1 = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"file1.txt"]; | ||
239 | + NSString *path2 = [[self filePathForTemporaryTestFiles] stringByAppendingPathComponent:@"file2.txt"]; | ||
240 | + [@"hello" writeToFile:path1 atomically:NO encoding:NSUTF8StringEncoding error:nil]; | ||
241 | + [@"there" writeToFile:path2 atomically:NO encoding:NSUTF8StringEncoding error:nil]; | ||
242 | + [request addFile:path1 forKey:@"test_file[]"]; | ||
243 | + [request addFile:path2 forKey:@"test_file[]"]; | ||
244 | + | ||
245 | + [request startSynchronous]; | ||
246 | + NSString *expectedOutput = @"here\r\nare\r\nsome\r\nvalues\r\nfile1.txt\r\nfile2.txt\r\n"; | ||
247 | + BOOL success = [[request responseString] isEqualToString:expectedOutput]; | ||
248 | + GHAssertTrue(success,@"Failed to send the correct data"); | ||
249 | + | ||
250 | + // Check data replaces older data | ||
251 | + request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/single-values"]]; | ||
252 | + [request addPostValue:@"here" forKey:@"test_value[]"]; | ||
253 | + [request addPostValue:@"are" forKey:@"test_value[]"]; | ||
254 | + [request addPostValue:@"some" forKey:@"test_value[]"]; | ||
255 | + [request addPostValue:@"values" forKey:@"test_value[]"]; | ||
256 | + | ||
257 | + [request setPostValue:@"this is new data" forKey:@"test_value[]"]; | ||
258 | + | ||
259 | + [request addFile:path1 forKey:@"test_file[]"]; | ||
260 | + [request addFile:path2 forKey:@"test_file[]"]; | ||
261 | + | ||
262 | + [request setData:[@"this is new data" dataUsingEncoding:NSUTF8StringEncoding] forKey:@"test_file[]"]; | ||
263 | + | ||
264 | + [request startSynchronous]; | ||
265 | + expectedOutput = @"this is new data\r\nfile\r\n"; | ||
266 | + success = [[request responseString] isEqualToString:expectedOutput]; | ||
267 | + GHAssertTrue(success,@"Failed to send the correct data"); | ||
268 | +} | ||
230 | 269 | ||
231 | @end | 270 | @end |
-
Please register or login to post a comment