Using PowerShell Invoke-RestMethod to POST large binary multipart/form-data - powershell

I'm trying to use the Invoke-RestMethod cmdlet in PowerShell 3 and 4, to upload a large binary file using a REST API's multipart/form-data upload. Here is a working cURL example on how to perform what I want to do in PowerShell:
curl -i -k -H "accept: application/json" -H "content-type: multipart/form-data" -H "accept-language: en-us" -H "auth: tokenid" -F file="#Z:\large_binary_file.bin" -X POST "https://server/rest/uri2"
I would love to see a working example on how to use Invoke-RestMethod to POST a multipart/form-data. I found a blog post from the PowerShell team showing how to use Invoke-RestMethod to upload to OneDrive (aka SkyDrive), but doesn't work well. I'd also like to avoid using System.Net.WebClient if at all possible. I also found another thread here on Stackoverflow, but it really didn't help much.
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
$server = "https://server"
uri = "/rest/uri1"
$headers = #{"accept" = "application/json"; "content-type" = "application/json";"accept-language" = "en-us"}
$body = #{"userName" = "administrator"; "password" = "password"}
$method = "POST"
#Get Session ID
$resp = Invoke-RestMethod -Method $method -Headers $headers -Uri ($server+$uri) -body (convertto-json $Body -depth 99)
$sessionID = $resp.sessionID
#Upload file
$uri = "/rest/uri2"
$headers = #{"accept" = "application/json";"content-type" = "multipart/form-data"; "accept- language" = "en-us"; "auth" = $sessionID}
$medthod = "POST"
$largeFile = "Z:\large_binary_file.bin"
I have tried both ways of using Invoke-RestMethod:
Invoke-RestMethod -Method $method -Headers $headers -Uri ($server+$uri) -InFile $largeFile
or
$body = "file=$(get-content $updateFile -Enc Byte -raw)"
Invoke-RestMethod -Method $method -Headers $headers -Uri ($server+$uri) -body $body

I notice couple of mistakes in your invoke statement. First, you need to use POST keyword instead of string value. Second, make sure that Content-Type is set to multipart/form-data. So this is my revised statement -
$uri = "http://blahblah.com"
$imagePath = "c:/justarandompic.jpg"
$upload= Invoke-RestMethod -Uri $uri -Method Post -InFile $imagePath -ContentType 'multipart/form-data'
You can check the response from the server by checking $upload.

Related

Upload file to API endpoint with PowerShell using multipart/form-data

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.

Invoke-WebRequest -Method 'POST' with -ContentType 'application/json' Fails

When using:
$body = #{
Manager = "spmigrationuser#contoso.com" #$item.PMEmail
Name = "some name"
Number = "Some number"
Practice = "Some Practice"
}
$response = Invoke-RestMethod -Method Post -Uri $Url -Body $body -ContentType 'application/json' # -Headers $Headers
Or
$response = Invoke-WebRequest -Method 'POST' -Uri $Url -Body $body -ContentType 'application/json' # -Headers $Headers
Neither ContentType 'application/json'
Nor
$Headers = #{'Content-Type' = 'application/json' }
-Headers $Headers
Works
The error is always:
"Invoke-WebRequest : {"error":{"code":"InvalidRequestContent","message":"The request content is not valid and could not be deserialized: 'Error parsing NaN value. Path '', line 1, position 1.'."}}"
The same call works in Postman
I am using PS 5.1 and I must have -ContentType 'application/json' otherwise PS works but the service fails
What can be the issue?
I agree with NickSalacious. Your issue is that you are not sending JSON.
If you are using Postman and just starting to do API in PowerShell. Postman has a "Code" Link in the top right hand corner of the request. Just below and to the right of the Send button. In there you can select PowerShell. This will give you a good basis to see how the same request could be ran in PowerShell.
Postman would turn your body into this:
$body = "{`n `"Manager`": `"spmigrationuser#contoso.com`",`n `"Name`": `"some name`",`n `"Number`": `"Some number`",`n `"Practice`": `"Some Practice`"`n}"
This is not the easiest to work with and to read. Learning and using ConvertTo-Json is going to help a lot more in the long run.
*Edit: Also look at Invoke-RestMethod and Invoke-WebRequest. They behave differently and sometimes one will be better than the other.
*Edit2: Figured I would put an example of another way to do it.
$request = #{
Uri = 'http://YourURI.Here'
Headers = #{ 'Authorization' = $token
'AnotherHeader?' = 'Sure'}
Method = 'POST'
Body = '{
"Manager": $item.PMEmail,
"Name": "some name",
"Number": "Some number",
"Practice": "Some Practice"
}'
ContentType = 'application/json'
}
$response = Invoke-RestMethod #request
The API requires that the body be a JSON string. You can do a simple conversion (using ConvertTo-Json) in your Invoke-RestMethod command and set the content type accordingly.
Invoke-RestMethod -Method POST -Uri $uri -Header $header -Body ($body | ConvertTo-Json) -ContentType 'application/json'
Sorry I bothered all of you.
I tested on another computer and it works fine.

