【问题标题】:Does Invoke-WebRequest support arrays as POST form parameters?Invoke-WebRequest 是否支持数组作为 POST 表单参数?
【发布时间】:2014-07-11 20:53:18
【问题描述】:

看来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"...

【问题讨论】:

    标签: powershell powershell-3.0


    【解决方案1】:

    你是对的。 Invoke-WebRequest 的 Body 参数是一个对象,但对于表单键值对,它需要一个 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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-07
      • 2016-09-18
      • 1970-01-01
      • 1970-01-01
      • 2017-12-24
      • 1970-01-01
      相关资源
      最近更新 更多