【问题标题】:Powershell New-WebBinding Pipeline Issue with Name Property名称属性的 Powershell New-WebBinding 管道问题
【发布时间】:2016-02-15 21:42:04
【问题描述】:

在对象中进行管道传输时,我遇到了 New-WebBinding 的问题。我有一个对象,它定义了 5 个属性:名称、协议、端口、IPAddress 和 HostHeader(New-WebBinding cmdlet 支持所有 5 个属性作为 Accept Pipeline 输入: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
}

【问题讨论】:

  • 期待看到这个问题的答案。我还尝试了PSObjectPSCustomObject 的变体,但没有成功。好问题。
  • 目前 Microsoft Connect 似乎没有为任何版本的 Powershell 接收任何错误报告。所以我会留下一个最有可能的错误,唯一的解决方法是在 foreach 中迭代你的对象。我还没有第5版,不知道最新版是否解决了。
  • 这在 v5 中的行为相同。我确实反汇编了New-WebBinding cmdlet 所在的Microsoft.IIs.PowerShell.Provider 程序集,并且我们感兴趣的属性正确地归因于[Parameter(ValueFromPipelineByPropertyName = true)]
  • 这绝对是错误。一种解决方法是将当前位置更改为某个站点 (cd IIS:\Sites\SomeSite),这并不重要。

标签: powershell powershell-3.0 powershell-4.0 powershell-5.0


【解决方案1】:

PetSerAl 在上面的评论中提出了正确的想法:

一种解决方法是将当前位置更改为某个站点 (cd IIS:\Sites\SomeSite),这并不重要

这确实有效,但为什么它在正常的文件系统提示符下不起作用?

为了了解New-WebBinding 的行为方式为何,我将包含此和其他WebAdministration cmdlet 的Microsoft.IIS.PowerShell.Provider 程序集加载到dotPeek 中。程序集位于 GAC 中,因此您告诉 dotPeek“从 GAC 打开”。

加载后,我们感兴趣的类称为NewWebBindingCommand

粗略检查后,我们可以看到所有参数属性都使用[Parameter(ValueFromPipelineByPropertyName = true)] 属性进行修饰,因此这是一个好的开始,使用匹配属性名称的对象数组应该可以工作:

NewWebBindingCommand 最终继承自 System.Management.Automation.Cmdlet,在这种情况下覆盖了 BeginProcessing 方法。如果被覆盖,BeginProcessing 将由 PowerShell 调用并且“为 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.siteNameName 属性的私有成员值,该属性将绑定到-Name 参数。当我们到达上面的if(...) 语句时,`this.siteName 还没有绑定(它是空的),因此落入:

this.siteName = this.GetSiteName("Name");

GetSiteName() 的调用调用了 cmdlet 的直接基类 HelperCommand,它提供了许多对许多不同的 WebAdministration cmdlet 有用的“帮助”方法。

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 的基类帮助程序的代码方法GetSiteName().

这是在 VS2015 中插入 GetSiteName 时的屏幕截图,当附加到绑定到 New-Kevulator 的 PowerShell 会话管道时:

顶部的箭头表示由siteName 支持的Name 属性尚未绑定并且仍然是null(如上所述,这会导致GetSiteName 被执行)。我们也刚刚越过了这一行的断点:

PathInfo pathInfo = 
        this.SessionState.PSVariable.Get("PWD").Value as PathInfo;

...它决定了我们所处的路径提供者的类型。在这种情况下,我们在一个普通的文件系统C:\&gt; 提示符下,所以提供者名称将是FileSystem。我用第二个箭头突出显示了这一点。

如果我们Import-Module WebAdministrationCD IIS: 然后重新运行并再次中断,您可以看到路径提供程序已更改为WebAdministration,它负责处理IIS:&gt; 内外的生命:

如果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

【讨论】:

  • 优秀的解构和解释。感谢您的努力。
  • @Paolis 不客气,不抓挠真是太好了 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多