PowerShell Invoke-RestMethod to query Token

Am Currently trying to query the authToken
$uri = 'http://xxx.centreon/api/index.php?action=authenticate'
$headers = #{
'username' = 'exemple'
'password' = 'exemple'
}
Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -ContentType application/x-www-form-urlencoded
But am having bad parameters
Via linux : curl -s -d "username=exemple&password=exemple" -H "Content-Type: application/x-www-form-urlencoded" -X POST http://xxx.centreon/api/index.php?action=authenticate
{"authToken":"NWU5OWM0YjRhMjIzMDYuMzg2NzYyMDY="}-
So this is a vague question but i do see how things might be wrong.
First the Username and Password are usually in the Body not the Header.
In your curl command I see its being passed as -d which would be the Body
$uri = 'http://xxx.centreon/api/index.php?action=authenticate'
$Body = #{
'username' = 'exemple'
'password' = 'exemple'
}
Invoke-RestMethod -Uri $uri -Method Post -body $Body -ContentType "application/x-www-form-urlencoded"

convert curl command to invoke-WebRequest or Invoke-RestMethod

I have a requirement to convert a working curl command to Invoke-WebRequest command
The command is used to create a project in SonarQube
Curl:
curl -u as123123112312: -X POST "http://sonarqube.it.company.net:9000/api/projects/create?key=%project_key%&name=%project_name%" > NUL
the command I tried:
$e = #{
Uri = "http://sonarqube.it.company.net:9000/api/projects/create?key=%project_key%&name=%project_name%"
Headers = #{"Authorization" = "Token as123123112312"}
}
Invoke-WebRequest #e -Method POST
Error:
Invoke-WebRequest : The remote server returned an error: (401) Unauthorized
Does anyone have an idea of converting curl to invoke-webrequest
This has been asked before. When you are posting, you need to have a body to send too, example:
$username = "as123123112312";
$password = "blah";
$Bytes = [System.Text.Encoding]::UTF8.GetBytes("$username`:$password");
$encodedCredentials = [System.Convert]::ToBase64String($bytes);
$body = "your content (i.e. json here)";
Invoke-WebRequest -Method Post -Body $body -Headers #{ "Authorization" = "Basic " + $encodedCredentials} -Uri "http://sonarqube.it.company.net:9000/api/projects/create?key=%project_key%&name=%project_name%"

how to convert curl command in Invoke-RestMethod of powershell

I need to convert the following command from bash curl to powershell syntax:
curl -s --head --header "PRIVATE-TOKEN:XXXXXX" "https://gitlab.XXXXXX/api/v4/projects/${id}/issues?state=all&per_pages=100"
This one in particular to get value of X-Total-Page from results.
I've tried to convert with this, but doesn't works:
function getPageNumbers ($myId)
{
$privateToken = "myToken"
$headers = #{"PRIVATE-TOKEN"="$privateToken"}
$url = "https://gitlab.XXXXXX/api/v4/projects/$myId/issues?state=all&per_pages=100"
$result = Invoke-RestMethod -Method Head -Uri "$url" -Header $headers -ContentType "text/json"
return $result
}
maybe because the "Head" method is used only for Invoke-Webrequest ?
Any suggestions?
Thank you in advance!
I am not seeing the body. But you can add that. You can do something like this:
$privateToken = "myToken"
$headers = #{"PRIVATE-TOKEN"="$privateToken"}
Invoke-WebRequest -Uri "https://gitlab.XXXXXX/api/v4/projects/$myId/issues?state=all&per_pages=100" -ContentType "application/json" -Headers $headers -Method Post
I would also suggest you to go through the CURL to Invoke-WebRequest
Hope it helps.