Reputation: 688
I am trying to upload a text using multi-part form encoding
in iOS.
What i Did :
I have tried via the info provided in the ios Upload Image and Text using HTTP POST.
Problem :
I put log in server side, And here is what did i receive from my IOS App.
No problem with key
Name. But, I receive the value
as null
"443" "POST" "/api/private/json/" "content=null&scope=null&action=null&documentname=null&apikey=null"
My code :
NSMutableDictionary *params = [NSMutableDictionary new];
[params setObject:@"XXXXX" forKey:@"apikey"];
[params setObject:@"DataAPI" forKey:@"scope"];
[params setObject:@"push" forKey:@"action"];
[params setObject:docName forKey:@"documentName"];
[params setObject:content forKey:@"content"];
[params setObject:authToken forKey:@"authtoken"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:30];
[request setHTTPMethod:@"POST"];
// set Content-Type in HTTP header
NSString *boundary = @"V2ymHFg03ehbqgZCaKO6jy";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];
NSMutableData *body = [NSMutableData data];
// add params (all params are strings)
for (NSString *param in params) {
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", param] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@\r\n", [params objectForKey:param]] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// setting the body of the post to the reqeust
[request setHTTPBody:body];
NSString *myString = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding];
NSLog(@"%@",myString);
// set the content-length
NSString *postLength = [NSString stringWithFormat:@"%d", [body length]];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
// set URL
[request setURL:url];
NSURLResponse *response;
NSData *POSTReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *theReply = [[NSString alloc] initWithBytes:[POSTReply bytes] length:[POSTReply length] encoding: NSASCIIStringEncoding];
NSLog(@"Reply: %@", theReply);
I couldn't able to understand the issue, Whether it is from server side or from my code.
Thanks for sharing your answers.
Upvotes: 0
Views: 2755
Reputation: 19098
This should fix it:
Instead of:
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
write
[body appendData:[[NSString stringWithFormat:@"--%@--", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
(the terminating CRLF is optional)
Please note if you write this:
for (NSString *param in params) {
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", param] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@\r\n", [params objectForKey:param]] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
that everything before the boundary-delimiter, e.g. \r\n--<boundary>
conceptually belongs to the data, e.g. to the parameter value.
Likewise, the preceding CRLF
of a "dash-boundary" (--<boundary>
) belongs to the delimiter.
Conceptually, the first delimiter MUST also start with a CRLF:
`CRLF--<boundary>`
however, it seems NSURLConnection already puts a CRLF after the message headers. So we end up having this:
Content-Type: multipart/form-data; boundary="xyz"CRLF
<other header>CRLF
CRLF <-- added by NSURLConnection, which is debatable if this is correct
... multipart body starts here
Any bytes after the headers and before the start of multipart shall be ignored by the server. This kind of junk is called preamble
:
Content-Type: multipart/form-data; boundary="xyz"CRLF
<other header>CRLF
<preamble bytes>CRLF--xyz
^~~~~~~~~ delimiter
....
preamble
may be empty or it may contain any number of CRLFs, for example.
The body-part
, which consists of MIME headers and the part body-data, follows after the boundary, but strictly not immediately:
<preamble-bytes>CRLF--xyz<transport-padding>CRLF<body-part>
^~~~~~~~~ delimiter ^~~~~~~~~~~ MIME headers and data
where <transport-padding>
is any number of spaces or horizontal tabs, that is, it may be empty.
This all means, that we should be able to use the following line as a non-terminating delimiter (for the first and any subsequent non-terminating boundary-delimiter) plus a CRLF:
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
Then the body-part
immediately follows (starting with headers, then the data):
[body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@", (self.parameters)[key]] dataUsingEncoding:NSUTF8StringEncoding]];
These four lines can be used in a loop for each parameter.
The terminating delimiter can be added as follows:
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
A terminating CRLF is optional. Any bytes following the last CRLF belong to the epilogue
and shall be ignored by the server.
See: RCF 2046 http://www.ietf.org/rfc/rfc2046.txt.
Upvotes: 1
Reputation: 2746
You are not appending the terminating boundary.
This:
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
Should look like this:
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
EDIT:
You are missing some more things actually. You are not setting the content type for the data and you're missing some new lines.
Your loop should look like this:
for (NSString *param in params) {
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", param] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@\r\n", [params objectForKey:param]] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"\r\n\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
EDIT2:
Here's an example server-side code which is handling that same request correctly:
#!/usr/bin/env python
import web
urls = ("/", "Index")
app = web.application(urls, globals())
class Index(object):
def POST(self):
x = web.input()
print x
print x['apikey']
print x['scope']
return 'success'
if __name__ == "__main__":
app.run()
Output:
<Storage {'scope': u'asd', 'apikey': u'123\r\n'}>
123
asd
127.0.0.1:60044 - - [02/Dec/2013 15:02:55] "HTTP/1.1 POST /" - 200 OK
And sample console app. which you can compile with clang - clang -framework Foundation test_post.m -o test_post
#import <Foundation/Foundation.h>
@interface Uploader : NSObject <NSURLConnectionDataDelegate>
@property (nonatomic, strong) NSDictionary *parameters;
@property (nonatomic, strong) NSURLConnection *connection;
@end
@implementation Uploader
- (id)init {
if (self = [super init]) {
}
return self;
}
- (NSMutableURLRequest*)createRequest {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:@"http://localhost:8080/"]];
[request setHTTPMethod:@"POST"];
NSString *boundary = @"0xB0un9ArY";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];
[request setValue:@"1.0" forHTTPHeaderField:@"MIME-Version"];
[request setValue:@"keep-alive" forHTTPHeaderField:@"Connection"];
NSMutableData *body = [NSMutableData data];
for (NSString *key in [self.parameters allKeys]) {
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@\r\n", (self.parameters)[key]] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];
return request;
}
- (void)upload {
NSMutableURLRequest *request = [self createRequest];
self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (self.connection) [self.connection start];
}
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {NSLog(@"connection:didReceiveResponse: %@", response);}
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {NSLog(@"connection: didReceiveData: %@", data);}
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error {NSLog(@"connection: didFailWithError: %@", error);}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection { NSLog(@"connectionDidFinishLoading:"); }
@end
int main(int argc, char *argv[])
{
@autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
Uploader *uploader = [[Uploader alloc] init];
uploader.parameters = @{@"apikey": @"123", @"scope": @"asd"};
[uploader upload];
[runLoop run];
}
return 0;
}
Upvotes: 1
Reputation: 151
NSURL *url = [NSURL URLWithString:@"http://210.7.64.98/360videos/webservices/devicetoken.php"];
NSDictionary *postDict = [NSDictionary dictionaryWithObjectsAndKeys:devToken1, @"token",
@"iphone", @"device_type", nil];
NSData *postData = [self encodeDictionary:postDict];
// Create the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
[request setValue:[NSString stringWithFormat:@"%d", postData.length] forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Peform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error) {
// Deal with your error
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error);
return;
}
NSLog(@"Error %@", error);
return;
}
NSString *responeString = [[NSString alloc] initWithData:receivedData
encoding:NSUTF8StringEncoding];
NSLog(@"token Response : %@",responeString);
});
//here is encode method
Upvotes: 0