How do I get the body of a web request that returned 400 Bad Request from Invoke-RestMethod

Viewed 57264

When I run the following statement

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

the endpoint returns 400 Bad Request, which causes PowerShell to show the following not-so-helpful message:

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

How do I get the body of the response, which might tell me what was wrong with the request I sent?

8 Answers

$RespErr will have the more details about the BadRequest in my case its

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

It looks like it works only in localhost, i tried with my actual server it didn't work.

another way to try is this

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

For me it only worked in a Pester context, when setting the streams Position to 0 before reading it.

        $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

You can get HTTP response without exceptions regardless if it is 200 or 400:

Powershell 7 introduced -SkipHttpErrorCheck

It works for both Invoke-WebRequest and Invoke-RestMethod:

PS> $res = Invoke-WebRequest -SkipHttpErrorCheck -Method POST https://login.microsoftonline.com/does-not-exist/oauth2/token
PS> $res    
                                                                                                                                                                                                                                                          
StatusCode        : 400                                                                                                                  
StatusDescription : BadRequest                                                                                                           
Content           : {"error":"invalid_request","error_description":"AADSTS900144: The request body must contain the following parameter:
                     'grant_type'.\r\nTrace ID: f40877fd-ae34-4b95-a8d4-c7b8ba613801\r\nCorrelation ID: …
RawContent        : HTTP/1.1 400 BadRequest
                    Cache-Control: no-store, no-cache
                    Pragma: no-cache
                    Strict-Transport-Security: max-age=31536000; includeSubDomains
                    X-Content-Type-Options: nosniff
                    P3P: CP="DSP CUR OTPi IND OTRi…
Headers           : {[Cache-Control, System.String[]], [Pragma, System.String[]], [Strict-Transport-Security, System.String[]], [X-Conte
                    nt-Type-Options, System.String[]]…}
Images            : {}
InputFields       : {}
Links             : {}
RawContentLength  : 503
RelationLink      : {}

MS documentation says:

-SkipHttpErrorCheck


This parameter causes the cmdlet to ignore HTTP error statuses and continue to process responses. The error responses are written to the pipeline just as if they were successful.

The textual response sent from the server is contained in the error variable at the following place:

$_.ErrorDetails.Message

While not exactly as the OP intended; I had a similar case with wanting to see the body/headers of the SENT request, rather than the response. The solution is -SessionVariable VarNameHere - You can then inspect this variable and see the exact content of the sent request.

Related