Как заставить Powershell Invoke-Restmethod возвращать тело ответа HTTP 500

Вызов Invoke-RestMethod возвращает только очень бесполезное исключение ниже и не (насколько я могу судить) позволяет собирать содержимое тела (объект JSON, показанный в результатах трассировки фиддлера). Это кажется довольно плохой реализацией, если так, потому что определение http 500 довольно конкретно, что клиент должен возвращать тело ответа, чтобы помочь устранить неполадки... Я что-то упустил?

invoke-restmethod -method Post -uri "https://api-stage.enviance.com/ver2/EqlService.svc/eql" -Body (ConvertTo-Json $eqlhash)  -Headers @{"Authorization"="Enviance $session"}

invoke-restmethod: удаленный сервер возвратил ошибку: (500) Internal Server Error. В строке:1 символ:9...

След скрипача ниже

HTTP/1.1 500 Внутренняя ошибка сервера Подключение: закрыть Дата: четверг, 12 сентября 2013 г., 17:35:00 GMT Сервер: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50727 EnvApi- Версия: 2.0,2.0 EnvApi-Remaining-Calls: 994,994 EnvApi-Remaining-Interval: 2684,2684 Cache-Control: без кеш-памяти Pragma: no-cache Истекает: -1 Content-Type: text/csv; кодировка = UTF-8

{"errorNumber": 0, "message": "Текущий пользователь не имеет прав на получение данных из таблицы 'CustomFieldTemplate'"}

8 ответов

Решение

Хотя это старый поток, здесь ответ на проблему с командлетами Invoke-WebRequest и Invoke-RestMethod.

Этот беспокоил меня довольно долго. Поскольку все ответы 4xx и 5xx генерируют исключение, вы должны его перехватить, и тогда вы сможете извлечь ответ оттуда. Используйте это так:

$resp = try { Invoke-WebRequest ... } catch { $_.Exception.Response }

Теперь $resp всегда содержит все, что вам нравится.

Другой ответ действительно дает вам ответ, но вам нужен дополнительный шаг, чтобы получить фактическое тело ответа, а не только заголовки. Вот фрагмент:

try {
        $result = Invoke-WebRequest ...
}
catch {
        $result = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($result)
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $responseBody = $reader.ReadToEnd();
}

Это решение больше не работает с PowerShell 6 - оно не поддерживает GetResponseStream(), Вместо этого используйте

try {
    $result = Invoke-WebRequest ...
}
catch {
    $_.ErrorDetails.Message
}

Я написал короткую вспомогательную функцию для поддержки PowerShell 6 и более ранних версий:

function ParseErrorForResponseBody($Error) {
    if ($PSVersionTable.PSVersion.Major -lt 6) {
        if ($Error.Exception.Response) {  
            $Reader = New-Object System.IO.StreamReader($Error.Exception.Response.GetResponseStream())
            $Reader.BaseStream.Position = 0
            $Reader.DiscardBufferedData()
            $ResponseBody = $Reader.ReadToEnd()
            if ($ResponseBody.StartsWith('{')) {
                $ResponseBody = $ResponseBody | ConvertFrom-Json
            }
            return $ResponseBody
        }
    }
    else {
        return $Error.ErrorDetails.Message
    }
}

try {
    $result = Invoke-WebRequest ...
}
catch {
    ParseErrorForResponseBody($_)
}

В поисках ответа на мою проблему я нашел эту тему.

Эти решения работали для меня, но мне пришлось добавить две новые строки:

 $reader.BaseStream.Position = 0
 $reader.DiscardBufferedData()

Спасибо!

Я использовал исключительно Invoke-RestMethod - и, поскольку это может быть оболочка для Invoke-WebRequest, следующая информация может быть полезной.

Вам нужно перехватить исключение, а затем получить поток ответа от Response вот так.

    try
    {
        $response = Invoke-RestMethod Method Get uri "$($uri)" -Headers $headers
        return $response
    }
    catch
    {
        Write-Host "Exception details: "
        $e = $_.Exception
        Write-Host ("`tMessage: " + $e.Message)
        Write-Host ("`tStatus code: " + $e.Response.StatusCode)
        Write-Host ("`tStatus description: " + $e.Response.StatusDescription)

        Write-Host "`tResponse: " -NoNewline
        $memStream = $e.Response.GetResponseStream()
        $readStream = New-Object System.IO.StreamReader($memStream)
        while ($readStream.Peek() -ne -1) {
            Write-Host $readStream.ReadLine()
        }
        $readStream.Dispose();
    }

Ссылка: https://social.technet.microsoft.com/Forums/windowsserver/en-US/7d838d53-bfd5-4706-b0df-c4bee2d00412/using-memorystream-and-textreader

Обратите внимание, что в качестве дополнения к поставщику ответов Florial Feldhaus $_.ErrorDetails.Message не является исходным телом ответа. Регулярное выражение удаления HTML-тега используется для облегчения чтения ответа на github-ссылке. Поскольку поток расположен на линии 1535, в настоящее время нет способа извлечь исходное тело ответа.

Обходной путь заключается в использовании dotnet HttpClient чтобы получить исходный ответ вместо использования Invoke-WebRequest командлет

$url = "http://localhost"
$client = [System.Net.Http.HttpClient]::new()
$request = [System.Net.Http.HttpRequestMessage]::new()
$request.Content = [System.Net.Http.StringContent]::new("Hello World", [System.Text.Encoding]::UTF8, "plain/text")

$result = $client.SendAsync($request).GetAwaiter().GetResult()
$content = $result.Content.ReadAsStringAsync().GetAwaiter().GetResult()

Write-Verbose $content -Verbose

Ядро PowerShell Github здесь

Поскольку это одно из первых попаданий в Google, вот ответ для powershell core 6.0.0/7.0.0.

Invoke-RestMethod и Invoke-WebRequest поддерживает-SkipHttpErrorCheckвыключатель:

Этот параметр заставляет командлет игнорировать состояния ошибок HTTP и продолжать обработку ответов. Ответы на ошибки записываются в конвейер так же, как если бы они были успешными.

Если используется этот переключатель, командлеты не выдают ошибку при получении кода состояния сбоя.


Также Invoke-RestMethod теперь имеет параметры командной строки.-StatusCodeVariableи это можно использовать для извлечения и сохранения кода состояния ответа и заголовков


-ResponseHeadersVariableбыл добавлен в v6.0.0 и-SkipHttpErrorCheck/ResponseStatusVariableна версии 7.0.0

Улучшенная встроенная обработка теперь (?)
Я использую Powershell 7.3.9 в Linux и 5.1.19041.3570 в Windows и обнаружил, что тело ответа теперь отображается в сообщении об ошибке. Поэтому я удалил дополнительную обработку исключений и позволил поведению по умолчанию взять верх.

Другие вопросы по тегам