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