I have an existing PowerShell module that runs against Windows PowerShell 5.1. It depends heavily on the Invoke-WebRequest
and Invoke-RestMethod
cmdlets which have some fairly significant changes between 5.1 and PowerShell Core 6.
I've already managed to make most of the code work between editions (Desktop/Core), but the last thing I'm having trouble with is handling the exceptions thrown by HTTP failure responses. The existing code looks more or less like this.
try {
$response = Invoke-WebRequest @myparams -EA Stop
# do more stuff with the response
} catch [System.Net.WebException] {
# parse the JSON response body for error details
}
I don't necessarily want to trap any exceptions other than the ones generated by failing HTTP response codes from the server. The exception type returned on Core edition is different than Desktop edition and requires separate code paths to parse the response. So initially I tried this:
try {
$response = Invoke-WebRequest @myparams -EA Stop
# do more stuff with the response
} catch [System.Net.WebException] {
# Desktop: parse the JSON response body for error details
} catch [Microsoft.PowerShell.Commands.HttpResponseException] {
# Core: parse the JSON response body for error details
}
This works fine when running in Core. But when running in Desktop, I get a Unable to find type [Microsoft.PowerShell.Commands.HttpResponseException]
error.
What's the best way to work around this? Do I need to just catch all exceptions, string match on the type, and re-throw what I don't want? Is there a more elegant way I'm missing that doesn't involve releasing separate versions of the module for Desktop and Core versions of PowerShell?
I might recommend that you make thin wrapper around the cmdlets that does exception handling, and maybe you create a consistent exception class of your own and each version throws the same one.
At run time, you determine your version/edition, and set an alias to which one you want to use.
Example:
function Invoke-PS51WebRequest {
[CmdletBinding()]
param(...)
try {
Invoke-WebRequest @PSBoundParameters -EA Stop
# return the response
} catch [System.Net.WebException] {
# create a new exception
throw [My.Custom.Exception]$_
}
}
function Invoke-PS6XWebRequest {
[CmdletBinding()]
param(...)
try {
Invoke-WebRequest @PSBoundParameters -EA Stop
# return the response
} catch [System.Net.WebException] {
# Desktop
throw [My.Custom.Exception]$_
} catch [Microsoft.PowerShell.Commands.HttpResponseException] {
# Core
throw [My.Custom.Exception]$_
}
}
switch ($PSVersionTable)
{
{ $_.Is51 } { Set-Alias -Name Invoke-WebRequest -Value Invoke-PS51WebRequest -Force }
{ $_.Is6X } { Set-Alias -Name Invoke-WebRequest -Value Invoke-PS6XWebRequest -Force }
}
try {
Invoke-WebRequest @myParams -ErrorAction Stop
} catch [My.Custom.Exception] {
# do something
}
This needs a lot of work (parsing the exceptions properly, maybe making more than 2 of these variations, determining which platform you're on for real as $PSversionTable.Is51
and .Is6X
aren't real, creating your own exception class, creating a proper instance of it instead of casting $_
to it, etc.).
I also demonstrated overriding the actual name Invoke-WebRequest
, but I recommend using your own name Invoke-MyCustomWebRequest
and using that throughout your code instead. It will keep things more manageable.