mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add Simplified multipart/form-data Support to Web Cmdlets Through -Form Parameter (#5972)
- Adds -Form Parameter to Invoke-WebRequest and Invoke-RestMethod. - Form Accepts any IDictionary. - Keys are used as multipart/form-data field names (PSObject unwrapped and ConvertTo<String>(Object)). - FileInfo values are added as StreamContent with application/octet-stream content type and the FileInfo.Name as the file name. - Strings are treated as StringContent. - Singe values are converted to string with ConvertTo<String>(Object) and treated as StringContent - Top level collections are enumerated and converted as above. Nested collections are treated as a single value and converted accordingly. - Form is mutually exclusive with -Body and -InFile. - Per PowerShell-Committee decision, -Form makes no assumptions about the HTTP method used. It can theoretically be used with any method. User will need to manually supply -Method POST to post the form. - ContentType and content related headers supplied to -Headers will be ignored/cleared as MultipartFormDataContent requires control of these headers.
This commit is contained in:
+136
-1
@@ -306,6 +306,14 @@ namespace Microsoft.PowerShell.Commands
|
||||
[Parameter(ValueFromPipeline = true)]
|
||||
public virtual object Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary for use with RFC-7578 multipart/form-data submissions.
|
||||
/// Keys are form fields and their respective values are form values.
|
||||
/// A value may be a collection of form values or single form value.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public virtual IDictionary Form {get; set;}
|
||||
|
||||
/// <summary>
|
||||
/// gets or sets the ContentType property
|
||||
/// </summary>
|
||||
@@ -430,6 +438,18 @@ namespace Microsoft.PowerShell.Commands
|
||||
"WebCmdletBodyConflictException");
|
||||
ThrowTerminatingError(error);
|
||||
}
|
||||
if ((null != Body) && (null != Form))
|
||||
{
|
||||
ErrorRecord error = GetValidationError(WebCmdletStrings.BodyFormConflict,
|
||||
"WebCmdletBodyFormConflictException");
|
||||
ThrowTerminatingError(error);
|
||||
}
|
||||
if ((null != InFile) && (null != Form))
|
||||
{
|
||||
ErrorRecord error = GetValidationError(WebCmdletStrings.FormInFileConflict,
|
||||
"WebCmdletFormInFileConflictException");
|
||||
ThrowTerminatingError(error);
|
||||
}
|
||||
|
||||
// validate InFile path
|
||||
if (InFile != null)
|
||||
@@ -1075,8 +1095,21 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
|
||||
if (null != Form)
|
||||
{
|
||||
// Content headers will be set by MultipartFormDataContent which will throw unless we clear them first
|
||||
WebSession.ContentHeaders.Clear();
|
||||
|
||||
var formData = new MultipartFormDataContent();
|
||||
foreach (DictionaryEntry formEntry in Form)
|
||||
{
|
||||
// AddMultipartContent will handle PSObject unwrapping, Object type determination and enumerateing top level IEnumerables.
|
||||
AddMultipartContent(fieldName: formEntry.Key, fieldValue: formEntry.Value, formData: formData, enumerate: true);
|
||||
}
|
||||
SetRequestContent(request, formData);
|
||||
}
|
||||
// coerce body into a usable form
|
||||
if (Body != null)
|
||||
else if (Body != null)
|
||||
{
|
||||
object content = Body;
|
||||
|
||||
@@ -1590,6 +1623,108 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds content to a <see cref="MultipartFormDataContent" />. Object type detection is used to determine if the value is String, File, or Collection.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The Field Name to use.</param>
|
||||
/// <param name="fieldValue">The Field Value to use.</param>
|
||||
/// <param name="formData">The <see cref="MultipartFormDataContent" />> to update.</param>
|
||||
/// <param name="enumerate">If true, collection types in <paramref name="fieldValue" /> will be enumerated. If false, collections will be treated as single value.</param>
|
||||
private void AddMultipartContent(object fieldName, object fieldValue, MultipartFormDataContent formData, bool enumerate)
|
||||
{
|
||||
if (null == formData)
|
||||
{
|
||||
throw new ArgumentNullException("formDate");
|
||||
}
|
||||
|
||||
// It is possible that the dictionary keys or values are PSObject wrapped depending on how the dictionary is defined and assigned.
|
||||
// Before processing the field name and value we need to ensure we are working with the base objects and not the PSObject wrappers.
|
||||
|
||||
// Unwrap fieldName PSObjects
|
||||
if (fieldName is PSObject namePSObject)
|
||||
{
|
||||
fieldName = namePSObject.BaseObject;
|
||||
}
|
||||
|
||||
// Unwrap fieldValue PSObjects
|
||||
if (fieldValue is PSObject valuePSObject)
|
||||
{
|
||||
fieldValue = valuePSObject.BaseObject;
|
||||
}
|
||||
|
||||
// Treat a single FileInfo as a FileContent
|
||||
if (fieldValue is FileInfo file)
|
||||
{
|
||||
formData.Add(GetMultipartFileContent(fieldName: fieldName, file: file));
|
||||
return;
|
||||
}
|
||||
|
||||
// Treat Strings and other single values as a StringContent.
|
||||
// If enumeration is false, also treat IEnumerables as StringContents.
|
||||
// String implements IEnumerable so the explicit check is required.
|
||||
if (enumerate == false || fieldValue is String || !(fieldValue is IEnumerable))
|
||||
{
|
||||
formData.Add(GetMultipartStringContent(fieldName: fieldName, fieldValue: fieldValue));
|
||||
return;
|
||||
}
|
||||
|
||||
// Treat the value as a collection and enumerate it if enumeration is true
|
||||
if (enumerate == true && fieldValue is IEnumerable items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
// Recruse, but do not enumerate the next level. IEnumerables will be treated as single values.
|
||||
AddMultipartContent(fieldName: fieldName, fieldValue: item, formData: formData, enumerate: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="StringContent" /> from the supplied field name and field value. Uses <see cref="ConvertTo<T>(Object)" /> to convert the objects to strings.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The Field Name to use for the <see cref="StringContent" />.</param>
|
||||
/// <param name="fieldValue">The Field Value to use for the <see cref="StringContent" />.</param>
|
||||
private StringContent GetMultipartStringContent(Object fieldName, Object fieldValue)
|
||||
{
|
||||
var contentDisposition = new ContentDispositionHeaderValue("form-data");
|
||||
contentDisposition.Name = LanguagePrimitives.ConvertTo<String>(fieldName);
|
||||
|
||||
var result = new StringContent(LanguagePrimitives.ConvertTo<String>(fieldValue));
|
||||
result.Headers.ContentDisposition = contentDisposition;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="StreamContent" /> from the supplied field name and <see cref="Stream" />. Uses <see cref="ConvertTo<T>(Object)" /> to convert the fieldname to a string.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The Field Name to use for the <see cref="StreamContent" />.</param>
|
||||
/// <param name="stream">The <see cref="Stream" /> to use for the <see cref="StreamContent" />.</param>
|
||||
private StreamContent GetMultipartStreamContent(Object fieldName, Stream stream)
|
||||
{
|
||||
var contentDisposition = new ContentDispositionHeaderValue("form-data");
|
||||
contentDisposition.Name = LanguagePrimitives.ConvertTo<String>(fieldName);
|
||||
|
||||
var result = new StreamContent(stream);
|
||||
result.Headers.ContentDisposition = contentDisposition;
|
||||
result.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="StreamContent" /> from the supplied field name and file. Calls <see cref="GetMultipartStreamContent(Object, Stream)" /> to create the <see cref="StreamContent" /> and then sets the file name.
|
||||
/// </summary>
|
||||
/// <param name="fieldName">The Field Name to use for the <see cref="StreamContent" />.</param>
|
||||
/// <param name="file">The file to use for the <see cref="StreamContent" />.</param>
|
||||
private StreamContent GetMultipartFileContent(Object fieldName, FileInfo file)
|
||||
{
|
||||
var result = GetMultipartStreamContent(fieldName: fieldName, stream: new FileStream(file.FullName, FileMode.Open));
|
||||
result.Headers.ContentDisposition.FileName = file.Name;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion Helper Methods
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,12 @@
|
||||
<data name="BodyConflict" xml:space="preserve">
|
||||
<value>The cmdlet cannot run because the following conflicting parameters are specified: Body and InFile. Specify either Body or Infile, then retry. </value>
|
||||
</data>
|
||||
<data name="BodyFormConflict" xml:space="preserve">
|
||||
<value>The cmdlet cannot run because the following conflicting parameters are specified: Body and Form. Specify either Body or Form, then retry. </value>
|
||||
</data>
|
||||
<data name="FormInFileConflict" xml:space="preserve">
|
||||
<value>The cmdlet cannot run because the following conflicting parameters are specified: InFile and Form. Specify either InFile or Form, then retry. </value>
|
||||
</data>
|
||||
<data name="CredentialConflict" xml:space="preserve">
|
||||
<value>The cmdlet cannot run because the following conflicting parameters are specified: Credential and UseDefaultCredentials. Specify either Credential or UseDefaultCredentials, then retry.</value>
|
||||
</data>
|
||||
|
||||
@@ -1098,6 +1098,29 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
}
|
||||
|
||||
Context "Multipart/form-data Tests" {
|
||||
<#
|
||||
Content-Type request headers for multipart/form-data appear as:
|
||||
multipart/form-data; boundary="0ab0cb90-f01b-4c15-bd4d-53d073efcc1d"
|
||||
MultipartFormDataContent sets a random GUID for the boundary before submitting the request
|
||||
to the remote endpoint. Tests in this context for Content-Type match 'multipart/form-data'
|
||||
as we do not have access to the random GUID.
|
||||
#>
|
||||
<#
|
||||
Kestrel/ASP.NET inconsistently renders the new line for uploaded text files.
|
||||
File content tests in this context use match as a workaround.
|
||||
#>
|
||||
BeforeAll {
|
||||
$file1Name = "testfile1.txt"
|
||||
$file1Path = Join-Path $testdrive $file1Name
|
||||
$file1Contents = "Test123"
|
||||
$file1Contents | Set-Content $file1Path -Force
|
||||
|
||||
$file2Name = "testfile2.txt"
|
||||
$file2Path = Join-Path $testdrive $file2Name
|
||||
$file2Contents = "Test456"
|
||||
$file2Contents | Set-Content $file2Path -Force
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest Supports Multipart String Values" {
|
||||
$body = GetMultipartBody -String
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
@@ -1107,6 +1130,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Items.TestString[0] | Should Be 'TestValue'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest Supports Multipart File Values" {
|
||||
$body = GetMultipartBody -File
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
@@ -1118,6 +1142,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
$result.Files[0].ContentType | Should Be 'text/plain'
|
||||
$result.Files[0].Content | Should Match 'TestContent'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest Supports Mixed Multipart String and File Values" {
|
||||
$body = GetMultipartBody -String -File
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
@@ -1130,6 +1155,108 @@ Describe "Invoke-WebRequest tests" -Tags "Feature" {
|
||||
$result.Files[0].ContentType | Should Be 'text/plain'
|
||||
$result.Files[0].Content | Should Match 'TestContent'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest -Form supports string values" {
|
||||
$form = @{TestString = "TestValue"}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$response = Invoke-WebRequest -Uri $uri -Form $form -Method 'POST'
|
||||
$result = $response.Content | ConvertFrom-Json
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Items.TestString.Count | Should Be 1
|
||||
$result.Items.TestString[0] | Should BeExactly 'TestValue'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest -Form supports a collection of string values" {
|
||||
$form = @{TestStrings = "TestValue", "TestValue2"}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$response = Invoke-WebRequest -Uri $uri -Form $form -Method 'POST'
|
||||
$result = $response.Content | ConvertFrom-Json
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Items.TestStrings.Count | Should Be 2
|
||||
$result.Items.TestStrings[0] | Should BeExactly 'TestValue'
|
||||
$result.Items.TestStrings[1] | Should BeExactly 'TestValue2'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest -Form supports file values" {
|
||||
$form = @{TestFile = [System.IO.FileInfo]$file1Path}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$response = Invoke-WebRequest -Uri $uri -Form $form -Method 'POST'
|
||||
$result = $response.Content | ConvertFrom-Json
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Files.Count | Should Be 1
|
||||
|
||||
$result.Files[0].Name | Should BeExactly "TestFile"
|
||||
$result.Files[0].FileName | Should BeExactly $file1Name
|
||||
$result.Files[0].ContentType | Should BeExactly 'application/octet-stream'
|
||||
$result.Files[0].Content | Should Match $file1Contents
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest -Form supports a collection of file values" {
|
||||
$form = @{TestFiles = [System.IO.FileInfo]$file1Path, [System.IO.FileInfo]$file2Path}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$response = Invoke-WebRequest -Uri $uri -Form $form -Method 'POST'
|
||||
$result = $response.Content | ConvertFrom-Json
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Files.Count | Should Be 2
|
||||
|
||||
$result.Files[0].Name | Should BeExactly "TestFiles"
|
||||
$result.Files[0].FileName | Should BeExactly $file1Name
|
||||
$result.Files[0].ContentType | Should BeExactly 'application/octet-stream'
|
||||
$result.Files[0].Content | Should Match $file1Contents
|
||||
|
||||
$result.Files[1].Name | Should BeExactly "TestFiles"
|
||||
$result.Files[1].FileName | Should BeExactly $file2Name
|
||||
$result.Files[1].ContentType | Should BeExactly 'application/octet-stream'
|
||||
$result.Files[1].Content | Should Match $file2Contents
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest -Form supports combinations of strings and files" {
|
||||
$form = @{
|
||||
TestStrings = "TestValue", "TestValue2"
|
||||
TestFiles = [System.IO.FileInfo]$file1Path, [System.IO.FileInfo]$file2Path
|
||||
}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$response = Invoke-WebRequest -Uri $uri -Form $form -Method 'POST'
|
||||
$result = $response.Content | ConvertFrom-Json
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Items.TestStrings.Count | Should Be 2
|
||||
$result.Files.Count | Should Be 2
|
||||
|
||||
$result.Items.TestStrings[0] | Should BeExactly 'TestValue'
|
||||
$result.Items.TestStrings[1] | Should BeExactly 'TestValue2'
|
||||
|
||||
$result.Files[0].Name | Should Be "TestFiles"
|
||||
$result.Files[0].FileName | Should BeExactly $file1Name
|
||||
$result.Files[0].ContentType | Should BeExactly 'application/octet-stream'
|
||||
$result.Files[0].Content | Should Match $file1Contents
|
||||
|
||||
$result.Files[1].Name | Should BeExactly "TestFiles"
|
||||
$result.Files[1].FileName | Should BeExactly $file2Name
|
||||
$result.Files[1].ContentType | Should BeExactly 'application/octet-stream'
|
||||
$result.Files[1].Content | Should Match $file2Contents
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest -Form is mutually exclusive with -Body" {
|
||||
$form = @{TestString = "TestValue"}
|
||||
$body = "test"
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
|
||||
{Invoke-WebRequest -Uri $uri -Form $form -Body $Body -ErrorAction 'Stop'} |
|
||||
ShouldBeErrorId 'WebCmdletBodyFormConflictException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-WebRequest -Form is mutually exclusive with -InFile" {
|
||||
$form = @{TestString = "TestValue"}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
|
||||
{Invoke-WebRequest -Uri $uri -Form $form -InFile $file1Path -ErrorAction 'Stop'} |
|
||||
ShouldBeErrorId 'WebCmdletFormInFileConflictException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand'
|
||||
}
|
||||
}
|
||||
|
||||
Context "Invoke-WebRequest -Authentication tests" {
|
||||
@@ -1902,6 +2029,29 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
|
||||
}
|
||||
|
||||
Context "Multipart/form-data Tests" {
|
||||
<#
|
||||
Content-Type request headers for multipart/form-data appear as:
|
||||
multipart/form-data; boundary="0ab0cb90-f01b-4c15-bd4d-53d073efcc1d"
|
||||
MultipartFormDataContent sets a random GUID for the boundary before submitting the request
|
||||
to the remote endpoint. Tests in this context for Content-Type match 'multipart/form-data'
|
||||
as we do not have access to the random GUID.
|
||||
#>
|
||||
<#
|
||||
Kestrel/ASP.NET inconsistently renders the new line for uploaded text files.
|
||||
File content tests in this context use match as a workaround.
|
||||
#>
|
||||
BeforeAll {
|
||||
$file1Name = "testfile1.txt"
|
||||
$file1Path = Join-Path $testdrive $file1Name
|
||||
$file1Contents = "Test123"
|
||||
$file1Contents | Set-Content $file1Path -Force
|
||||
|
||||
$file2Name = "testfile2.txt"
|
||||
$file2Path = Join-Path $testdrive $file2Name
|
||||
$file2Contents = "Test456"
|
||||
$file2Contents | Set-Content $file2Path -Force
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod Supports Multipart String Values" {
|
||||
$body = GetMultipartBody -String
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
@@ -1933,6 +2083,103 @@ Describe "Invoke-RestMethod tests" -Tags "Feature" {
|
||||
$result.Files[0].ContentType | Should Be 'text/plain'
|
||||
$result.Files[0].Content | Should Match 'TestContent'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod -Form supports string values" {
|
||||
$form = @{TestString = "TestValue"}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$result = Invoke-RestMethod -Uri $uri -Form $form -Method 'POST'
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Items.TestString.Count | Should Be 1
|
||||
$result.Items.TestString[0] | Should Be 'TestValue'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod -Form supports a collection of string values" {
|
||||
$form = @{TestStrings = "TestValue", "TestValue2"}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$result = Invoke-RestMethod -Uri $uri -Form $form -Method 'POST'
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Items.TestStrings.Count | Should Be 2
|
||||
$result.Items.TestStrings[0] | Should Be 'TestValue'
|
||||
$result.Items.TestStrings[1] | Should Be 'TestValue2'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod -Form supports file values" {
|
||||
$form = @{TestFile = [System.IO.FileInfo]$file1Path}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$result = Invoke-RestMethod -Uri $uri -Form $form -Method 'POST'
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Files.Count | Should Be 1
|
||||
|
||||
$result.Files[0].Name | Should Be "TestFile"
|
||||
$result.Files[0].FileName | Should Be $file1Name
|
||||
$result.Files[0].ContentType | Should Be 'application/octet-stream'
|
||||
$result.Files[0].Content | Should Match $file1Contents
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod -Form supports a collection of file values" {
|
||||
$form = @{TestFiles = [System.IO.FileInfo]$file1Path, [System.IO.FileInfo]$file2Path}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$result = Invoke-RestMethod -Uri $uri -Form $form -Method 'POST'
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Files.Count | Should Be 2
|
||||
|
||||
$result.Files[0].Name | Should Be "TestFiles"
|
||||
$result.Files[0].FileName | Should Be $file1Name
|
||||
$result.Files[0].ContentType | Should Be 'application/octet-stream'
|
||||
$result.Files[0].Content | Should Match $file1Contents
|
||||
|
||||
$result.Files[1].Name | Should Be "TestFiles"
|
||||
$result.Files[1].FileName | Should Be $file2Name
|
||||
$result.Files[1].ContentType | Should Be 'application/octet-stream'
|
||||
$result.Files[1].Content | Should Match $file2Contents
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod -Form supports combinations of strings and files" {
|
||||
$form = @{
|
||||
TestStrings = "TestValue", "TestValue2"
|
||||
TestFiles = [System.IO.FileInfo]$file1Path, [System.IO.FileInfo]$file2Path
|
||||
}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
$result = Invoke-RestMethod -Uri $uri -Form $form -Method 'POST'
|
||||
|
||||
$result.Headers.'Content-Type' | Should Match 'multipart/form-data'
|
||||
$result.Items.TestStrings.Count | Should Be 2
|
||||
$result.Files.Count | Should Be 2
|
||||
|
||||
$result.Items.TestStrings[0] | Should Be 'TestValue'
|
||||
$result.Items.TestStrings[1] | Should Be 'TestValue2'
|
||||
|
||||
$result.Files[0].Name | Should Be "TestFiles"
|
||||
$result.Files[0].FileName | Should Be $file1Name
|
||||
$result.Files[0].ContentType | Should Be 'application/octet-stream'
|
||||
$result.Files[0].Content | Should Match $file1Contents
|
||||
|
||||
$result.Files[1].Name | Should Be "TestFiles"
|
||||
$result.Files[1].FileName | Should Be $file2Name
|
||||
$result.Files[1].ContentType | Should Be 'application/octet-stream'
|
||||
$result.Files[1].Content | Should Match $file2Contents
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod -Form is mutually exclusive with -Body" {
|
||||
$form = @{TestString = "TestValue"}
|
||||
$body = "test"
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
|
||||
{Invoke-RestMethod -Uri $uri -Form $form -Body $Body -ErrorAction 'Stop'} |
|
||||
ShouldBeErrorId 'WebCmdletBodyFormConflictException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand'
|
||||
}
|
||||
|
||||
It "Verifies Invoke-RestMethod -Form is mutually exclusive with -InFile" {
|
||||
$form = @{TestString = "TestValue"}
|
||||
$uri = Get-WebListenerUrl -Test 'Multipart'
|
||||
|
||||
{Invoke-RestMethod -Uri $uri -Form $form -InFile $file1Path -ErrorAction 'Stop'} |
|
||||
ShouldBeErrorId 'WebCmdletFormInFileConflictException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand'
|
||||
}
|
||||
}
|
||||
|
||||
#region charset encoding tests
|
||||
|
||||
Reference in New Issue
Block a user