GeezerVison is showing github.com — Invoke-RestMethod should return the full error response from the remote endpoint · Issue #2193 · PowerShell/PowerShell
Invoke-RestMethod should return the full error response from the remote endpoint #2193Description
Steps to reproduce
Today, if you use Invoke-RestMethod and have any sort of malformed data in your request, Invoke-RestMethod eats the server response and instead returns a generic PowerShell error message, greatly increasing pain when troubleshooting.
As an example of desired behavior, it would be good if PowerShell surfaced the remote server response instead of just writing an error and dumping the response.
Expected behavior
It would be wonderful if Invoke-RestMethod wrote to an alternate stream or provided rich error data when encountering an error with an endpoint.
For example. note the error data I'm about to get when I used the very useful Failure function provided in this blog post, I'm able to see the full remote server response, which makes troubleshooting this an absolute breeze.
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
}
With this function loaded in memory, I'll run the below code with try/catch and call Failure on error.
$Splat = @{
Method = 'PUT'
Uri = 'https://api.us.onelogin.com/api/1/users/27697924/add_roles'
ContentType = "application/json"
Headers = @{authorization = "bearer:$token" }
Body = @{role_id_array = (143175)}
}
PS C:\git> Invoke-RestMethod @Splat
Status: A system exception was caught.
{"status":{"error":true,"code":400,"type":"bad request","message":"role_id_array should be -\u003e array of positive integers"}}
The request body has been saved to $global:helpme
Actual behavior
In today's PowerShell, all server response data from Invoke-RestMethod is eaten and a generic PowerShell error is presented. In scouring the results from $error[0] I was unable to find the actual server response.
$Splat = @{
Method = 'PUT'
Uri = 'https://api.us.onelogin.com/api/1/users/27697924/add_roles'
ContentType = "application/json"
Headers = @{authorization = "bearer:$token" }
Body = @{role_id_array = (143175)}
}
PS C:\git> Invoke-RestMethod @Splat
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-RestMethod @Splat
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Environment data
> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.14393.103
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.14393.103
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Reacted by Matt McNabb, Nicholas M. Getchell, Dennis Brunner, Warren Frame, Brian Scholer, Nick Bradford, Mikhail Plavskiy, Christopher Riddersäter, Marco Mangiante, Kev Maitland and 9 more Activitymattmcnabb commented on Sep 7, 2016 Excellent write-up Stephen! I can second Stephen's frustration with this issue! ngetchell commented on Sep 7, 2016 I've definitely been bitten by this. AWahlqvist commented on Sep 7, 2016 Same as the above, also using a helper function to get the error response stream. Hope the fix doesn't break those modules though, but even if it does it would be worth it not having to deal with it everytime.
Great write-up! added this to the This milestone has been deleted milestone on Nov 2, 2016modified the milestones: This milestone has been deleted, This milestone has been deleted on Dec 19, 2016vors commented on Feb 11, 2017 Essentially, if we just remove one line
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode(); |
we will get a pretty decent output.
For example:
Reading web response StatusCode : 400e stream... (Number of bytes read: 229) StatusDescription : BadRequest Content : Error in call to API function "team/get_info": Bad HTTP "Content-Type" header: "application/x-www-form-urlencoded". Expecting one of
"application/json", "application/json; charset=utf-8", "text/plain...
RawContent : HTTP/1.1 400 BadRequest
Server: nginx
Date: Sat, 11 Feb 2017 06:39:47 GMT
Connection: keep-alive
User-Agent:
Error in call to API function "tea...
Forms :
Headers : {[Server, System.String[]], [Date, System.String[]], [Connection, System.String[]], [User-Agent, System.String[]]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml :
RawContentLength : 229
vors commented on Feb 11, 2017 Btw, in the FullCLR version there is no call to it. SteveL-MSFT commented on Feb 13, 2017 Here's the experience I have coded up, feedback welcome:
PS /home/steve/repos/PowerShell> invoke-webrequest http://httpstat.us/418
invoke-webrequest : 418 I'm a teapot
At line:1 char:1
+ invoke-webrequest http://httpstat.us/418
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Method: GET, Re...rShell/6.0.
0
}:HttpRequestMessage) [Invoke-WebRequest], I'm a teapot
+ FullyQualifiedErrorId : 418,Microsoft.PowerShell.Commands.InvokeWebReque
stCommand
PS /home/steve/repos/PowerShell> $e.FullyQualifiedErrorId
418,Microsoft.PowerShell.Commands.InvokeWebRequestCommandReacted by Joey Aiello and Matt McNabb SteveL-MSFT commented on Feb 14, 2017 The content of the response is stuffed into the Message property of the ErrorDetail and the HTTP Status Code is in the front of the FullyQualifiedErrorId. Response headers are thrown away (not sure if there's any useful case when you receive an error from the server). vors commented on Feb 15, 2017
HTTP Status Code is in the front of the FullyQualifiedErrorId
Not sure that's a good idea. SteveL-MSFT commented on Feb 15, 2017 sergei (@vors), open to suggestions. Could prefix with HttpStatus, but then it's more parsing for scripts that want the actual code joeyaiello commented on Feb 16, 2017 @PowerShell/powershell-committee (subset of Hemant Mahawar (@HemantMahawar) Jason Shirk (@lzybkr) Kenneth Hansen (@khansen00) and myself) think that the most important piece is that you're able to get at the Response property of the exception as you can in FullCLR:
When running Invoke-WebRequest http://httpstat.us/418, the behavior currently differs between Core and Full:
FullCLR:
PS C:\Users\jaiello> $error[1].exception.response
IsMutuallyAuthenticated : False
Cookies : {ARRAffinity=ce89bc5d89e3eae7a38e90a2582b973f890225b3bddc47e95c77a1d7831a8a71}
Headers : {X-AspNetMvc-Version, Access-Control-Allow-Origin, Content-Length, Cache-Control...}
SupportsHeaders : True
ContentLength : 16
ContentEncoding :
ContentType : text/plain; charset=utf-8
CharacterSet : utf-8
Server : Microsoft-IIS/8.0
LastModified : 2/15/2017 4:16:36 PM
StatusCode : 418
StatusDescription : I'm a teapot
ProtocolVersion : 1.1
ResponseUri : http://httpstat.us/418
Method : GET
IsFromCache : False
CoreCLR:
PS C:\Program Files\PowerShell\6.0.0.15> $error[0].exception | fl -force *
Message : Response status code does not indicate success: 418 (I'm a teapot).
Data : {}
InnerException :
TargetSite : System.Net.Http.HttpResponseMessage EnsureSuccessStatusCode()
StackTrace : at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at Microsoft.PowerShell.Commands.WebRequestPSCmdlet.ProcessRecord()
HelpLink :
Source : System.Net.Http
HResult : -2146233088
Reacted by Steve Lee and Mark SteveL-MSFT commented on Feb 24, 2017 The experience won't be exactly the same as the coreClr types are different, but it's improved and similar:
PS /home/steve/repos/PowerShell> invoke-webrequest http://httpstat.us/418
invoke-webrequest : Response status code does not indicate success: 418 (I'm a teapot).
At line:1 char:1
+ invoke-webrequest http://httpstat.us/418
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Method: GET, Re...rShell/6.0.0
}:HttpRequestMessage) [Invoke-WebRequest], I'm a teapot
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCo
mmand
PS /home/steve/repos/PowerShell> $error[0].exception.response
Version : 1.1
Content : System.Net.Http.StreamContent
StatusCode : 418
ReasonPhrase : I'm a teapot
Headers : {[Cache-Control, System.String[]], [Server, System.String[]], [X-AspNetMvc-Version,
System.String[]], [X-AspNet-Version, System.String[]]...}
RequestMessage : Method: GET, RequestUri: 'http://httpstat.us/418', Version: 1.1, Content: <null>,
Headers:
{
User-Agent: Mozilla/5.0
User-Agent: (Windows NT; Linux 4.4.0-64-generic #85-Ubuntu SMP Mon Feb 20 11:50:30
UTC 2017; en-US)
User-Agent: WindowsPowerShell/6.0.0
}
IsSuccessStatusCode : FalseReacted by Nicholas M. Getchell and Matt McNabb abhinavsingh003 commented on Mar 14, 2019 Tried another work around to capture error from a REST API is anyone is trying to capture that:
function Failure {
$Message = $_.ErrorDetails.Message;
$json = $Message | ConvertFrom-Json #if error you are trying to capture is returned in json
Write-Host ("Message: "+ $Message)
}
With this function loaded in memory, put your try / catch around Invoke-RestMethod and call Failure on error. Hope this helps. Reacted by MeGoodGuy, ƘɌỈSƬƠƑ, Jayendran Arumugam, golfalot and Gregory Ott Reacted by MeGoodGuy and kmoninsis Reacted by MeGoodGuy MeGoodGuy commented on Dec 18, 2020 Thanks abhinavsingh003 !
` Catch {
if($_.ErrorDetails.Message) {
#WebResponseError
Write-Host $_.ErrorDetails.Message;
} else {
#UsualException
$_
}
}`
Reacted by zinwayz, shunkin and golfalot
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|