Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invoke-RestMethod should return the full error response from the remote endpoint #2193

Closed
1RedOne opened this issue Sep 6, 2016 · 12 comments

Comments

@1RedOne
Copy link
Contributor

@1RedOne 1RedOne commented Sep 6, 2016

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                                                                                                                                                                                                                           


@mattmcnabb

This comment has been minimized.

Copy link

@mattmcnabb mattmcnabb commented Sep 7, 2016

Excellent write-up Stephen! I can second Stephen's frustration with this issue!

@ngetchell

This comment has been minimized.

Copy link

@ngetchell ngetchell commented Sep 7, 2016

I've definitely been bitten by this.

@AWahlqvist

This comment has been minimized.

Copy link

@AWahlqvist AWahlqvist commented 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!

@SteveL-MSFT SteveL-MSFT added this to the 6.0.0 milestone Nov 2, 2016
@SteveL-MSFT SteveL-MSFT modified the milestones: 6.0.0-beta, 6.0.0 Dec 19, 2016
@joeyaiello joeyaiello mentioned this issue Jan 25, 2017
75 of 116 tasks complete
@SteveL-MSFT SteveL-MSFT self-assigned this Jan 31, 2017
@vors

This comment has been minimized.

Copy link
Collaborator

@vors vors commented Feb 11, 2017

Essentially, if we just remove one line

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

This comment has been minimized.

Copy link
Collaborator

@vors vors commented Feb 11, 2017

Btw, in the FullCLR version there is no call to it.

@SteveL-MSFT

This comment has been minimized.

Copy link
Member

@SteveL-MSFT SteveL-MSFT commented 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.InvokeWebRequestCommand
@SteveL-MSFT

This comment has been minimized.

Copy link
Member

@SteveL-MSFT SteveL-MSFT commented 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

This comment has been minimized.

Copy link
Collaborator

@vors vors commented Feb 15, 2017

HTTP Status Code is in the front of the FullyQualifiedErrorId

Not sure that's a good idea.

@SteveL-MSFT

This comment has been minimized.

Copy link
Member

@SteveL-MSFT SteveL-MSFT commented Feb 15, 2017

@vors, open to suggestions. Could prefix with HttpStatus, but then it's more parsing for scripts that want the actual code

@joeyaiello

This comment has been minimized.

Copy link
Member

@joeyaiello joeyaiello commented Feb 16, 2017

@PowerShell/powershell-committee (subset of @HemantMahawar @lzybkr @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
@SteveL-MSFT

This comment has been minimized.

Copy link
Member

@SteveL-MSFT SteveL-MSFT commented 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 : False
@abhinavsingh003

This comment has been minimized.

Copy link

@abhinavsingh003 abhinavsingh003 commented 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.