I have an existing REST API that accept x-www-form-urlencoded. The API need parameter apikey, and tested successfully in Postman as shown below.
However I need to invoke this API using Powershell.Below is my code :
$params = #{"apikey"="abcd1234"}
Invoke-WebRequest -Uri http://localhost:3030/api/v1/usergroupsync -Method POST -Body $params
#also tried below, no avail.
Invoke-RestMethod -Uri http://localhost:3030/api/v1/usergroupsync -Method POST -Body $params
However I encountered this error :
Invoke-WebRequest : The underlying connection was closed: An unexpected error occured on a receive At line:14 char:1
+ Invoke-WebRequest -Uri http://localhost:3030/api/v1/usergroupsync -Method POST -...
+==============================================================================
+ CategoryInfo : InvalidOperations: (System.Net.HttpWebRequest:HTTTpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebcmdletWebResponseException,Microsoft.Powershell.Commands.InvokeWebRequest
If I remove -Body, there is no error, and Response was as expected "API Key is not valid" which means my REST API validate correctly.
So I suspect the reason if my issue is on the body? Any idea on how to solve this issue?
PS Version is 4.0.0
PS C:\> $PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
4 0 -1 -1
You should use the -Header switch to pass your parameters. Though Invoke-WebRequest support header, I recommend using Invoke-RestMethod as it also return the Headers.
Try something like,
Invoke-RestMethod -Method Post -Uri http://localhost:3030/api/v1/usergroupsync -Body (ConvertTo-Json $body) -Header #{"apikey"=$apiKey}
Check this and this for more information
Related
I am using Invoke-WebRequest in Powershell and whenever my request is determined to be invalid by the targeted API endpoint, it obviously denies the request and sends back an HTTP error code like (400) Bad Request but it also includes the reason for the error (provided by the API vendor) but that isn't included in the logs inside of PowerShell.
I confirmed the detailed error is sent back because I see it in PostMan and the vendor confirmed the same. Powershell just doesn't want to show it. Here is an example of my code and the response it is generating.
Invoke-WebRequest -Credential $cred -Uri $url -Method POST -Body $json -ContentType 'application/json'
Invoke-WebRequest : The remote server returned an error: (400) Bad Request.
At \\*****\******$\Appsense\Desktop\Untitled2.ps1:42 char:1
+ Invoke-WebRequest -Credential $cred -Uri $url -Method POST -Body $jso ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest)
[Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.
InvokeWebRequestCommand
How do I capture that more detailed error message?
Two parts to this. First you need to have it throw a terminating error with -ErrorAction Stop. This allows us to then use a try/catch block to catch the exception. With the Exception we can then get the detailed response stored in the Exception Status Description. This is fine for most requests.
To get the body of the message requires a few more steps. Since we get a WebResponse object, there is no "Nice" Message parameter for us. So we have to use a StreamReader to Stream the content ourselves:
try
{
$Response = Invoke-WebRequest -Credential $cred -Uri $url -Method POST -Body $json -ContentType 'application/json' -ErrorAction Stop
# This will only execute if the Invoke-WebRequest is successful.
$StatusCode = $Response.StatusCode
}
catch
{
#Excepion - Display error codes
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
#Get body of me
$streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
$ErrResp = $streamReader.ReadToEnd() | ConvertFrom-Json
$streamReader.Close()
Write-Host $ErrResp
}
I'm attempting to use a REST API where I can add a list of items via a POST. However, I cannot seem to get the format of the body correct. The documentation says it's looking for a parameter called "data" that's type is body and the data type is an array. The sample provided for data, shows ["String","String","String"] I've asked for help on the vendors forums, but few users seem to use PowerShell.
I receive the following error:
"Invoke-RestMethod : {"message":"Request body must be populated for body parameter \"data\"","details":{},"description":"","code":10,"http_response":{"message":"The request was well-formed but was unable to be followed due to semantic errors","code":422}}"
I've tried many different formats for the body, but none seem to take. Here's an example of what I've been attempting:
$apiKey = "XXXXXXXXXXXXXXXXXXXXXX"
$url = "https://X.X.com"
$URI = "https://X.X.com/api/reference_data/sets/bulk_load/APITest"
Invoke-RestMethod -Method Post -Uri $URI -Body (convertto-json $body) -Header #{"SEC"= $apiKey }
$body = #{"10.10.1.5","10.10.1.5","50.50.50.50","123.45.6.7"}
I've also tried something like:
$body = #{"data"="body";"value"="10.10.1.5","50.50.50.50","123.45.6.7"} | convertto-json
But then I get this error:
Invoke-RestMethod : {"message":"beginObject() Expecting JSON Array, not a JSON Start of an Object","details":{},"description":"An error occurred parsing the JSON formatted message
body","code":1001,"http_response":{"message":"Invalid syntax for this request was provided","code":400}}
At Z:\Tools\Scripts\PowerShell\RefSetPostExample.ps1:24 char:1
+ Invoke-RestMethod -Method Post -Uri $URI -Body $body -contenttype "ap ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Thanks in advance for any advice on this.
Thanks to #4c74356b41 's practical advice I found:
http://wahlnetwork.com/2016/02/18/using-powershell-arrays-and-hashtables-with-json-formatting/
The answer was to do this:
$body = #("10.10.50.50","10.50.1.5")| convertto-json
Thank you!
I'm working with an API via PowerShell that returns human-readable errors as a json object in the response body when an error occurs. However, when I attempt to find that json body in an exception, I can see the error, the underlying System.Net.WebException and the further underlying System.Net.HttpWebResponse, but nowhere can I find the actual body they're referring to. Is this something that is accessible?
For example, here is a valid API call that would work:
Invoke-RestMethod -Method Get -Headers #{Authorization="Token token=$YourTokenHere";"Content-type"="application/json"} -Uri "https://mydomain.pagerduty.com/api/v1/users/ABCDEF" -Body #{offset=0;limit=100}
If you then change the user ID at the end of the URI, it fails and you get this error:
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-RestMethod -Method Get -Headers #{Authorization="Token token=b ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I can dive down in to the error to see the underlying error and the response if I do the following and convert to json for easy viewing of subproperties:
$Error[0].Exception.Response | ConvertTo-Json
But no matter how I comb through these errors, I can't seem to find the json body. Where might it be, or how can I capture it? I seem to have the same result if I do a try/catch.
You could read the responsestream so you can get the body of the response. Ex:
try {
Invoke-RestMethod -Method Get -Headers #{Authorization="Token token=$YourTokenHere";"Content-type"="application/json"} -Uri "https://mydomain.pagerduty.com/api/v1/users/ABCDEF" -Body #{offset=0;limit=100}
} catch {
$stream = New-Object System.IO.StreamReader $_.Exception.Response.GetResponseStream()
$json = $stream.ReadToEnd()
$stream.Dispose()
$json
}
Output:
{"error":{"message":"Account Not Found","code":2007}}
I wrote some code which tries to Get a value from one Rest API and post it to another.
I have the code saves in a .ps1 file. If I edit it and run it (or just copy and paste it into an empty PowerShell terminal) it does what I expect. However when I try to run the same .ps1 file directly I get an error on the 2nd Invoke-RestMethod.
Don't understand why I'm getting a different result and the error message not giving me many clues.
What am I doing wrong?
The code I am using is (with modified API key):
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($APIkey+":"))
$headers = #{"Content-Type" = "application/json"; "AUTHORIZATION" = "Basic $encoded"}
$APIkey = "123456789"
$metricId = "123"
$r = Invoke-RestMethod -Uri https://coinbase.com/api/v1/currencies/exchange_rates
$metric = [PSCustomObject]#{
value = [Math]::Round($r.btc_to_eur, 2)
}
$baseuri = "https://api.numerousapp.com/v1/metrics/$metricId/events"
$data = ConvertTo-Json $metric
Invoke-RestMethod -Uri $baseuri -Body $data -Headers $headers -Method Post
And the error message I get when running the .ps1 file is:
Invoke-RestMethod : :
At C:\NumerousBitcoinEur.ps1:13 char:1
+ Invoke-RestMethod -Uri $baseuri -Body $data -Headers $headers -Method Post
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I'm using PowerShell 4.0
$APIkey is being set after it is being used, which must be wrong. It probably works in the console because $APIkey happens to already be set.
If you like (I think it's a good idea), you can add the following to the top of your scripts to catch errors like this one.
Set-StrictMode -Version Latest
Since I upgraded to Windows 8 a lot of my PowerShell scripts relying on launching an invisible IE won’t quite work anymore, so I tried switching to the Invoke-WebRequest command. I did a lot of googling but still can’t get my script to work.
This is what it should do:
load up a website with a simple form (username, password, submit-button),
enter the credentials
and submit them.
The Microsoft tech-net examples were not very helpful for me, that is what I pieced together:
$myUrl = "http://some.url"
$response = Invoke-WebRequest -Uri $myUrl -Method Default -SessionVariable $rb
$form = $response.Forms[0]
$form.Fields["user"] = "username"
$form.Fields["password"] = "password"
$response = Invoke-WebRequest -Uri $form.Action -WebSession $rb -Method POST
$response.StatusDescriptionOK
I receive two errors, the first one when trying to write into the user field:
Cannot index into a null array.
$form.Fields["user"] = "username"
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
The second one has to do with the $form.Action which I have no idea what it should read:
Invoke-WebRequest : Cannot validate argument on parameter 'Uri'. The argument is null or empty. Supply an argument that is not null or empty and then try the command again.
Again, I relied heavily on example #2 at Microsoft.
Try doing the post directly e.g.:
$formFields = #{username='john doe';password='123'}
Invoke-WebRequest -Uri $myUrl -Method Post -Body $formFields -ContentType "application/x-www-form-urlencoded"
To address your problem with the unsigned/untrusted certificate, add the line
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
before the Invoke-WebRequest statement
The example in the question works, but you have to use rb and not $rb in the first line:
$response = Invoke-WebRequest -Uri $myUrl -Method Default -SessionVariable rb
I also had to use ($myUrl + '/login') since this is my login address.
$response = Invoke-WebRequest -Uri ($myUrl + '/login') -Method Default -SessionVariable rb
And in the last line used ($myUrl + $form.Action):
$response = Invoke-WebRequest -Uri ($myUrl + $form.Action) -WebSession $rb -Method POST
If you're me and have been troubleshooting a bad Web Request, in my case a -Body that was becoming null at my API, then you will want to know about the gotcha that is about interleaving your line continuations with comments. This
$r = iwr -uri $url `
-method 'POST' `
-headers $headers `
# -contenttype 'application/x-www-form-urlencoded' ` # default
-Body $body
Notice the commented out line # -contenttype 'application/x-www-form-urlencoded' # default
Putting a comment truncates the remaining back-ticked line continuation. Therefore, in my case my web request ended up with a request having 0-byte payload.