I’ve been spending time crafting API calls in PowerShell and learning some hard lessons about error handling. Especially as I publish code that others try in their own environments.
One such lesson is around response status values from Invoke-WebRequest, which is a handy cmdlet released with PowerShell version 3. At first, I assumed the cmdlet would return any response status – such as 403 Forbidden or 405 Method Not Allowed – when invoked. Thus, I typically make a call in which the entire response is stored in a simple, throw-away variable such as $r.
Digging In Deeper
Here’s an example:
$r = Invoke-WebRequest -Uri "$uri/api/4.0/edges" -Body $body -Method:Post -Headers $head -ContentType "application/xml" -TimeoutSec 180 -ErrorAction:Stop
I have discovered that this is not the case. Failed requests result in an exception that never end up in the $r variable. Thus, putting logic against $r to determine the status code becomes somewhat pointless unless the reply is successful – such as 200 OK or 201 Created.
To combat this, I’ve started using Try and Catch statements in my API calls, such as the line below found in my NSX script.
try {$r = Invoke-WebRequest -Uri "$uri/api/4.0/edges" -Body $body -Method:Post -Headers $head -ContentType "application/xml" -TimeoutSec 180 -ErrorAction:Stop} catch {Failure}
In the event of an exception, the code will run the Failure function, which I’ve configured to collect a bunch of data on the failure, help with troubleshooting, and ultimately break (stop) the script execution. I had to really comb through Stack Overflow to find an elegant way to parse the response body, which are the guts filled with details on why things failed. Sort of a sad panda, here. 🙁
function Failure { $global:helpme = $body $global:helpmoref = $moref $global:result = $_.Exception.Response.GetResponseStream() $global:reader = New-Object System.IO.StreamReader($global:result) $global:responseBody = $global:reader.ReadToEnd(); Write-Host -BackgroundColor:Black -ForegroundColor:Red "Status: A system exception was caught." Write-Host -BackgroundColor:Black -ForegroundColor:Red $global:responsebody Write-Host -BackgroundColor:Black -ForegroundColor:Red "The request body has been saved to `$global:helpme" break }
It would be much cleaner if the cmdlet would simply return the response header, status, and body without throwing an exception, and then allow me to (easily) decide what to do with the reply. If you’re in a similar boat, this may help.
Next Steps
Please accept a crisp high five for reaching this point in the post!
If you’d like to learn more about Continuous Integration, or other modern technology approaches, head over to the Guided Learning page.