我有一个包含多个斜杠字符(/
)的URL作为文件名(不是URL)的一部分。但是当我发送http请求时,百分比编码的%2F
在请求分派之前被转换为/
,因此生成了错误的URL。
如何制作文字http请求,忽略PowerShell中的百分比编码值?
使用的实际网址(Chromium browser):
我尝试了Invoke-WebRequest
cmdlet:
Invoke-WebRequest -Uri $ChromeUrl -OutFile $FilePath -Verbose
VERBOSE: GET https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64/292817/chrome-win32.zip?generation=1409504089694000&alt=media with 0-byte payload1`
未找到错误。
还尝试了WebClient
DownloadFile
方法:
$wclient = New-Object System.Net.WebClient
$wclient.DownloadFile($ChromeUrl, $FilePath)
由于再次请求错误的网址而返回404。
briantist和Tanuj Mathur提供的基于反思的解决方法都很有效。后者:
$UrlFixSrc = @"
using System;
using System.Reflection;
public static class URLFix
{
public static void ForceCanonicalPathAndQuery(Uri uri)
{
string paq = uri.PathAndQuery;
FieldInfo flagsFieldInfo = typeof(Uri).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
ulong flags = (ulong) flagsFieldInfo.GetValue(uri);
flags &= ~((ulong) 0x30);
flagsFieldInfo.SetValue(uri, flags);
}
}
"@
Add-Type -TypeDefinition $UrlFixSrc-Language CSharp
[URLFix]::ForceCanonicalPathAndQuery([URI]$ChromeUrl)
Invoke-WebRequest -Uri $ChromeUrl -OutFile $FilePath -Verbose
VERBOSE: GET https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292640%2Fchrome-win32.zip?generation=1409351584147000&alt=media
更干净的解决方案(由Tanuj Mathur提供),但需要访问系统文件,是通过添加包含以下内容的配置文件%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe.config
:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<uri>
<schemeSettings>
<add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
<add name="https" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
</schemeSettings>
</uri>
</configuration>
必须在powerhsell_ise.exe.config
中进行相应的修改,才能在ISE中发挥作用。
我认为它是一个System.URI
类构造函数问题,在隐式转换时调用,它会转换转义值。尝试了一个重载的变体Uri ([String]uriString, [Boolean]dontEscape)
。但没有区别。有或没有dontEscape
参数的相同结果。
$uri = new-object System.Uri($ChromeUrl, $true)
$uri | Format-List OriginalString, AbsoluteUri
OriginalString : https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292817%2Fchrome-win32.zip?generation=1409504089694000&alt=media
AbsoluteUri : https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64/292817/chrome-win32.zip?generation=1409504089694000&alt=media
还尝试通过将百分号字符替换为百分比编码值%25
来欺骗URI解析器。但后来它完全忽略了一切。
Invoke-WebRequest -Uri $ChromeUrl.Replace('%', '%25') -OutFile $DownloadPath -Verbose
VERBOSE: GET https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%252F292817%252Fchrome-win32.zip?generation=1409504089694000&alt=media with 0-byte pa yload
我发现请求URL正确的唯一方法是通过Internet Explorer实例。
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Visible = $true
$ie.Silent = $false
$ie.Navigate2($ChromeUrl)
但后来我不知道如何自动化“保存为”#39;按钮单击并将其保存到所需的路径。此外,即使实施,我也不觉得这是一个很好的解决方案。当IE已经从系统运行或卸载时会发生什么?
答案 0 :(得分:5)
在过去的几个小时里,我一直在玩你的代码,这真是太过分了。在Powershell ISE中运行时,给定的代码及其变体都会通过,但在Powershell控制台上失败。 问题本身似乎是Microsoft Connect here中记录的问题。
有趣的是,根据用户Glenn Block's answer对相关问题,此错误已在.NET Framework 4.5中修复。
您可以通过运行命令$PSVersionTable
来检查Powershell正在使用的.NET框架的版本。只要CLRVersion
值的格式为4.0.30319.x,其中x&gt; 1700,那么你正在运行框架的v4.5。
我在我的机器上运行.NET Framework 4.5上的Powershell v4.0,这解释了为什么Powershell ISE显示正确的行为,但我无法弄清楚为什么Powershell控制台没有。我验证了两者加载的.NET程序集,它们看起来是一样的。
根据情况,我们有两种选择。 一种是使用反射并在.Net类上设置一个私有字段以防止这种行为(如this answer中所述)。 另一种方法是使用Microsoft Connect问题中列出的解决方法。这涉及以下步骤:
"C:\Windows\System32\WindowsPowerShell\v1.0\"
)。此文件夹中应包含文件powershell.exe
。powershell.exe.config
在文本编辑器中打开此文件,并将以下文本粘贴到其中:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<uri>
<schemeSettings>
<add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
<add name="https" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
</schemeSettings>
</uri>
</configuration>
保存此文件。关闭所有正在运行的Powershell实例。
答案 1 :(得分:2)
如果您要使用PowerShell,您还可以在纯PowerShell中执行解决方法1:
function UrlFix([Uri]$url) {
$url.PathAndQuery | Out-Null
$m_Flags = [Uri].GetField("m_Flags", $([Reflection.BindingFlags]::Instance -bor [Reflection.BindingFlags]::NonPublic))
[uint64]$flags = $m_Flags.GetValue($url)
$m_Flags.SetValue($url, $($flags -bxor 0x30))
}
UrlFix $ChromeUrl
Invoke-WebRequest -Uri $ChromeUrl -OutFile $FilePath -Verbose
答案 2 :(得分:1)
但这里真的很奇怪。我正在运行PowerShell 4.0。在控制台主机中运行时,我可以重现此问题。但是,如果我在ISE主机中运行完全相同的代码,它可以完美运行。
我不知道如何或为什么。我甚至远程连接到另一个不在我的网络上的系统,以确保我没有以某种方式改变我的系统上任何奇怪的东西。结果相同。底线:
$a = 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292817%2Fchrome-win32.zip?generation=1409504089694000&alt=media'
Invoke-WebRequest -Uri $a
这在ISE中有效,在控制台主机中不起作用。我甚至尝试用-UseBasicParsing
来确保它不是一个奇怪的DOM解析怪癖。
我在Simon Maurier对“How to make System.Uri not to unescape %2f (slash) in path?”的回答中使用了C#代码,并将其改编为在PowerShell中使用:
$uriFixerDef = @'
using System;
using System.Reflection;
public class UriFixer
{
private const int UnEscapeDotsAndSlashes = 0x2000000;
private const int SimpleUserSyntax = 0x20000;
public static void LeaveDotsAndSlashesEscaped(Uri uri)
{
if (uri == null)
throw new ArgumentNullException("uri");
FieldInfo fieldInfo = uri.GetType().GetField("m_Syntax", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null)
throw new MissingFieldException("'m_Syntax' field not found");
object uriParser = fieldInfo.GetValue(uri);
fieldInfo = typeof(UriParser).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null)
throw new MissingFieldException("'m_Flags' field not found");
object uriSyntaxFlags = fieldInfo.GetValue(uriParser);
// Clear the flag that we do not want
uriSyntaxFlags = (int)uriSyntaxFlags & ~UnEscapeDotsAndSlashes;
uriSyntaxFlags = (int)uriSyntaxFlags & ~SimpleUserSyntax;
fieldInfo.SetValue(uriParser, uriSyntaxFlags);
}
}
'@
Add-Type -TypeDefinition $uriFixerDef
$u = 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292817%2Fchrome-win32.zip?generation=1409504089694000&alt=media'
[UriFixer]::LeaveDotsAndSlashesEscaped($u)
Invoke-WebRequest -Uri $u
我首先在ISE中测试它然后发现ISE无论如何都在工作。所以我确实在一个干净的控制台主机环境中尝试了这个,在调用该方法之前,我得到了notfound
。在调用之后,工作。
正如在链接的答案中所说,这是一个丑陋的黑客,可能在未来的版本中破坏等。
我希望它有所帮助,这是一个有趣的问题。