Powershell - 将 WebRequest 调用到 URL,其中包含文字“/”(%2F)

Powershell - Invoke-WebRequest to a URL with literal '/' (%2F) in it

我一直在尝试使用以下命令从 powershell 访问其中包含 / 字符的 URL(这是对 gitlab 服务器的查询,以检索名为 [=16 的项目=]):

Invoke-WebRequest https://server.com/api/v3/projects/foo%2Fbar -Verbose

现在,奇怪的是使用 PowerShell ISE 或 Visual Studio,请求是正常的。使用 PowerShell 本身时,URL 会自动取消转义并且请求失败。 例如。

在ISE/VS中:

$> Invoke-WebRequest https://server.com/api/v3/projects/foo%2Fbar -Verbose
VERBOSE: GET https://server.com/api/v3/projects/foo%2Fbar with 0-byte payload
VERBOSE: received 19903-byte response of content type application/json

StatusCode        : 200
StatusDescription : OK
Content           : .... data ....

在 Powershell 中:

$> Invoke-WebRequest https://server.com/api/v3/projects/foo%2Fbar -Verbose
VERBOSE: GET https://server.com/api/v3/projects/foo/bar with 0-byte payload
Invoke-WebRequest : {"error":"404 Not Found"}
At line:1 char:1
+ Invoke-WebRequest 'https://server.com/api/v3/projects/foo%2Fbar ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

我尝试在 URL 周围添加单引号和双引号,但没有任何帮助。

这种行为的原因是什么,我如何使 PS 不取消转义 URL 字符串?


环境:Windows7,也在Server 2012R2上测试,结果相同。

$> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      4.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.42000
BuildVersion                   6.3.9600.16406
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion      2.2

通过此功能尝试URL

function fixuri($uri){
  $UnEscapeDotsAndSlashes = 0x2000000;
  $SimpleUserSyntax = 0x20000;

  $type = $uri.GetType();
  $fieldInfo = $type.GetField("m_Syntax", ([System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic));

  $uriParser = $fieldInfo.GetValue($uri);
  $typeUriParser = $uriParser.GetType().BaseType;
$fieldInfo = $typeUriParser.GetField("m_Flags", ([System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::FlattenHierarchy));
$uriSyntaxFlags = $fieldInfo.GetValue($uriParser);

$uriSyntaxFlags = $uriSyntaxFlags -band (-bnot $UnEscapeDotsAndSlashes);
$uriSyntaxFlags = $uriSyntaxFlags -band (-bnot $SimpleUserSyntax);
$fieldInfo.SetValue($uriParser, $uriSyntaxFlags);
}

$uri = New-Object System.Uri -ArgumentList ("https://server.com/api/v3/projects/foo%2Fbar")
fixuri $uri

我在 PowerShell 5.1 中遇到过类似的问题。我的目的是通过 Git Lab Web API 获得一个项目。正如网络 API 所述:

Get single project
Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user. If using namespaced projects call make sure that the NAMESPACE/PROJECT_NAME is URL-encoded, eg. /api/v3/projects/diaspora%2Fdiaspora (where / is represented by %2F).

nik 的不同之处在于,我的 Invoke-WebRequest 调用通过直接调用成功,但在作业中失败。这是代码:

Start-Job -ScriptBlock {
    try{
        Invoke-WebRequest https://server.com/api/v3/projects/foo%2Fbar -verbose
    } catch {
        Write-Output $_.Exception
    }
}

获取作业中的输出。 运行 命令:

> Get-Job | Receive-Job -Keep

例外情况如下:

VERBOSE: GET https://server.com/api/v3/projects/foo/bar with 0-byte payload
System.Net.WebException: The remote server returned an error: (404) Not Found.
  at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.GetResponse(WebRequest request)
  at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()

感谢 Oleg SH 的回答。我的问题解决了。但我认为 Start-Job cmdlet 中可能存在错误


环境:Windows7

>$PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.14409.1005
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14409.1005
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

这是 的备用端口 - 它接受一个字符串和 returns 一个新的 URI。

function CreateUriWithoutIncorrectSlashEncoding {
    param(
        [Parameter(Mandatory)][string]$uri
    )

    $newUri = New-Object System.Uri $uri

    [void]$newUri.PathAndQuery # need to access PathAndQuery (presumably modifies internal state)
    $flagsFieldInfo = $newUri.GetType().GetField("m_Flags", [System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
    $flags = $flagsFieldInfo.GetValue($newUri)
    $flags = $flags -band (-bnot 0x30) # remove Flags.PathNotCanonical|Flags.QueryNotCanonical (private enum)
    $flagsFieldInfo.SetValue($newUri, $flags)

    $newUri
}

用法:

$uri = CreateUriWithoutIncorrectSlashEncoding "https://server.com/api/v3/projects/foo%2Fbar"