powershell 为什么Invoke-WebRequest和Invoke-RestMethod同时失败和成功?

5us2dqdw  于 2023-01-13  发布在  Shell
关注(0)|答案(2)|浏览(202)

我编写了一个小的PowerShell脚本来向服务器发送请求并返回一个简单的XML结果。

PowerShell脚本

$xml = "<?xml version='1.0' encoding='utf-8'?><Search><ID>278A87E1-1BC2-4E19-82E9-8BBE31D67D20</ID></Search>"
$response = Invoke-RestMethod -Method Post -Uri "http://localhost/search" -ContentType "application/xml" -Body $xml

就是这样,非常简单,我看不出它会失败。我还尝试了Invoke-WebRequest脚本,但都失败了。返回的错误是Invoke-RestMethod : Value cannot be null. Parameter name: name。奇怪的是,当我使用Wireshark监视此脚本时,我看到了连接,我看到了POST,并看到了来自服务器的结果,一切看起来都很好,但cmdlet说它失败了(是的,返回代码是200)。
如果我使用-OutFile参数运行Invoke-WebRequest/Invoke-RestMethod,它运行良好,没有错误,并将结果保存到指定的文件中;-OutVariable会失败,以防您感到疑惑。
结果是一个xml文件,标头指定它是xml,并且xml格式正确。

成功时的结果

<?xml version="1.0" encoding="UTF-8" ?>
<Result version="1.0" xmlns="urn:xmlns-org">
    <searchID>{278a87e1-1bc2-4e19-82e9-8bbe31d67d20}</searchID>
    <responseStatus>true</responseStatus>
    <responseStatusStrg>MORE</responseStatusStrg>
    <numOfMatches>40</numOfMatches>
</Result>

有人知道为什么Invoke-XXX cmdlet返回错误以及我可以做些什么来修复它吗?同样,当我使用-OutFile参数时,它工作得非常好,即使当它失败时,我也可以看到脚本和Wireshark中的服务器之间的正确对话。
另外,如果我使用-Verbose,它会告诉我以下内容:

VERBOSE: POST http://localhost/search with -1-byte payload
VERBOSE: received X-byte response of content type application/xml; charset="UTF-8"

其中X-byte是响应的实际大小,但根据发送到服务器的数据,每个响应的大小显然不同。我只是觉得奇怪的是,cmdlet失败了,但说它收到了带有数据的响应,并发送了-1-byte有效负载。

u3r8eeie

u3r8eeie1#

我继续查看了Invoke-WebRequest cmdlet代码,并找出了它失败并出现此特定错误的原因。
调用System.Globalization.EncodingTable.GetCodePageFromName失败。编码作为参数传递给该函数,并通过Content-Type标头从cmdlet中检索编码。对于此服务器,Content-Type在响应中作为Content-Type: application/xml; charset="UTF-8"发送回。
问题是引号不是将值括在charset中的标准方法,因此cmdlet将其解析为"UTF-8",而不是有效的UTF-8。cmdlet将"UTF-8"传递给函数,函数将引发异常,指出提供的编码无效。这是很好的,如果这是在最后一个异常中报告的,那么就更有意义了,但事实并非如此。
无效编码异常由Microsoft.PowerShell.Commands.ContentHelper.GetEncodingOrDefault函数捕获,并在异常处理程序中再次调用GetEncoding,但使用空参数,这导致参数name的最终ArgumentNullException

Microsoft.PowerShell.命令.内容帮助程序.获取编码或默认值

internal static Encoding GetEncodingOrDefault(string characterSet)
{
  string name = string.IsNullOrEmpty(characterSet) ? "ISO-8859-1" : characterSet;
  try
  {
    return Encoding.GetEncoding(name);
  }
  catch (ArgumentException ex)
  {
    return Encoding.GetEncoding((string) null);
  }
}

catch语句中对GetEncoding的调用触发GetCodePageFromName中的以下代码,该代码本身是从GetEncoding调用的

if (name==null) { 
    throw new ArgumentNullException("name");
}

PowerShell正在正确地处理这个问题,因为从技术上讲,它是一个无效的值,但你可能会认为他们会调用Trim("\"")只是为了安全。

m3eecexj

m3eecexj2#

虽然这个线程是多年前的,但我在2022年遇到了它,当时我遇到了同样的错误“Invoke-RestMethod:值不能为空。参数名:name'访问一个API来创建一个用户。在我的例子中,内容类型是application/json(而不是上面Vane问题中的application/xml)。API POST需要基本身份验证。
我在转换到Base64之前用UTF8编码了凭证,然后错误消失了。鉴于上面Vane的Content-Type调查跟踪了charset="UTF-8"的返回,这个UTF8编码和错误的消失可能有关,尽管这只是我的猜测。

$username = "your_username"
$password = "your_password"
$credential = "${username}:${password}"
$credentialBytes = [System.Text.Encoding]::UTF8.GetBytes($credential)
$encodedCredential = [System.Convert]::ToBase64String($credentialBytes)
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic $encodedCredential")
$headers.Add("Content-Type", "application/json")

相关问题