web-dev-qa-db-fra.com

Comment obtenir le corps d'une requête Web qui a renvoyé 400 requêtes incorrectes de Invoke-RestMethod

Quand je lance la déclaration suivante

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

le noeud final renvoie 400 Bad Request, ce qui oblige PowerShell à afficher le message peu utile suivant:

Invoke-WebRequest: le serveur distant a renvoyé une erreur: (400) requête incorrecte .
 À la ligne: 1 caractère: 1 
 + Invoke-WebRequest "https://api.mysite.com/the/endpoint" - Corps ...
 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
 + CategoryInfo: InvalidOperation: (System.Net.HttpWebRequest: HttpWebRequest) [Invoke-WebRequest], WebException 
 + FullyQualifiedErrorId: WebCmdletWebResponseException, Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Comment puis-je obtenir le corps de la réponse, qui pourrait me dire ce qui n'allait pas avec la demande que j'ai envoyée?

19
Tomas Aschan

Selon Invoke-RestMethod documentation, la cmdlet peut renvoyer différents types en fonction du contenu reçu. Assigner la sortie de la cmdlet à une variable ($resp = Invoke-RestMethod (...)), puis vérifier si le type est HtmlWebResponseObject ($resp.gettype()). Vous aurez alors de nombreuses propriétés à votre disposition, telles que BaseResponse, Content et StatusCode. 

Si $resp est un autre type (chaîne, psobject et très probablement null dans ce cas), il semble que le message d'erreur The remote server returned an error: (400) Bad Request soit le corps de la réponse, extrait uniquement du langage HTML (j'ai testé cela sur certaines de mes méthodes), voire tronqué. Si vous souhaitez l'extraire, exécutez la cmdlet à l'aide du paramètre commun pour stocker le message d'erreur: Invoke-RestMethod (...) -ErrorVariable RespErr. Vous l'aurez également dans la variable $RespErr.

MODIFIER:

Ok, je l’ai eu et c’était plutôt évident :). Invoke-RestMethod lève une erreur, alors attrapons-la simplement:

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

Voici tout ce dont vous avez besoin, en particulier dans l'objet WebResponse . J'ai énuméré 3 propriétés qui attirent le regard, il y a plus. De même, si vous stockez $_ au lieu de $_.Exception, certaines propriétés que PowerShell a déjà été extraites, mais je ne m'attends pas à rien de plus significatif que dans .Exception.Response

6
Adam Luniewski

Il existe un problème connu avec PowerShell Invoke-WebRequest et Invoke-RestMethod dans lequel le shell mange le corps de la réponse lorsque le code d'état est une erreur (4xx ou 5xx). On dirait que le contenu JSON que vous recherchez s’évapore de cette manière. Vous pouvez récupérer le corps de la réponse dans votre bloc catch en utilisant $_.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
13
brendan62269

$ RespErr aura le plus de détails sur le BadRequest dans mon cas sa 

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

On dirait que cela ne fonctionne que dans localhost, j'ai essayé avec mon serveur actuel cela ne fonctionnait pas.

une autre façon d'essayer est-ce 

    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_

Si vous êtes juste après la réponse StatusCode et Content, voici un nouveau moyen de résoudre ce problème sans beaucoup d'essais désagréables et de lecture manuelle des flux de réponses:

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

Autant que je sache, le ErrorRecord.ErrorDetails.Message contient l'équivalent exact de la propriété Microsoft.PowerShell.Commands.WebResponseObject.Content qui vous serait renvoyé si l'invocation de Invoke-WebRequest aboutissait, sans que vous ayez à faire tout ce GetResponseStream() jazz.

1
Schneider

Pour moi, cela ne fonctionnait que dans un contexte Pester, lorsque vous définissez la position du flux sur 0 avant de le lire.

        $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