在Powershell中实现装饰器模式

时间:2018-10-20 13:32:17

标签: powershell design-patterns parameter-passing decorator parameter-splatting

我有我的自定义函数 f ,该函数运行一些东西,然后调用预定义的函数 Invoke-WebRequest

我想让 f 接受 Invoke-WebRequest 的所有参数,然后将这些参数传递给 Invoke-WebRequest 。 / p>

 f --UseBasicParsing -Uri https://google.com -UseBasicParsing  -Body @{'name'='user'} -ErrorOnAction Stop
 # Some processing is made
 # then, the following is executed
 Invoke-WebRequest -Uri https://google.com -UseBasicParsing -Body @{'name'='user'} -ErrorOnAction Stop

有没有一种快速的方法来实现这一目标? (而不是在 f 中声明所有可能的参数)

2 个答案:

答案 0 :(得分:3)

尽管它与不声明参数不同,但是您可以通过生成代理命令来生成声明:

[System.Management.Automation.ProxyCommand]::Create((Get-Command Invoke-WebRequest))

结果将如下所示:

[CmdletBinding(HelpUri='https://go.microsoft.com/fwlink/?LinkID=217035')]
param(
    [switch]
    ${UseBasicParsing},

    [Parameter(Mandatory=$true, Position=0)]
    [ValidateNotNullOrEmpty()]
    [uri]
    ${Uri},

    [Microsoft.PowerShell.Commands.WebRequestSession]
    ${WebSession},

    [Alias('SV')]
    [string]
    ${SessionVariable},

    [pscredential]
    [System.Management.Automation.CredentialAttribute()]
    ${Credential},

    [switch]
    ${UseDefaultCredentials},

    [ValidateNotNullOrEmpty()]
    [string]
    ${CertificateThumbprint},

    [ValidateNotNull()]
    [X509Certificate]
    ${Certificate},

    [string]
    ${UserAgent},

    [switch]
    ${DisableKeepAlive},

    [ValidateRange(0, 2147483647)]
    [int]
    ${TimeoutSec},

    [System.Collections.IDictionary]
    ${Headers},

    [ValidateRange(0, 2147483647)]
    [int]
    ${MaximumRedirection},

    [Microsoft.PowerShell.Commands.WebRequestMethod]
    ${Method},

    [uri]
    ${Proxy},

    [pscredential]
    [System.Management.Automation.CredentialAttribute()]
    ${ProxyCredential},

    [switch]
    ${ProxyUseDefaultCredentials},

    [Parameter(ValueFromPipeline=$true)]
    [System.Object]
    ${Body},

    [string]
    ${ContentType},

    [ValidateSet('chunked','compress','deflate','gzip','identity')]
    [string]
    ${TransferEncoding},

    [string]
    ${InFile},

    [string]
    ${OutFile},

    [switch]
    ${PassThru})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Invoke-WebRequest', [System.Management.Automation.CommandTypes]::Cmdlet)
        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
    try {
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName Microsoft.PowerShell.Utility\Invoke-WebRequest
.ForwardHelpCategory Cmdlet

#>

答案 1 :(得分:2)

如果您不需要预先验证参数,最简单的方法是将所有参数(反映在自动变量$Args中)通过splatting传递,即作为@Args

function f { Invoke-WebRequest @Args }

请注意此方法的局限性:

  • 您的函数不能为advanced (cmdlet-like) function,因为此类函数拒绝接受未绑定到声明的参数的参数。

  • 虽然可以在声明参数的同时仍使函数不高级(以便声明特定于f的参数,这些参数不应反映在$Args中),{{ 3}},即使没有显式的[CmdletBinding([...])]属性。

如果您确实需要提高功能,最好的选择是创建一个代理功能,如use of a [Parameter(...)] attribute implicitly makes your function an advanced one中所述,该代理功能通过自动将绑定到声明的参数的值传递给$PSBoundParameters变量。