Как получить тело веб-запроса, которое возвратило 400 Bad Request из Invoke-RestMethod

Когда я запускаю следующую инструкцию

Invoke-RestMethod "https://api.mysite.com/the/endpoint" `
    -Body (ConvertTo-Json $data) `
    -ContentType "application/json" `
    -Headers $DefaultHttpHeaders `
    -Method Post

конечная точка возвращает 400 Bad Request, что приводит к тому, что PowerShell отображает следующее не очень полезное сообщение:

Invoke-WebRequest : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-WebRequest "https://api.mysite.com/the/endpoint" -Body  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Как получить тело ответа, которое может сказать мне, что было не так с запросом, который я отправил?

Ответ 1

Согласно документации Invoke-RestMethod, командлет может возвращать разные типы в зависимости от получаемого контента. Вывод команды cmdlet в переменную ($resp = Invoke-RestMethod (...)), а затем проверьте, есть ли тип HtmlWebResponseObject ($resp.gettype()). Тогда у вас будет много свойств в вашем распоряжении, например BaseResponse, Content и StatusCode.

Если $resp - это какой-то другой тип (строка, psobject и, скорее всего, null в этом случае), кажется, что сообщение об ошибке The remote server returned an error: (400) Bad Request является телом ответа, только лишенным из html (я тестировал это по некоторым из моих методов), возможно даже усеченный. Если вы хотите его извлечь, запустите командлет с помощью общего параметра, чтобы сохранить сообщение об ошибке: Invoke-RestMethod (...) -ErrorVariable RespErr, и вы получите его в переменной $RespErr.

EDIT:

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

try{$restp=Invoke-RestMethod (...)} catch {$err=$_.Exception}
$err | Get-Member -MemberType Property

  TypeName: System.Net.WebException

    Name           MemberType Definition
    ----           ---------- ----------
    Message        Property   string Message {get;}
    Response       Property   System.Net.WebResponse Response {get;}
    Status         Property   System.Net.WebExceptionStatus Status {get;}

Здесь все, что вам нужно, особенно в объекте WebResponse. Я перечислил 3 свойства, которые бросаются в глаза, там больше. Кроме того, если вы храните $_ вместо $_.Exception, могут быть некоторые свойства, которые PowerShell уже извлечен для вас, но я не ожидаю ничего более значимого, чем в .Exception.Response.

Ответ 2

Известна проблема с PowerShell Invoke-WebRequest и Invoke-RestMethod, где оболочка ест тело ответа, когда код состояния является ошибкой (4xx или 5xx). Похоже, что контент JSON, который вы ищете, испаряется именно таким образом. Вы можете получить тело ответа в блоке catch с помощью $_.Exception.Response.GetResponseStream()

    try {
    Invoke-RestMethod "https://api.mysite.com/the/endpoint" `
        -Body (ConvertTo-Json $data) `
        -ContentType "application/json" `
        -Headers $DefaultHttpHeaders `
        -Method Post
    }
    catch {
        $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
        $ErrResp = $streamReader.ReadToEnd() | ConvertFrom-Json
        $streamReader.Close()
    }

    $ErrResp

Ответ 3

$RespErr будет иметь более подробную информацию о BadRequest в моем случае

$responce = Invoke-RestMethod -Uri https://localhost:44377/explore/v2/Content -Method Post -Body $PostData -Headers $header -ErrorVariable RespErr;

$RespErr;

{ "error":{ "code":"","message":"The FavoriteName field is required." } }

Похоже, что он работает только в localhost, я пытался с моим фактическим сервером, что он не работал.

Другой способ попробовать - это

    try{
$response = ""
$response = Invoke-WebRequest -Uri https://contentserverint-mhdev.azurewebsites.net/apis/explore/v2/Content?overwrite=true -Method Post -Body $PostData -Headers  $header -ErrorVariable RespErr 
#$response = Invoke-RestMethod -Uri https://localhost:44377/explore/v2/Content?overwrite=true -Method Post -Body $PostData -Headers  $header -ErrorVariable RespErr 
Write-Host "Content created with url="$response.value[0] 

}
catch [System.Net.WebException] {   
        $respStream = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.IO.StreamReader($respStream)
        $respBody = $reader.ReadToEnd() | ConvertFrom-Json
        $respBody;
 }