似乎Invoke-WebRequest
无法将数组序列化为POST表单数据:
PS> $uri = "http://httpbin.org/post"
PS> Invoke-WebRequest $uri -Body @{Stuff=[string[]]@("abc", "def")} -Method Post
StatusCode : 200
StatusDescription : OK
Content : {
"args": {},
"data": "",
"files": {},
"form": {
"Stuff": "System.String[]"
},
"headers": {
"Cache-Control": "max-age=259200",
"Connection": "close",
"Content-Length"...
RawContent : HTTP/1.0 200 OK
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 589
Content-Type: application/json
Date: Fri, 11 Jul 2014 20:40:42 GMT
Server...
Forms : {}
Headers : {[Access-Control-Allow-Origin, *], [Connection, keep-alive],
[Content-Length, 589]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 589
由于.NET不接受字典中的重复键名,我不能这样做:
PS> Invoke-WebRequest $uri -Body @{Stuff="abc"; Stuff="def"} -Method Post
At line:1 char:45
+ Invoke-WebRequest $uri -Body @{Stuff="abc"; Stuff="def"} -Method Post
+ ~~~~~
Duplicate keys 'Stuff' are not allowed in hash literals.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : DuplicateKeyInHashLiteral
我通过发送内容为Stuff[]=abc&Stuff[]=def
的原始HTTP请求,仔细检查了错误是否来自httpbin,我得到了此响应:
{
"args": {},
"data": "",
"files": {},
"form": {
"Stuff[]": [
"abc",
"def"
]
},
"headers": {
"Cache-Control": "max-age=259200",
"Connection": "close",
"Content-Length"...
答案 0 :(得分:3)
你是对的。 Invoke-WebRequest的Body参数是一个Object,但是对于表单键值对,它需要一个IDictionary并且不处理数组。
可能的解决方法是使用System.Net.HttpWebRequest手动构建请求,并在System.Collections.Specialized.NameValueCollection中发送表单键值对。
function Invoke-WebRequestExample
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][System.Uri] $Uri,
[Parameter(Mandatory=$false)][System.Object] $Body,
[Parameter(Mandatory=$false)][Microsoft.PowerShell.Commands.WebRequestMethod] $Method
# Extend as necessary to match the signature of Invoke-WebRequest to fit your needs.
)
Process
{
# If not posting a NameValueCollection, just call the native Invoke-WebRequest.
if ($Body -eq $null -or $body.GetType() -ne [System.Collections.Specialized.NameValueCollection]) {
Invoke-WebRequest @PsBoundParameters
return;
}
$params = "";
$i = 0;
$j = $body.Count;
$first = $true;
while ($i -lt $j) {
$key = $body.GetKey($i);
$body.GetValues($i) | %{
$val = $_;
if (!$first) {
$params += "&";
} else {
$first = $false;
}
$params += [System.Web.HttpUtility]::UrlEncode($key) + "=" + [System.Web.HttpUtility]::UrlEncode($val);
};
$i++;
}
$b = [System.Text.Encoding]::UTF8.GetBytes($params);
# Use HttpWebRequest instead of Invoke-WebRequest, because the latter doesn't support arrays in POST params.
$req = [System.Net.HttpWebRequest]::Create($Uri);
$req.Method = "POST";
$req.ContentLength = $params.Length;
$req.ContentType = "application/x-www-form-urlencoded";
$str = $req.GetRequestStream();
$str.Write($b, 0, $b.Length);
$str.Close();
$str.Dispose();
[HttpWebResponse] $res = $req.GetResponse();
$str = $res.GetResponseStream();
$rdr = New-Object -TypeName "System.IO.StreamReader" -ArgumentList ($str);
$content = $rdr.ReadToEnd();
$str.Close();
$str.Dispose();
$rdr.Dispose();
# Build a return object that's similar to a Microsoft.PowerShell.Commands.HtmlWebResponseObject
$ret = New-Object -TypeName "System.Object";
$ret | Add-Member -Type NoteProperty -Name "BaseResponse" -Value $res;
$ret | Add-Member -Type NoteProperty -Name "Content" -Value $content;
$ret | Add-Member -Type NoteProperty -Name "StatusCode" -Value ([int] $res.StatusCode);
$ret | Add-Member -Type NoteProperty -Name "StatusDescription" -Value $res.StatusDescription;
return $ret;
}
}
$uri = "http://www.someurl.com/";
$b1 = @{myval = "foo"};
Invoke-WebRequestExample -Uri $uri -Body $b1 -Method Post;
$b2 = New-Object -TypeName "System.Collections.Specialized.NameValueCollection";
$b2.Add("myval[]", "foo");
$b2.Add("myval[]", "bar");
Invoke-WebRequestExample -Uri $uri -Body $b2 -Method Post;