PwSh.Fw.Web.psm1
<#
#> <# .SYNOPSIS Download a file from the Internet. .Description Download a file using System.Net.WebClient class. This function will not test of the web resource exist. .PARAMETER Url URL from where to fetch file .PARAMETER To Destination folder to write file .PARAMETER Filename Optional filename to rename downloaded file on-the-fly. .PARAMETER UseWebClient Instruct Download-File to use the .Net webclient object .PARAMETER UseInvokeWebRequest Instruct Download-File to use the slower but safer Invoke-WebRequest method #> function Download-File { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification="Download is a more intuitive verb for this function because Get- is too generic.")] [OutputType([Boolean])]Param( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][System.Uri]$Url, [string]$To = (Get-Location).providerpath, [string]$Filename = [system.uri]::UnEscapeDataString($URL.Segments[-1]), [switch]$UseWebClient, [switch]$UseInvokeWebRequest, [hashtable]$Headers, [switch]$PassThru ) Begin { # eenter($MyInvocation.MyCommand) # if (!$Filename) { $FileName = $URL.Segments[-1] } # if ([string]::IsNullOrEmpty($To)) { $To = Get-Location } # edevel("Url = $Url") # edevel("To = $To") # edevel("Filename = $Filename") # edevel("UseWebClient = $UseWebClient") # edevel("UseInvokeWebRequest = $UseInvokeWebRequest") if (-not (dirExist $To)) { Write-Error "Folder '$To' does not exist." return $false } # compute default download method if ($UseWebClient -eq $UseInvokeWebRequest) { $UseWebClient = $false $UseInvokeWebRequest = $true } } Process { $webrc = Get-UriWebRc -Url $Url.AbsoluteUri -AsInt Write-Devel "[$webrc] $URL" if ($webrc -eq 200) { # edebug "Downloading $Url to $To/$Filename" if ($UseWebClient) { # edevel "Download using WebClient .Net object." if (Test-IsUNCPath -Path $To) { Write-Warning "$To is a UNC Path. WebClient does not support UNC Path. Falling back to InvokeWebRequest method." $UseInvokeWebRequest = $true } $webClient = New-Object System.Net.WebClient if ($Headers) { $webClient.Headers = $Headers } $webClient.DownloadFile($Url, "$To/$Filename") $rc = $? } if ($UseInvokeWebRequest) { # edevel "Download using Invoke-WebRequest method." $null = Invoke-WebRequest -Uri $Url.AbsoluteUri -OutFile "$To/$Filename" -PassThru -UseBasicParsing -Headers $Headers $rc = $? } } else { $rc = $false } if ($Passthru) { return (Resolve-Path "$To/$Filename").Path } else { return $rc } } End { # eleave($MyInvocation.MyCommand) } } <# .SYNOPSIS Download a file from the Internet. .Description Download a file using Invoke-WebRequest method .PARAMETER Url URL from where to fetch file .PARAMETER To Destination folder to write file .PARAMETER Filename Optional filename to rename downloaded file on-the-fly. #> function Download-FileWithRC { [CmdletBinding()][OutputType([Int])]Param ( [System.Uri]$Url, [string]$To, [string]$Filename ) Begin { # eenter($MyInvocation.MyCommand) if (!$Filename) { $FileName = $URL.Segments[-1] } } Process { $outFile = $To + "/" + $Filename $webrc = Get-UriWebRc -Url $Url.AbsoluteUri -AsInt if ($webrc -eq 200) { $webRequest = Invoke-WebRequest -Uri $Url.AbsoluteUri -OutFile $outFile -PassThru return $webRequest.StatusCode } else { return $webrc } } End { # eleave($MyInvocation.MyCommand) } } <# .SYNOPSIS Get the HTTP response (or status) code from a web URI .DESCRIPTION Only get the HTTP response code, do not download any content .PARAMETER Url Full URL to test .PARAMETER AsText If true, output the status message text (e.g. OK) .PARAMETER AsInt If true, output the status message code (e.g. 200) .OUTPUTS Valid HTTP status code or status message. If hostname cannot be resolved, then an error message is returned accordingly (-AsText) or -1 (-AsInt) .EXAMPLE $httpCode = Get-UriWebRc -Url "https://www.google.com" -AsText Will output 'OK' (I hope so !) .EXAMPLE $httpCode = Get-UriWebRc -Url "http://www.google.com/nonexistentfile" -AsInt Will output 404 .NOTES General notes .LINK https://stackoverflow.com/questions/795751/can-i-get-detailed-exception-stacktrace-in-powershell https://www.reddit.com/r/PowerShell/comments/30r1e9/any_way_to_get_just_the_response_code_using/ #> function Get-UriWebRc { [CmdletBinding()]Param ( [System.Uri]$Url, [switch]$AsText, [switch]$AsInt ) Begin { # eenter($MyInvocation.MyCommand) if ($AsText -eq $false -and $AsInt -eq $false) { $AsInt = $true } } Process { # $request = [System.Net.WebRequest]::Create($Url.AbsoluteUri) $response = @{} $response.StatusCode = -1 $response.StatusMsg = $null try { # $request = invoke-webrequest -method head -uri $Url.AbsoluteUri -UseBasicParsing $request = invoke-webrequest -uri $Url.AbsoluteUri -UseBasicParsing # $response = $request.GetResponse() $response.StatusCode = $request.StatusCode $response.StatusMsg = $request.StatusDescription } catch { $response.StatusCode = $_.Exception.Response.StatusCode.value__ $response.StatusMsg = $_.Exception.Response.StatusCode } if ($null -eq $response) { if ($AsText) { $Global:Error[0].ToString() } if ($AsInt) { -1 } } if ($AsText) { $response.StatusMsg } # This is a System.Net.HttpStatusCode enum value (@see https://msdn.microsoft.com/fr-fr/library/system.net.httpstatuscode(v=vs.110).aspx ) if ($AsInt) { $response.StatusCode } # This is the numeric version. } End { # eleave($MyInvocation.MyCommand) } } <# .SYNOPSIS Test if a web resource exist .DESCRIPTION Wrapper of Get-UriWebRc to quickly know if a web resource is available or not. .PARAMETER URL The URL to test. Better use it to check for a file .EXAMPLE Test-WebFileExist -URL http://www.example.com/file.txt #> function Test-WebFileExist { [CmdletBinding()] [OutputType([Boolean])] Param ( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][System.Uri]$URL ) Begin { Write-EnterFunction } Process { $int = Get-UriWebRc -Url $URL -AsInt switch ($int) { 200 { $bool = $true } default { $bool = $false } } return $bool } End { Write-LeaveFunction } } <# .SYNOPSIS Convert an object to a query string. .DESCRIPTION Convert an object to a single string to be used with HTTP GET method. .PARAMETER InputObject Object to convert .PARAMETER variableName Main variable name to use .PARAMETER CurrentDepth Current depth into InputObject. This parameter is used internally for recursivity. Do not assign. .PARAMETER MaxDepth Maximum depth of object to convert into a string .PARAMETER CurrentPath Parameter used internally for recursivity. Do not assign. .EXAMPLE $q = ConvertTo-QueryString -InputObject @{"one" = 1, "two" = 2} -variableName "query" Invoke-RestMethod -Method Get -ContentType "application/json" -Uri $("https://www.example.com/search?" + $q) The example above will convert the hashtable into the query string : "query[one]=1&query[two]=2" and then invoke a REST method using this query. .NOTES General notes #> function ConvertTo-QueryString { [CmdletBinding( DefaultParameterSetName="OBJECT" )][OutputType([String])]Param ( [AllowNull()] [Parameter(ParameterSetName = 'OBJECT', Mandatory = $true, ValueFromPipeLine = $true)]$InputObject, [Parameter()][string]$variableName, [int16]$CurrentDepth = -1, [uint16]$MaxDepth = 16, [string]$CurrentPath ) Begin { # eenter($MyInvocation.MyCommand) $queryString = "" } Process { switch ($PSCmdlet.ParameterSetName) { 'OBJECT' { $object = $InputObject | ConvertTo-Json | ConvertFrom-Json break } default { $object = Get-Variable -Name $variableName -ValueOnly | ConvertTo-Json | ConvertFrom-Json break } } if ($null -eq $object) { return } if ($CurrentDepth -gt $MaxDepth) { return $queryString += $("&" + $CurrentPath + "=" + $object) } $CurrentDepth++ if ($CurrentDepth -eq 0) { $CurrentPath = $variableName } # edevel("CurrentDepth = " + $CurrentDepth + " / " + $MaxDepth) # edevel("CurrentPath = " + $CurrentPath) if ($object -is [array]) { # edevel("array / " + $object.GetType()) for ($index = 0; $index -lt $object.count; $index++) { $queryString += ConvertTo-QueryString -CurrentDepth $CurrentDepth -InputObject $object[$index] -CurrentPath $($CurrentPath + "[$index]") } } elseif ($object -is [hashtable]) { # edevel("object / " + $object.GetType()) # list properties of class System.Object $ObjectClassProperties = @( Get-ObjectProperties -obj $object ) # extract custom properties of $object, without properties of class of object $properties = $object.PsObject.Properties | Where-Object { $ObjectClassProperties -notcontains $_.Name } foreach($obj in $properties) { $queryString += ConvertTo-QueryString -CurrentDepth $CurrentDepth -InputObject $obj.Value -CurrentPath $($CurrentPath + "[" + $obj.Name + "]") } } else { # edevel("fallback / " + $object.GetType()) switch ($object.GetType()) { 'bool' { $queryString += $("&" + $CurrentPath + "=" + $object) break } 'int' { $queryString += $("&" + $CurrentPath + "=" + $object) break } 'long' { $queryString += $("&" + $CurrentPath + "=" + $object) break } 'String' { $queryString += $("&" + $CurrentPath + "=" + $object) break } 'PSCustomObject' { # edevel("PSCustomObject / " + $object.GetType()) $ObjectClassProperties = @( Get-ObjectProperties -obj $object ) $properties = $object.PsObject.Properties | Where-Object { $ObjectClassProperties -notcontains $_.Name } foreach($obj in $properties) { $queryString += ConvertTo-QueryString -CurrentDepth $CurrentDepth -InputObject $obj.Value -CurrentPath $($CurrentPath + "[" + $obj.Name + "]") } } default { break } } } # edevel("queryString = " + $queryString) $CurrentDepth-- if ($CurrentDepth -eq -1) { return $queryString.Trim("&") } else { return $queryString } } End { # eleave($MyInvocation.MyCommand) } } <# .SYNOPSIS Trim leading and trailing characters .DESCRIPTION Trim leading and trailing additional non-wanted characters like - slashes '/' .EXAMPLE Trim-Url -URL "https://localhost:8000/" .NOTES General notes #> function Trim-Url { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$URL ) Begin { # Write-EnterFunction } Process { return $URL.Trim('/') } End { # Write-LeaveFunction } } |