I have done a bunch of searches, but nothing so far as helped me solve this. Any help is appreciated.
I am trying to convert a curl command to Invoke-WebRequest, but for some reason I am not getting a response back. I have tried:
$headers = #{
outh_a = "WEBREQ";
oauth_key = "key";
etc...
}
Invoke-WebRequest -Uri $url -Method POST -Body "{}" -Headers $headers -Verbose
The curl command looks like this:
.\curl.exe -x POST -d "{}" -H "oauth_a: WEBREQ" -H "oauth_key:key" -H "Content-Type: application/json" -H "oauth_sm:HMAC-SHA2" -H "oauth_t:timestamp"-H "oauth_s:key+signature=" "https://example.com/services/api/request?t=data&a=data2&n=404&i=0"
The command works perfectly and returns the data. I am using PowerShell as we want to parse through the data once received. Any help is greatly appreciated.
The curl command will fail if I submit it without -d "{}", so that part is required. The server I guess is expecting to receive a specific amount of data.
I am not sure what is going on to prevent a response. I have tried curl from the same machine I am doing the PowerShell script on and it works. I even used SoapUI to make the same call and it works there too.
Edit:
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[System.Net.WebRequest]::DefaultWebProxy.Credentials =
[System.Net.CredentialCache]::DefaultCredentials
[Net.ServicePointManager]::SecurityProtocol =
[Net.SecurityProtocolType]::Ssl3, [Net.SecurityProtocolType]::Tls,
[Net.SecurityProtocolType]::Tls11, [Net.SecurityProtocolType]::Tls12
$Domain = "https://example.com/services/api/request?t=oauth_auth_token&a=appid&n=404&i=0"
# OAUTH Authorization Request Header
$Header = #{
o_nonce = 'TEST'
o_con_key = 'key'
o_sig_meth = 'type'
o_timestamp = 'timestamp'
o_sig = 'signature'
o_ver = '1.0'
o_realm = 'http://example.com/services'
}
$Body = #{}
$API_Response = Invoke-RestMethod -Method POST -Headers $Header -Uri $Domain -Body $Body -Verbose -TimeoutSec 20
Update:
#MikeW I'd have to look at your PS code in more detail, could you post the entire API call minus the sensitive information (api key etc...)?
Also, on a side note, sometimes just passing the parameters on the URI works just as well. Have you tried passing the parameters through the URI? You can see the following example of an API call I'm making using just the URI:
Invoke-RestMethod -Uri ("$EndpointURL/Tickets/$TicketID/?username=$ServiceAccount&apiKey=$APIKey");
Related
I am using Powershell 7.3.1 and Invoke-RestMethod. I need to submit a form to a server but one of the fields needs to be typed as JSON or the request will fail. In cURL, this can be accomplished by appending ;type=application/json to the field but that doesn't seem to work with Invoke-RestMethod.
In cURL, submitting a form with types would look something like this:
curl --location --request POST 'http://localhost:8082/csv-as-input' \
--header 'Content-Type: multipart/form-data' \
--form 'file=#"C:/Users/test/task-b5cfe19-input.csv"' \
--form 'json="{\"name\": \"Test\",\"jobType\": \"BATCH_FILE\",\"runMode\": \"VALIDATE\",\"config\": {\"branchId\": \"12345\",\"trustedSource\": true}}";type=application/json'
In this case, the json form element is treated as a JSON string because of the ;type=application/json at the end.
I have tried to write the Powershell version using the new -Form parameter:
$CreateJobForm = #{
'file' = Get-Item -Path 'C:\Users\test\task-b5cfe19-input.csv'
'json' = '{"name": "Test","jobType": "BATCH_FILE","runMode": "VALIDATE","config": {"branchId": "12345","trustedSource": true}};type=application/json'
}
Invoke-RestMethod -Method Post -Uri http://localhost:8082/csv-as-input -Form $CreateJobForm
The cURL command works but the Powershell version fails because it is coming across as a text/plain type.
Is there a way to type the json field as JSON in Powershell?
You can use the -ContentType parameter to specify the content type of the request body as multipart/form-data, and then use the ConvertTo-Json cmdlet to convert the JSON object to a JSON string and include it in the form data.
$json = #{
'name' = 'Test'
'jobType' = 'BATCH_FILE'
'runMode' = 'VALIDATE'
'config' = #{
'branchId' = '12345'
'trustedSource' = $true
}
}
$CreateJobForm = #{
'file' = Get-Item -Path 'C:\Users\test\task-b5cfe19-input.csv'
'json' = (ConvertTo-Json -InputObject $json)
}
Invoke-RestMethod -Method Post -Uri http://localhost:8082/csv-as-input -ContentType 'multipart/form-data' -Form $CreateJobForm
This curl command works:
curl -v -X POST https://subdomain.playvox.com/api/v1/files/upload?context=quality -H "Content-Type: multipart/form-data" -u [username]:[password] -F file=#c:\path\to\file.wav
But I am unable to perform the same thing in PowerShell using the Invoke-RestMethod cmdlet. Here's my attempt:
$file_contents = [System.IO.File]::ReadAllBytes($($file_path))
Invoke-RestMethod -Uri "https://subdomains.playvox.com/api/v1/files/upload?context=quality" -Method Post -ContentType "multipart/form-data" -Headers #{ "Authorization" = "Basic $($playvox_base64_auth)" } -Body #{ file = $file_contents }
When run the API responds with invalid_param, "file" is required. However I confirmed the call to ReadAllBytes succeeds and gives back the raw file data. It seems like PowerShell is not sending the request body in the right format? I've looked at several other answers here and documentation online and nothing I found has worked.
Discovered there is a -Form parameter in Powershell 6 and later. I updated my powershell and used the following instead:
$file_contents = Get-Item $file_path
Invoke-RestMethod -Uri "https://subdomains.playvox.com/api/v1/files/upload?context=quality" -Method Post -ContentType "multipart/form-data" -Headers #{ "Authorization" = "Basic $($playvox_base64_auth)" } -Form #{ file = $file_contents }
And it worked.
I'm trying to find the equivalent of this curl command in PowerShell with Invoke-webrequest :
curl -k https://url/api \
-H "Authorization: mykey" \
-F "json=#test.json;type=application/json" \
-F "file=#test.txt; type=application/octet-stream"```
-H is ok, but I didn't find for two -F option.
Any idea ?
Many thanks.
The -f switch in cUrl is used for a multi-part formdata content type. PowerShell fortunately natively supports it, here's a generic example to get you started.
$Uri = 'https://api.contoso.com/v2/profile'
$Form = #{
firstName = 'John'
lastName = 'Doe'
email = 'john.doe#contoso.com'
avatar = Get-Item -Path 'c:\Pictures\jdoe.png'
birthday = '1980-10-15'
hobbies = 'Hiking','Fishing','Jogging'
}
$Result = Invoke-WebRequest -Uri $Uri -Method Post -Form $Form
And for your specific scenario, something like this should get you moving in the right direction.
$url = 'https://url/api'
$Form = #{
json = Get-Content .\test.json
file = Get-Content .\test.txt
}
$Result = Invoke-WebRequest -Uri $Uri -Method Post -Form $Form -Token "mkey"
It seems like you should be able to do -Form ... -Form ... but you can't, you need to build a multipart form object to submit.
see example 5 of the Microsoft docs https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-7.1
or this https://stackoverflow.com/a/65093133/89584 answer
or just install curl on windows (https://stackoverflow.com/a/16216825/89584) and use curl's nicer API
When using the following curl command I get a 200 OK response that provides the auth token I need for future requests. However, when I try to replicate the same thing in PowerShell using the invoke-restmethod below, I get no response at all.
I've tried turning on verbose and it just says 0 bytes returned. I've also tried using -Timeoutsec and that makes no difference. I've also verified the base64 string for the user and pass is the same and is correct. I also know I'm hitting the API, because I can touch other endpoints and get 401 unauthorized.
I've also tried using POSTMan and using the feature that lets you past the cURL command in and it'll build the request for you and I get the same empty/null response.
I've been able to get it working in Python without issue as well.
Would really appreciate some help with this, as I'm not sure what I'm missing here.
Curl call (200 OK):
curl -qgsSkH --no-progress-bar --header "Authorization: Basic SDFLKJSLKDJFLKJWERFV3Fh1RHZjIyQl" -D auth.txt -F form=foo https://10.10.10.10:443/ENDPOINT/v6/auth/login -vvv**
Python Code which also works:
import requests
import base64
base_url = 'https://server.domain.com/ENDPOINT/v6/auth/login'
username = 'APIAccount'
password = 'PASSWORD' # Since only alphanumeric characters are alloowed, this should be an extra long password.
credentials = (username + ':' + password).encode('utf-8')
base64_encoded_credentials = base64.b64encode(credentials).decode('utf-8')
headers = {
'Authorization': 'Basic ' + base64_encoded_credentials
}
resp = requests.post(base_url, headers=headers, verify=False)
print(headers)
print('Status:', resp.status_code)
PowerShell code (no output):
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$USER = "APIAccount"
$password = 'PASSWORD'
$HeaderAuth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $USER, $password)))
$SessionHeader = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$SessionHeader.Add('Authorization',('Basic {0}' -f $HeaderAuth))
$SessionHeader.Add('Accept','application/json')
$SessionHeader.Add('Content-Type','application/json')
$URL = "https://server.domain.com/ENDPOINT/v6/auth/login"
$APIResponse = Invoke-RestMethod -Method POST -Uri $URL -Headers $Sessionheader -Verbose -TimeoutSec 33
Using Invoke-WebRequest instead, did the trick.
I've searched high and low and asked on the product forums, but cannot seem to figure this out.
Using PowerShell 5 I'm attempting to limit my results by using a range header in the way the API documentation indicates. However, I receive the following error when I try to use it.
"The 'RANGE' header must be modified using the appropriate property or
method. Parameter name: name"
I've tried:
$headers = #{
SEC= $apiKey
range="items=0-49"
}
$result = Invoke-RestMethod -Method get -Uri $global:URI -ContentType 'application/json' -Header $headers
and...
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add('Accept','Application/Json')
$headers.Add('RANGE','items=0-49')
$headers.Add('SEC',$ApiKey)
$result = Invoke-WebRequest -Uri $global:URI -Headers $headers -Method get
Also, here's the curl example given within the API documentation:
curl -s -X GET -u USERNAME -H 'Range: items=0-49' -H 'Version: 10.0' -H 'Accept: application/json' 'https://product.com/api/s/of'
Really appreciate any pointers on this.
Thanks in advance
It seems to be a bug in PowerShell. I found this blog page: https://sethjackson.github.io/2017/01/18/header-woes/
The workaround according to this page:
$request = [System.Net.WebRequest]::Create($uri)
$request.Method = "GET"
$request.Headers.Add("SEC", $apiKey)
# add range header
$request.AddRange("items", 0, $count)
$reader = New-Object System.IO.StreamReader($request.GetResponse().GetResponseStream())
$data = ConvertFrom-Json $reader.ReadToEnd()