web-dev-qa-db-ja.com

Invoke-RestMethodから400 Bad Requestを返したWeb要求の本文を取得する方法

次のステートメントを実行すると

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:リモートサーバーがエラーを返しました:(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

応答の本文を取得するにはどうすればよいですか?これにより、送信した要求の何が問題なのかがわかりますか?

26
Tomas Aschan

Invoke-RestMethod ドキュメントによると、コマンドレットは受信するコンテンツに応じて異なるタイプを返すことができます。コマンドレットの出力を変数($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_変数に格納されます。

編集:

わかりました、私はそれを得て、それはかなり明白でした:)。 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_ほど意味のあるものは期待していません。

10
Adam Luniewski

PowerShell _Invoke-WebRequest_および_Invoke-RestMethod_には、ステータスコードがエラー(4xxまたは5xx)の場合にシェルが応答本文を食べるという既知の問題があります。探しているJSONコンテンツはまさにこの方法で蒸発しているように聞こえます。 $_.Exception.Response.GetResponseStream()を使用して、catchブロックで応答本文を取得できます。

_    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
_
24
brendan62269

$ 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." } }

ローカルホストでのみ動作するようです。実際のサーバーで試してみましたが、動作しませんでした。

別の方法はこれです

    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;
 }
4
shyam_

応答StatusCodeContentの直後にいる場合は、面倒なtry/catchや応答ストリームの手動読み取りなしでこの問題を解決する新しい方法があります。

_# Place the trap within your chosen scope (e.g. function or script)
trap [Net.WebException] { continue; }

# Exceptions are no longer thrown here
$response = Invoke-WebRequest $endpoint

# Check if last command failed
if (!$?)
{   
    # $error[0] now contains the ErrorRecord of the last error (in this case from Invoke-WebRequest)
    # Note: $response should be null at this point

    # Due to the magic of Microsoft.PowerShell.Commands.InvokeWebRequestCommand.WebCmdletWebResponseException
    # we can get the response body directly from the ErrorDetails field
    $body = $error[0].ErrorDetails.Message

    # For compatibility with $response.StatusCode lets cast to int    
    $statusCode = [int] $error[0].Exception.Response.StatusCode
}
_

私の知る限り、_ErrorRecord.ErrorDetails.Message_には_Microsoft.PowerShell.Commands.WebResponseObject.Content_プロパティとまったく同じものが含まれており、_Invoke-WebRequest_の呼び出しが成功すると返されます。すべてのGetResponseStream() jazz。

1
Schneider

私にとって、それは読む前にストリームの位置を0に設定するとき、Pesterコンテキストでのみ機能しました。

        $statusCode = $null
        $responseBody = $null
        try {
            $response = Invoke-RestMethod -Method GET -Uri "$($apiPrefix)$($operation)" -Headers $headers
            }
        catch [System.Net.WebException] {
            $statusCode = $_.Exception.Response.StatusCode
            $respStream = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($respStream)
            $reader.BaseStream.Position = 0
            $responseBody = $reader.ReadToEnd() | ConvertFrom-Json
        }
        $statusCode | Should Be $Expected
        $responseBody | Should Not Be $null
1
Kai Walter