在iOS中发送多部分POST请求并在PHP $_POST中读取参数?

9
我正在尝试从iPhone/iPad向php服务发送多行发布内容,问题在于由于某种原因,POST Content-Type似乎是application/x-www-form-urlencoded(我使用Wireshark发现了这个)。
这实际上是Wireshark POST数据包捕获的一部分:
    **Content-Type: application/x-www-form-urlencoded\r\n**
    Content-Length: 324\r\n
        [Content length: 324]
    Connection: keep-alive\r\n
    \r\n
    [Full request URI: http://my.server.com/mobile/tools/authentication]
Line-based text data: application/x-www-form-urlencoded
    --0xKhTmLbOuNdArY\r\n
    Content-Disposition: form-data; name="login"\r\n
    \r\n
    hello@hello.com\r\n
    --0xKhTmLbOuNdArY\r\n
    Content-Disposition: form-data; name="password"\r\n
    \r\n
    somepassword\r\n
    --0xKhTmLbOuNdArY\r\n
    \r\n
    --0xKhTmLbOuNdArY--\r\n

问题在于我尝试从这个POST请求中读取登录和密码变量,但由于PHP认为POST请求是x-www-form-urlencoded类型的,所以这是不可能的。如果我在authentication.php中进行如下设置:
<?php
echo("<pre>")
print_r($_POST)
echo("</pre>")
?>

I get this:

<pre>Array\n
(\n
    [--0xKhTmLbOuNdArY\r\n
Content-Disposition:_form-data;_name] => "login"\r\n
\r\n
hello@hello.com\r\n
--0xKhTmLbOuNdArY\r\n
Content-Disposition: form-data; name="password"\r\n
\r\n
somepassword\r\n
--0xKhTmLbOuNdArY\r\n
\r\n
--0xKhTmLbOuNdArY--\r\n
\n
)\n
</pre>

这显然不好,因为如果我使用这个简单的HTML表单发送请求:

<FORM action="http://my.server.com/mobile/tools/authentication.php" method="post">
   <P>
   <LABEL for="login">E-mail: </LABEL>
             <INPUT type="text" name="login"><BR>
   <LABEL for="password">pass: </LABEL>
             <INPUT type="text" name="password"><BR>
   <INPUT type="submit" value="Send"> <INPUT type="reset">
   </P>
</FORM>

我从一个表单中得到了这个Wireshark跟踪结果:

    Content-Type: application/x-www-form-urlencoded\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
    Accept-Encoding: gzip,deflate,sdch\r\n
    Accept-Language: en-US,en;q=0.8\r\n
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n
    Cookie: PHPSESSID=tpp10pbrdkkf5dg9qprlamfuu2\r\n
    \r\n
    [Full request URI: http://mymed2.sophia.inria.fr/mobile/tools/authentication.php]
Line-based text data: application/x-www-form-urlencoded
    login=hello%40hello.com&password=somepassword

这是从 authentication.php 中的 print_r($_POST) 输出的文本。

<pre>Array\n
(\n
    [login] => hello@hello.com\n
    [password] => somepassword\n
)\n
</pre>

这是我在Objective-C和Cocoa中编写POST请求的方法。
- (NSMutableURLRequest *) POSTRequestWithURL:(NSURL *)url andDataDictionary:(NSDictionary *) dictionary
{
    // Create a POST request
    NSMutableURLRequest *myMedRequest = [NSMutableURLRequest requestWithURL:url];
    [myMedRequest setHTTPMethod:@"POST"];

    // Add HTTP header info
    // Note: POST boundaries are described here: http://www.vivtek.com/rfc1867.html
    // and here http://www.w3.org/TR/html4/interact/forms.html
    NSString *POSTBoundary = [NSString stringWithString:@"0xKhTmLbOuNdArY"];
    [myMedRequest addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@\r\n", POSTBoundary] forHTTPHeaderField:@"Content-Type"];

    // Add HTTP Body
    NSMutableData *POSTBody = [NSMutableData data];
    [POSTBody appendData:[[NSString stringWithFormat:@"--%@\r\n",POSTBoundary] dataUsingEncoding:NSUTF8StringEncoding]];

    // Add Key/Values to the Body
    NSEnumerator *enumerator = [dictionary keyEnumerator];
    NSString *key;

    while ((key = [enumerator nextObject])) {
        [POSTBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
        [POSTBody appendData:[[NSString stringWithFormat:@"%@", [dictionary objectForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];
        [POSTBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", POSTBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
    }

    // Add the closing -- to the POST Form
    [POSTBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", POSTBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; 

    // Add the body to the myMedRequest & return
    [myMedRequest setHTTPBody:POSTBody];
    return myMedRequest;
}

正如您所见,我在开始时执行以下操作:

    [myMedRequest addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@\r\n", POSTBoundary] forHTTPHeaderField:@"Content-Type"];

但在之前的Wireshark跟踪日志中,POST数据包被视为Content-Type: application/x-www-form-urlencoded,也许是我做错了什么?

谢谢 :)

编辑:顺便说一句,我想避免使用像ASIHTTPRequest这样的外部框架,因为ASIHTTPRequest的开发已被主要开发人员放弃,详情请见这里


我能想到的唯一问题是:为什么你要手动编写 HTTP 请求?这不是 API 的作用吗? - CodeCaster
NSMutableURLRequest是提供的“API”,有一些框架可以做到这一点,但我试图避免使用外部框架。 - Goles
1个回答

19

好的,既然我找不到没有使用外部库的解决方案,我修复了我的代码并开源了它,以防有人想使用它。

我发布的代码中问题出在该行上的\r\n。

[myMedRequest addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@\r\n", POSTBoundary] forHTTPHeaderField:@"Content-Type"];

移除那个固定的东西就解决了问题。

希望这能帮到其他人 :)


已更新。现在也支持PUT/DELETE/PATCH。 :) - Goles
谢谢!我只是在试用它。它能处理嵌套字典吗?我正在尝试发布一个包含“entry”字典的图像,该字典位于根级别上,然后在嵌套级别上有描述、journal_id和图片。 - Kyle Clegg
不这么认为,但你可以在Github上提出一个问题 :) - Goles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接