名称为 属性 的 Powershell New-WebBinding 管道问题
Powershell New-WebBinding Pipeline Issue with Name Property
我在对象中使用管道时遇到 New-WebBinding 问题。我有一个定义 5 个属性的对象:名称、协议、端口、IP 地址和 HostHeader(所有 5 个在 New-WebBinding cmdlet 中都支持作为接受管道输入:ValueByPropertyName)。但是,当您传入此对象时,它仍会请求提交一个 Name:。如果你想重现这个问题,这里有一个快速测试功能。如果您在提示符下按回车键,它会成功处理对象,添加绑定。但是提示本身将其分解为非交互式脚本。
我已经用 PS v3 和 PS v4 测试过了。
我很确定我做的一切都是正确的,但我想确保没有我可能忽略的东西。现在我只是在 foreach 循环中遍历我的对象集合,它没有这个问题,但想看看这是否是我应该报告的错误。
function Test-WebBinding{
[CmdletBinding()]
Param()
$testBindingCol = @()
$testBinding1 = New-Object System.Object
$testBinding1 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
$testBinding1 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'https'
$testBinding1 | Add-Member -MemberType NoteProperty -Name Port -Value '4000'
$testBinding1 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
$testBinding1 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4000'
$testBindingCol += $testBinding1
$testBinding2 = New-Object System.Object
$testBinding2 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
$testBinding2 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'http'
$testBinding2 | Add-Member -MemberType NoteProperty -Name Port -Value '4001'
$testBinding2 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
$testBinding2 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4001'
$testBindingCol += $testBinding2
$testBindingCol | New-WebBinding
}
PetSerAl 的上述评论是正确的:
One workaround would be to change current location to some site (cd IIS:\Sites\SomeSite
), it does not really matter to which
这确实有效,但为什么在正常的文件系统提示下不起作用?
为了发现 New-WebBinding
为什么会这样,我将包含此命令和其他 WebAdministration
cmdlet 的 Microsoft.IIS.PowerShell.Provider
程序集加载到 dotPeek 中。程序集位于 GAC 中,因此您将 dotPeek 告诉 "Open from GAC"。
加载时,我们感兴趣的 class 称为 NewWebBindingCommand
。
粗略检查后,我们可以看到所有参数属性都用 [Parameter(ValueFromPipelineByPropertyName = true)]
属性修饰,所以这是一个好的开始,通过管道传递具有匹配 属性 名称的对象数组应该可以工作:
NewWebBindingCommand
最终继承自System.Management.Automation.Cmdlet
and in this instance is overriding the BeginProcessing
方法。如果被覆盖,PowerShell 会调用 BeginProcessing
并且 "Provides a one-time, preprocessing functionality for the cmdlet."
重要的是要了解 cmdlet 的 BeginProcessing
覆盖在处理任何管道馈送的命名参数并绑定到 cmdlet 的属性之前被调用(参见:Cmdlet Processing Lifecycle (MSDN))。
我们的 New-WebBinding
cmdlet 的 BeginProcessing
实现如下所示:
protected override void BeginProcessing()
{
base.BeginProcessing();
if (!string.IsNullOrEmpty(this.siteName))
return;
this.siteName = this.GetSiteName("Name");
}
this.siteName
是 Name
属性 的私有成员值,它将绑定到 -Name
参数。当我们到达上面的 if(...)
语句时,`this.siteName 尚未绑定(它为空),因此落入:
this.siteName = this.GetSiteName("Name");
对 GetSiteName()
的调用会调用 cmdlet 的直接基础 class HelperCommand
,它提供了许多 "helper" 方法,这些方法对许多不同的 WebAdministration
cmdlets.
HelperCommand.GetSiteName(string prompt)
看起来像这样:
protected string GetSiteName(string prompt)
{
PathInfo pathInfo = this.SessionState.PSVariable.Get("PWD").Value as PathInfo;
if (pathInfo != null && pathInfo.Provider.Name.Equals("WebAdministration", StringComparison.OrdinalIgnoreCase))
{
string[] strArray = pathInfo.Path.Split('\');
if (strArray.Length == 3 && strArray[1].Equals("sites", StringComparison.OrdinalIgnoreCase))
return strArray[2];
}
if (!string.IsNullOrEmpty(prompt))
return this.PromptForParameter<string>(prompt);
return (string) null;
}
为了了解这个问题,我创建了自己的 PowerShell cmdlet(称为 Kevulator
,抱歉)并引入了 New-WebBinding
cmdlet 的 BeginProcessing()
代码和来自New-WebBinding
的基础 class 辅助方法 GetSiteName()
.
这是在绑定到 New-Kevulator
:
的 PowerShell 会话管道中附加到 PowerShell 会话管道时在 VS2015 中突破 GetSiteName
的屏幕截图
顶部的箭头表明 siteName
支持的 Name
属性 尚未绑定,仍然是 null
(如上所述导致 GetSiteName
待执行)。我们也刚刚跨过了这一行的断点:
PathInfo pathInfo =
this.SessionState.PSVariable.Get("PWD").Value as PathInfo;
...这决定了我们所处的路径提供者类型。在这种情况下,我们处于普通文件系统 C:\>
提示符下,因此提供程序名称将为 FileSystem
。我用第二个箭头突出显示了这一点。
如果我们Import-Module WebAdministration
和CD IIS:
然后重新运行再break你可以看到path provider变成了WebAdministration
负责处理生命IIS:>
内及以后:
如果 pathInfo
名称不等于 WebAdministration
,即我们不在 IIS:
提示符下,则代码失败并提示输入 Name
命令行参数,就像您所经历的那样。
如果 pathInfo
值 是 WebAdministration
那么将发生以下两种情况之一:
如果路径是 IIS:
或 IIS:\Sites
,代码将失败并发出 Name
参数的提示。
如果路径是 IIS:\Sites\SomeSiteName
则返回 SomeSiteName
并有效地成为 -Name
参数值,我们从 GetSiteName()
函数中退出返回到 BeginProcessing
.
最后一个行为很有用,因为如果您当前的路径是 IIS:\Sites\MySite
,那么您可以通过管道输入该站点的绑定数组,但无需指定 Name
参数。或者,如果您确实在对象的管道数组中提供了 Name
属性,那么它们将覆盖从 IIS:\Sites\MySite
上下文中获取的默认站点名称。
所以,回到我们的代码,一旦 BeginProcessing
有 运行 它的过程,我们的管道命名参数现在实际上被绑定,然后 ProcessRecord
方法被调用,这是New-WebBinding
必须执行的肉类和土豆工作。
TLDR:
New-WebBinding
不会绑定管道参数,除非您将当前工作的 目录 更改为 IIS 网站,例如 cd iis:\Sites\MySite
.
我在对象中使用管道时遇到 New-WebBinding 问题。我有一个定义 5 个属性的对象:名称、协议、端口、IP 地址和 HostHeader(所有 5 个在 New-WebBinding cmdlet 中都支持作为接受管道输入:ValueByPropertyName)。但是,当您传入此对象时,它仍会请求提交一个 Name:。如果你想重现这个问题,这里有一个快速测试功能。如果您在提示符下按回车键,它会成功处理对象,添加绑定。但是提示本身将其分解为非交互式脚本。
我已经用 PS v3 和 PS v4 测试过了。
我很确定我做的一切都是正确的,但我想确保没有我可能忽略的东西。现在我只是在 foreach 循环中遍历我的对象集合,它没有这个问题,但想看看这是否是我应该报告的错误。
function Test-WebBinding{
[CmdletBinding()]
Param()
$testBindingCol = @()
$testBinding1 = New-Object System.Object
$testBinding1 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
$testBinding1 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'https'
$testBinding1 | Add-Member -MemberType NoteProperty -Name Port -Value '4000'
$testBinding1 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
$testBinding1 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4000'
$testBindingCol += $testBinding1
$testBinding2 = New-Object System.Object
$testBinding2 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
$testBinding2 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'http'
$testBinding2 | Add-Member -MemberType NoteProperty -Name Port -Value '4001'
$testBinding2 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
$testBinding2 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4001'
$testBindingCol += $testBinding2
$testBindingCol | New-WebBinding
}
PetSerAl 的上述评论是正确的:
One workaround would be to change current location to some site (
cd IIS:\Sites\SomeSite
), it does not really matter to which
这确实有效,但为什么在正常的文件系统提示下不起作用?
为了发现 New-WebBinding
为什么会这样,我将包含此命令和其他 WebAdministration
cmdlet 的 Microsoft.IIS.PowerShell.Provider
程序集加载到 dotPeek 中。程序集位于 GAC 中,因此您将 dotPeek 告诉 "Open from GAC"。
加载时,我们感兴趣的 class 称为 NewWebBindingCommand
。
粗略检查后,我们可以看到所有参数属性都用 [Parameter(ValueFromPipelineByPropertyName = true)]
属性修饰,所以这是一个好的开始,通过管道传递具有匹配 属性 名称的对象数组应该可以工作:
NewWebBindingCommand
最终继承自System.Management.Automation.Cmdlet
and in this instance is overriding the BeginProcessing
方法。如果被覆盖,PowerShell 会调用 BeginProcessing
并且 "Provides a one-time, preprocessing functionality for the cmdlet."
重要的是要了解 cmdlet 的 BeginProcessing
覆盖在处理任何管道馈送的命名参数并绑定到 cmdlet 的属性之前被调用(参见:Cmdlet Processing Lifecycle (MSDN))。
我们的 New-WebBinding
cmdlet 的 BeginProcessing
实现如下所示:
protected override void BeginProcessing()
{
base.BeginProcessing();
if (!string.IsNullOrEmpty(this.siteName))
return;
this.siteName = this.GetSiteName("Name");
}
this.siteName
是 Name
属性 的私有成员值,它将绑定到 -Name
参数。当我们到达上面的 if(...)
语句时,`this.siteName 尚未绑定(它为空),因此落入:
this.siteName = this.GetSiteName("Name");
对 GetSiteName()
的调用会调用 cmdlet 的直接基础 class HelperCommand
,它提供了许多 "helper" 方法,这些方法对许多不同的 WebAdministration
cmdlets.
HelperCommand.GetSiteName(string prompt)
看起来像这样:
protected string GetSiteName(string prompt)
{
PathInfo pathInfo = this.SessionState.PSVariable.Get("PWD").Value as PathInfo;
if (pathInfo != null && pathInfo.Provider.Name.Equals("WebAdministration", StringComparison.OrdinalIgnoreCase))
{
string[] strArray = pathInfo.Path.Split('\');
if (strArray.Length == 3 && strArray[1].Equals("sites", StringComparison.OrdinalIgnoreCase))
return strArray[2];
}
if (!string.IsNullOrEmpty(prompt))
return this.PromptForParameter<string>(prompt);
return (string) null;
}
为了了解这个问题,我创建了自己的 PowerShell cmdlet(称为 Kevulator
,抱歉)并引入了 New-WebBinding
cmdlet 的 BeginProcessing()
代码和来自New-WebBinding
的基础 class 辅助方法 GetSiteName()
.
这是在绑定到 New-Kevulator
:
GetSiteName
的屏幕截图
顶部的箭头表明 siteName
支持的 Name
属性 尚未绑定,仍然是 null
(如上所述导致 GetSiteName
待执行)。我们也刚刚跨过了这一行的断点:
PathInfo pathInfo =
this.SessionState.PSVariable.Get("PWD").Value as PathInfo;
...这决定了我们所处的路径提供者类型。在这种情况下,我们处于普通文件系统 C:\>
提示符下,因此提供程序名称将为 FileSystem
。我用第二个箭头突出显示了这一点。
如果我们Import-Module WebAdministration
和CD IIS:
然后重新运行再break你可以看到path provider变成了WebAdministration
负责处理生命IIS:>
内及以后:
如果 pathInfo
名称不等于 WebAdministration
,即我们不在 IIS:
提示符下,则代码失败并提示输入 Name
命令行参数,就像您所经历的那样。
如果 pathInfo
值 是 WebAdministration
那么将发生以下两种情况之一:
如果路径是 IIS:
或 IIS:\Sites
,代码将失败并发出 Name
参数的提示。
如果路径是 IIS:\Sites\SomeSiteName
则返回 SomeSiteName
并有效地成为 -Name
参数值,我们从 GetSiteName()
函数中退出返回到 BeginProcessing
.
最后一个行为很有用,因为如果您当前的路径是 IIS:\Sites\MySite
,那么您可以通过管道输入该站点的绑定数组,但无需指定 Name
参数。或者,如果您确实在对象的管道数组中提供了 Name
属性,那么它们将覆盖从 IIS:\Sites\MySite
上下文中获取的默认站点名称。
所以,回到我们的代码,一旦 BeginProcessing
有 运行 它的过程,我们的管道命名参数现在实际上被绑定,然后 ProcessRecord
方法被调用,这是New-WebBinding
必须执行的肉类和土豆工作。
TLDR:
New-WebBinding
不会绑定管道参数,除非您将当前工作的 目录 更改为 IIS 网站,例如 cd iis:\Sites\MySite
.