如何获取从Invoke-RestMethod返回400 Bad Request的Web请求的正文

时间:2016-03-14 11:44:58

标签: rest powershell

当我运行以下声明时

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

如何获取响应的正文,这可能会告诉我发送的请求有什么问题?

7 个答案:

答案 0 :(得分:18)

PowerShell Invoke-WebRequestInvoke-RestMethod存在一个已知问题,当状态代码为错误(4xx或5xx)时,shell会占用响应正文。听起来像你正在寻找的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

答案 1 :(得分:9)

根据Invoke-RestMethod文档,cmdlet可以根据收到的内容返回不同的类型。将cmdlet输出分配给变量($resp = Invoke-RestMethod (...)),然后检查类型是否为HtmlWebResponseObject$resp.gettype())。然后,您可以使用许多属性,例如BaseResponse,Content和StatusCode。

如果$resp是其他类型(字符串,psobject,在这种情况下很可能为null),似乎错误消息The remote server returned an error: (400) Bad Request是响应主体,只从html中删除(我测试了这个我的一些方法),甚至可能被截断。如果要提取它,请使用common参数运行cmdlet以存储错误消息: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中有任何更有意义的内容。

答案 2 :(得分:4)

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

答案 3 :(得分:1)

如果您刚刚回复StatusCodeContent,这是一种解决此问题的新方法,没有大量凌乱的尝试/捕获和手动读取响应流:

# 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()爵士乐。

答案 4 :(得分:1)

对我来说,它仅在Pester上下文中有效,当在读取之前将流Position设置为0时。

        $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

答案 5 :(得分:1)

从服务器发送的文本响应包含在以下位置的error变量中:

<my_entity_name>

答案 6 :(得分:0)

无论200还是400,您都可以无一例外地获得HTTP响应:

Powershell 7推出了-SkipHttpErrorCheck?

它同时适用于Invoke-WebRequestInvoke-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说:

-SkipHttpErrorCheck


此参数使cmdlet忽略HTTP错误状态,并且 继续处理回应。错误响应将写入 就好像他们成功一样。