Двоичные данные HTTP POST multipart / form-data с помощью PowerShell - PullRequest
0 голосов
/ 31 марта 2020

Использование PS 5.1.14409.1005 на W2K12 R2 x64.

Насколько я вижу, есть два способа сделать это -

  1. Создать тело составной части / формы вручную, следуя формат RF C
  2. Использовать MultipartFormDataContent. NET Типы

Я пробовал MultipartFormDataContent, но он не работает, когда я добавляю построенный объект в тело Invoke-WebRequest параметр (см. ниже, например), и мне удалось создать вручную с частями текстовой формы, но у меня возникли проблемы с двоичным. На снимке экрана показан захват запроса fiddler от Invoke-WebRequest powershell, который не работает, и от chrome, который работает. Двоичное содержимое отличается, и я не уверен, как получить его так же, как chrome.

Вот первые несколько байтов в шестнадцатеричном виде, сравнивающие фактический файл с тем, что chrome и powershell отправлены в fiddler , Во всем остальном есть небольшие отличия ...

file:   30 82 1D 32 02 01 03 30 82 1C F8
chrome: 30 82 1D 32 02 01 03 30 82 1C F8
ps:     30 2C 1D 32 02 01 03 30 2C 1C F8

Вот мой текущий код. Двоичный раздел заполняется Get-Content -Path $FilePath -Raw.

function Publish-RestApiFile {
    param ([string] $Url, [hashtable] $Params, [string] $FilePath, [string] $FileParamName)
    Add-Type -AssemblyName System.Web
    $bnd = [System.Guid]::NewGuid().ToString().ToLower().Replace('-','').PadLeft(40,'-')
    $content_type = "multipart/form-data; boundary=$bnd"
    $LF = "`r`n"
    $lines = @("--${bnd}${LF}")
    $Params.GetEnumerator() | ForEach-Object {
        $lines += "$('Content-Disposition: form-data; name="{0}"' -f $_.Name)${LF}${LF}$($_.Value)${LF}"
        $lines += "--${bnd}${LF}"
    }
    $file_name = Split-Path -Path $FilePath -Leaf
    $mime_type = [System.Web.MimeMapping]::GetMimeMapping($FilePath)
    $ct = Get-Content -Path $FilePath -Raw
    $lines += ('Content-Disposition: form-data; name="{0}"; filename="{1}"' -f $FileParamName,$file_name)+${LF}
    $lines += ("Content-Type: ${mime_type}${LF}${LF}"),$ct,${LF}
    $lines += ("--${bnd}--"+${LF})
    $lines = $lines -join ''
    Invoke-RestMethod -Uri $url -Method Post -Body $lines -ContentType $content_type -MaximumRedirection 0
}

Это показывает строку тела, сгенерированную текущим кодом - Generated multpart/form format

Это показывает различия в двоичном виде из запроса захватывает в Fiddler между PS Invoke-WebRequest (LEFT) и chrome (RIGHT). PowerShell (LEFT) Chrome (RIGHT)

Вот пример использования MultipartFormDataContent. Fiddler показывает, что объект просто приводится в строку в теле, например, System.Net.Http.StringContent System.Net.Http.StringContent System.Net.Http.StreamContent.

function Publish-RestApiFile2 {
    param ([string] $Url, [hashtable] $Params, [string] $FilePath, [string] $FileParamName)
    Add-Type -AssemblyName System.Net.Http, System.Web

    $mime_type = [System.Web.MimeMapping]::GetMimeMapping($FilePath)
    $form = [System.Net.Http.MultipartFormDataContent]::New()
    foreach ($param in $Params.GetEnumerator()) {
        $header = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
        $header.Name = $param.Name
        $content = [System.Net.Http.StringContent]::new($param.Value)
        $content.Headers.ContentDisposition = $header
        $form.Add($content)
    }
    $stream = [System.IO.FileStream]::New($FilePath, 'Open')
    $header = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
    $header.Name = $FileParamName
    $header.FileName = (Split-Path -Path $FilePath -Leaf)
    $content = [System.Net.Http.StreamContent]::New($stream)
    $content.Headers.ContentDisposition = $header
    $content.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse($mime_type)
    $form.Add($content)

    Invoke-WebRequest -Uri $Url -Method Post -Body $form -ContentType 'multipart/form-data'
    #Invoke-WebRequest -Uri $Url -Method Post -Body $form
    $stream.Close(); $stream.Dispose()
}

1 Ответ

1 голос
/ 31 марта 2020

Я работал, используя все. NET типов, а не Invoke-WebRequest -

На случай, если кому-то это поможет -

Add-Type -AssemblyName System.Net.Http

$url = "https://server.local/restendpoint"
$file = "C:\cert.pfx"

$fs = [System.IO.FileStream]::New($file, [System.IO.FileMode]::Open)

$s1 = New-Object System.Net.Http.StringContent 'alias_value'
$s2 = New-Object System.Net.Http.StringContent 'password_value'
$f1 = New-Object System.Net.Http.StreamContent $fs

$handler = New-Object System.Net.Http.HttpClientHandler
$handler.AllowAutoRedirect = $false # Don't follow after post redirect code 303
$client = New-Object System.Net.Http.HttpClient -ArgumentList $handler
$client.DefaultRequestHeaders.ConnectionClose = $true # Disable keep alive, get a 200 response rather than 303
$form = New-Object System.Net.Http.MultipartFormDataContent

$form.Add($s1, 'alias')
$form.Add($s2, 'password')
$form.Add($f1, 'file', 'cert.pfx')

$rsp = $client.PostAsync($u, $form).Result
$rsp.IsSuccessStatusCode # false if 303
$rsp.StatusCode -eq 303 # true if 303

$fs.Close(); $fs.Dispose()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...