Mit REST-APIs aus PowerShell arbeiten — Invoke-RestMethod in der Praxis
· Go Komura · PowerShell, REST-API, Windows, Automatisierung, Geschäftssysteme, Integration, JSON, Betriebsoptimierung
„Bestelldaten aus der Web-API des Kernsystems abrufen und in einen internen Excel-Bericht einfließen lassen.“ „Jeden Morgen eine SaaS-Anwesenheits-API abrufen und den Schichtplan des Tages ausgeben.“ Unter den Situationen, in denen PowerShell im Geschäftsalltag eingesetzt wird, hat die REST-API-Integration in den letzten Jahren eindeutig zugenommen. Ein eigenes Werkzeug dafür zu kaufen lohnt sich nicht, aber von Hand lässt es sich auch nicht mehr bewältigen. Als Werkzeug, das genau diese Lücke füllt, ist Invoke-RestMethod außerordentlich leistungsfähig.
Gleichzeitig ist es typisch für die API-Integration, dass es einfach ist, sie zum Laufen zu bringen, der Sprung in den Produktivbetrieb aber plötzlich schwierig wird. Deutscher Text wird zu Zeichensalat, der Inhalt von Fehlerantworten lässt sich nicht lesen, gelegentlich schlägt es mit 429 fehl, es kommt in einer Proxy-Umgebung nicht durch, und ausgerechnet unter Windows PowerShell 5.1 wird es auf TLS-Ebene abgewiesen. Jedes davon ist ein Problem, das typisch für „ein System mit einer Gegenstelle“ ist.
Dieser Artikel richtet sich an IT-Mitarbeitende und Entwickler, die intern APIs aus PowerShell aufrufen, und ordnet Authentifizierung, das Senden und Empfangen von JSON, Fehlerbehandlung, erneute Versuche, Paging sowie die 5.1-spezifischen Fallstricke in der Reihenfolge, in der sie in der Praxis benötigt werden.
1. Das Wichtigste zuerst
- Für JSON/XML-APIs
Invoke-RestMethodverwenden. Es wandelt die Antwort automatisch in Objekte um. Benötigen Sie den Statuscode oder Header, verwenden SieInvoke-WebRequestoder-StatusCodeVariable/-ResponseHeadersVariable.12 - Die Authentifizierung in einem Header zu übergeben, ist der universellste Ansatz. Ab PowerShell 6 können Sie auch
-Authentication Bearer -Token(ein SecureString) verwenden.1 - Nicht-ASCII-JSON als UTF-8-Byte-Array zu senden, ist die zuverlässige Option. Geben Sie
charset=utf-8in-ContentTypeexplizit an. ConvertTo-Jsonhat standardmäßig eine Tiefe von 2. Tief verschachtelte Objekte werden abgeschnitten, sofern Sie-Depthnicht angeben.3- 4xx/5xx werden zu abbrechenden Fehlern. Ab PowerShell 7 können Sie den Text mit
$_.ErrorDetails.Messagelesen.-SkipHttpErrorCheckist eine Option, um zu verhindern, dass sie als Ausnahmen ausgelöst werden.1 - Bei 429 gemäß
Retry-Afterwarten. Geben Sie-MaximumRetryCountan, und das eingebaute Verhalten folgt dem automatisch. Beachten Sie jedoch, dass das eingebaute erneute Versuchen den gesamten Bereich 400–599 (plus 304) abdeckt, sodass auch 401 und 404 erneut gesendet werden. Implementieren Sie es nur dann selbst, wenn Sie eine Behandlung je Code oder exponentiellen Backoff benötigen.1 - Nicht idempotente Anfragen wie
POSTnicht automatisch erneut senden. Selbst bei einem Kommunikationsfehler oder einem 5xx hat der Server die Anfrage möglicherweise bereits verarbeitet, und ein erneutes Senden erzeugt einen doppelten Datensatz. - Paging über den
Link-Header lässt sich mit-FollowRelLinkautomatisieren. Cursor-basiertes Paging benötigt eine eigene Schleife.1 - Windows PowerShell 5.1 hat eigene Fallstricke. Drei davon: ob
-UseBasicParsingnötig ist, das explizite Aktivieren von TLS 1.2, und wie die Kodierung gehandhabt wird.4 -SkipCertificateChecknicht im dauerhaften Betrieb verwenden. Die interne CA vertrauenswürdig zu machen, ist die richtige Lösung.
2. Invoke-RestMethod und Invoke-WebRequest
Invoke-RestMethod |
Invoke-WebRequest |
|
|---|---|---|
| Umgang mit der Antwort | JSON/XML wird automatisch in Objekte umgewandelt | WebResponseObject (roher Text, Header, Code) |
| Hauptverwendung | REST-APIs | Abrufen von HTML, Untersuchen von Status und Headern |
| Statuscode | Über -StatusCodeVariable erhalten (PS7+) |
.StatusCode |
| Header | Über -ResponseHeadersVariable erhalten (PS6+) |
.Headers |
Für den Aufruf von APIs ist Invoke-RestMethod die Standardwahl. Auch wenn Sie Header oder Codes benötigen, erhalten Sie sie über die dafür vorgesehenen Variablenparameter.
$data = Invoke-RestMethod -Uri 'https://api.example.co.jp/v1/orders' `
-Headers @{ Authorization = "Bearer $token" } `
-StatusCodeVariable status -ResponseHeadersVariable headers -TimeoutSec 30
"HTTP $status / requests remaining: $($headers['X-RateLimit-Remaining'])"
$data.items | Select-Object orderId, customerName, amount
3. Die Authentifizierung übergeben
Die universellste Methode ist, sie direkt in den Header zu schreiben, was unter 5.1 und 7 gleichermaßen funktioniert.
# (1) Bearer-Token (am gebräuchlichsten)
$headers = @{ Authorization = "Bearer $accessToken"; Accept = 'application/json' }
Invoke-RestMethod -Uri $uri -Headers $headers
# (2) API-Schlüssel (der Header-Name folgt der Spezifikation des Anbieters)
$headers = @{ 'X-Api-Key' = $apiKey }
# (3) Basic-Authentifizierung (-Authentication ist ab PowerShell 6 verfügbar)
Invoke-RestMethod -Uri $uri -Authentication Basic -Credential $cred
# (4) Ein Bearer-Token über -Token übergeben (ab PowerShell 6; kann ein SecureString sein)
Invoke-RestMethod -Uri $uri -Authentication Bearer -Token $secureToken
# (5) Client-Zertifikat
Invoke-RestMethod -Uri $uri -Certificate $cert
Verwenden Sie -Authentication, verweigert PowerShell standardmäßig die Nutzung über etwas anderes als HTTPS (Sie können dies mit -AllowUnencryptedAuthentication umgehen, sollten es aber nicht tun, da Anmeldeinformationen dann im Klartext übertragen würden).1
Dass Sie Token oder API-Schlüssel niemals direkt in ein Skript schreiben, versteht sich von selbst. Die Speicherung über SecretManagement wird in „Anmeldeinformationen in PowerShell sicher handhaben“ behandelt.
4. JSON senden — die Fallen bei Zeichensalat und -Depth
Die zwei Dinge, auf die Sie beim Senden mit Sicherheit stoßen, sind Zeichensalat und abgeschnittene Verschachtelung.
-Depth von ConvertTo-Json ist standardmäßig 2, und tiefere Ebenen werden nicht expandiert — sie werden durch etwas wie den Typnamen als Zeichenkette ersetzt.3 Geben Sie es für Anfragetexte mit Verschachtelung immer an.
$body = @{
order = @{
customer = @{ code = 'C001'; name = '株式会社サンプル' } # dritte Ebene
lines = @( @{ item = 'A-100'; qty = 3 } )
}
}
# [SCHLECHT] mit dem Standard -Depth 2 gehen die Inhalte von customer und lines verloren
$json = $body | ConvertTo-Json
# [GUT] eine ausreichende Tiefe angeben
$json = $body | ConvertTo-Json -Depth 10
Bei Zeichensalat ist das Umwandeln in ein UTF-8-Byte-Array vor dem Senden der zuverlässigste Ansatz.
$json = $body | ConvertTo-Json -Depth 10
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json)
$res = Invoke-RestMethod -Uri $uri -Method Post `
-Headers @{ Authorization = "Bearer $token" } `
-ContentType 'application/json; charset=utf-8' `
-Body $bytes -TimeoutSec 60
Unter PowerShell 7 wird eine unverändert übergebene Zeichenkette als UTF-8 gesendet, aber in Skripten, die mit 5.1 geteilt werden, gleicht die Umwandlung in ein Byte-Array den Umgebungsunterschied aus. Für das Gesamtbild der Zeichenkodierungen unter Windows siehe „Windows-Textkodierungen und Zeilenenden“.
5. Fehlerbehandlung — was Sie nicht lesen können, können Sie nicht untersuchen
Invoke-RestMethod behandelt 4xx/5xx-Antworten als abbrechende Fehler. Das bedeutet, Sie können sie mit try/catch abfangen, aber die Frage wird, wie Sie den Text der Fehlermeldung lesen, den die API zurückgegeben hat.
try {
$res = Invoke-RestMethod -Uri $uri -Method Post -Body $bytes `
-ContentType 'application/json; charset=utf-8' -TimeoutSec 30
}
catch {
$status = $_.Exception.Response.StatusCode # z. B. BadRequest / 400
# Ab PowerShell 7 landet der Antworttext hier (die Fehlermeldung der API)
$detail = $_.ErrorDetails.Message
Write-Warning "API call failed ($status): $detail"
throw
}
Damit Sie keine Zeit mit Untersuchungen der Art „wir haben ein HTTP 400 zurückbekommen, aber wir haben keine Ahnung, warum es abgelehnt wurde“ verschwenden, gestalten Sie Ihre Skripte so, dass sie den Fehlertext stets protokollieren.
Wenn Sie nach Statuscode verzweigen möchten, macht die Verwendung von -SkipHttpErrorCheck, um zu verhindern, dass Ausnahmen ausgelöst werden, den Code geradliniger (ab PowerShell 7).1
$res = Invoke-RestMethod -Uri $uri -SkipHttpErrorCheck -StatusCodeVariable code -TimeoutSec 30
switch ($code) {
200 { $res.items }
404 { Write-Warning 'The target does not exist'; @() }
{ $_ -ge 500 } { throw "Server-side error: $code" }
default { throw "Unexpected response: $code" }
}
6. Erneute Versuche — 429 und vorübergehende Fehler
Ab PowerShell 6 besitzt Invoke-RestMethod -MaximumRetryCount und -RetryIntervalSec, die bei einem Fehlschlag erneut versuchen. Und darüber hinaus: Wenn eine 429-Antwort Retry-After enthält, wird statt des angegebenen Intervalls der Wert dieses Headers verwendet.1 Mit anderen Worten: Wenn Sie nur „bei Ratenbegrenzung wie angewiesen warten und erneut versuchen“ möchten, genügt die eingebaute Funktion.
# Für die reine Ratenbegrenzung reicht dies oft aus
Invoke-RestMethod -Uri $uri -Headers $headers -MaximumRetryCount 4 -RetryIntervalSec 5
Allerdings wird nicht nur bei 429 erneut versucht. Die Dokumentation legt fest, dass „bei Empfang eines Fehlercodes zwischen 400 und 599 (und 304)“ erneut versucht wird.1 Das bedeutet, dass auch Anfragen, die egal wie oft man sie sendet niemals erfolgreich sein werden — 401 (Authentifizierungsfehler), 403 (unzureichende Berechtigung), 404 (falsche URL) — erneut versucht werden. Führen Sie es mit falsch konfiguriertem Token unbeaufsichtigt aus, verschwenden Sie -MaximumRetryCount × -RetryIntervalSec Sekunden, bevor Sie überhaupt erfahren, dass es fehlgeschlagen ist, während die API dieselbe ungültige Anfrage immer wieder erhält. Möchten Sie bei einem dauerhaften Fehler sofort abbrechen, benötigen Sie die im Folgenden gezeigte eigene Implementierung.
Eine eigene Retry-Funktion benötigen Sie, wenn Sie Anforderungen wie diese haben.
- Statuscodes unterschiedlich behandeln (bei 4xx sofort scheitern, nur 5xx erneut versuchen usw.)
- Exponentiellen Backoff verwenden (die eingebaute Funktion versucht im angegebenen Intervall erneut)
- Den Fehlertext der API bei einem Fehlschlag protokollieren
So sieht das aus.
function Invoke-KsApi {
[CmdletBinding()]
param(
[Parameter(Mandatory)] [string] $Uri,
[string] $Method = 'Get',
[object] $Body,
[hashtable] $Headers = @{},
[ValidateRange(1, 10)] [int] $MaxAttempts = 4,
# Wenn Retry-After eine längere Wartezeit als diese vorgibt, abbrechen statt zu warten
[ValidateRange(1, 86400)] [int] $MaxWaitSeconds = 300,
# Das erneute Senden von POST u. Ä. nur zulassen, wenn die API Idempotenzschlüssel unterstützt
[string] $IdempotencyKey
)
# Erneutes Senden ist nur akzeptabel, wenn der zweimalige Empfang derselben Anfrage das
# Ergebnis nicht ändert. Bei POST/PATCH kann der Fall eintreten, dass "der Server
# erfolgreich war, aber die Antwort nie ankam", wobei ein naives erneutes Senden
# einen doppelten Datensatz erzeugt
$idempotentMethods = 'Get', 'Head', 'Options', 'Put', 'Delete'
$canRetry = ($Method -in $idempotentMethods) -or $IdempotencyKey
if ($IdempotencyKey) { $Headers = $Headers + @{ 'Idempotency-Key' = $IdempotencyKey } }
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
$params = @{
Uri = $Uri
Method = $Method
Headers = $Headers
TimeoutSec = 60
SkipHttpErrorCheck = $true # wir wollen nach Code verzweigen, daher keine Ausnahme
StatusCodeVariable = 'code'
ResponseHeadersVariable = 'resHeaders'
}
# Damit auch ein $Body-Wert von $false, 0 oder eine leere Zeichenkette als Text
# gesendet werden kann, wird geprüft, ob das Argument übergeben wurde, statt auf
# Wahrheitsgehalt zu testen
if ($PSBoundParameters.ContainsKey('Body')) {
# Übergabe über die Pipe macht ein leeres Array @() zu "null Eingaben" und
# liefert $null zurück, daher direkt mit -InputObject konvertieren, ohne
# aufzuzählen (@() wird zu [])
$json = ConvertTo-Json -InputObject $Body -Depth 10
$params.Body = [System.Text.Encoding]::UTF8.GetBytes($json)
$params.ContentType = 'application/json; charset=utf-8'
}
# -SkipHttpErrorCheck unterdrückt nur HTTP-Fehlerantworten. Kommunikationsfehler,
# bei denen überhaupt keine Antwort zurückkommt - Timeouts, Namensauflösungsfehler,
# Verbindungsabbrüche, TLS-Fehler - werden weiterhin als Ausnahme geworfen, daher
# hier abfangen und erneut versuchen
try {
$code = $null
$res = Invoke-RestMethod @params
}
catch {
# Bei einer nicht idempotenten Anfrage ist es möglich, dass der Server
# erfolgreich war und nur die Antwort nicht ankam. Nicht automatisch erneut
# senden, die Entscheidung dem Aufrufer überlassen
if (-not $canRetry) {
throw "Communication error ($Method is not retried; please check whether it was processed): $($_.Exception.Message)"
}
if ($attempt -eq $MaxAttempts) { throw }
$wait = [math]::Min([math]::Pow(2, $attempt), 60)
Write-Warning "Communication error: $($_.Exception.Message) - retrying in $wait seconds ($attempt/$MaxAttempts)"
Start-Sleep -Seconds $wait
continue
}
if ($code -lt 400) { return $res } # Erfolg
# Ein erneuter Versuch lohnt sich nur bei vorübergehenden Fehlern. Dies über eine
# Positivliste explizit machen (das erneute Versuchen dauerhafter Fehler wie 405
# oder 415 verschwendet Zeit und hinterlässt am Ende nur eine belanglose
# "Retry-Limit"-Meldung)
$retryable = @(408, 429, 500, 502, 503, 504)
if ($code -notin $retryable) {
throw "API error ($code): $($res | ConvertTo-Json -Compress -Depth 3)"
}
# Ein 5xx kann bedeuten, dass "der Server verarbeitet hat und danach fehlgeschlagen
# ist", daher nicht idempotente Anfragen nicht erneut senden. Dasselbe gilt für 429
# (keine Garantie, dass vor der Verarbeitung abgewiesen wurde)
if (-not $canRetry) {
throw "API error ($code). $Method is not retried automatically: $($res | ConvertTo-Json -Compress -Depth 3)"
}
if ($attempt -eq $MaxAttempts) {
throw "Retry limit reached ($code): $($res | ConvertTo-Json -Compress -Depth 3)"
}
# Retry-After kann nicht nur als Sekundenzahl, sondern auch im HTTP-Datumsformat
# zurückkommen. Ein direktes Casten auf [int] wirft eine Ausnahme und reißt den
# gesamten Retry mit
$wait = $null
$retryAfter = if ($resHeaders) { $resHeaders['Retry-After'] | Select-Object -First 1 }
if ($retryAfter) {
$seconds = 0
$date = [datetime]::MinValue
if ([int]::TryParse($retryAfter, [ref] $seconds)) {
$wait = $seconds
}
elseif ([datetime]::TryParse($retryAfter,
[cultureinfo]::InvariantCulture,
[System.Globalization.DateTimeStyles]::AdjustToUniversal, [ref] $date)) {
$wait = [math]::Max(0, [int]($date - [datetime]::UtcNow).TotalSeconds)
}
}
if ($null -eq $wait) {
$wait = [math]::Min([math]::Pow(2, $attempt), 60) # exponentieller Backoff (gedeckelt bei 60s)
}
elseif ($wait -gt $MaxWaitSeconds) {
# Die Anweisung des Servers abzuschneiden und vorzeitig erneut zu senden,
# wiederholt nur 429, bis das Limit erreicht ist. Ist die Wartezeit zu lang,
# dies mit der Wartezeit an den Aufrufer zurückgeben
throw "Rate limited. The server's instructed wait of $wait seconds exceeds the limit of $MaxWaitSeconds seconds, so the call was aborted. Please try again later ($code)"
}
Write-Warning "HTTP $code - retrying in $wait seconds ($attempt/$MaxAttempts)"
Start-Sleep -Seconds $wait
}
}
Der Umgang mit Idempotenz ist der zweite Schlüsselpunkt. GET und PUT liefern beim zweimaligen Empfang derselben Anfrage dasselbe Ergebnis, POST nicht. Insbesondere für den Fall, dass „der Server den Datensatz erfolgreich registriert hat, die Verbindung aber abbrach, bevor die Antwort zurückkam“, erzeugt ein naives erneutes Senden einen doppelten Datensatz. Die obige Implementierung erlaubt automatische erneute Versuche nur bei idempotenten Methoden oder wenn die API einen Idempotenzschlüssel (Idempotency-Key) unterstützt, und bricht andernfalls mit einem ausdrücklichen „bitte prüfen, ob es bereits verarbeitet wurde“ ab.
Retry-After nicht zu kürzen ist ein weiterer wichtiger Punkt. Weist der Server an, in 30 Minuten wiederzukommen, und Sie senden wegen Ihres eigenen Limits nach 5 Minuten erneut, erhalten Sie nur dasselbe 429 zurück. Sie verbrauchen Ihre Versuche mit sinnlosen Anfragen und bleiben am Ende nur mit einer „Retry-Limit“-Meldung zurück. In der obigen Implementierung schlägt es, wenn die angewiesene Wartezeit -MaxWaitSeconds übersteigt, sofort mit angehängter Wartezeit fehl, statt kürzer zu warten und es erneut zu versuchen. In der Stapelverarbeitung kann der Aufrufer diese Ausnahme abfangen und wählen, entweder die Arbeit auf den nächsten Lauf zu übertragen oder genau die angewiesene Zeit zu warten.
Ein weiterer wichtiger Punkt ist, dass die Menge der wiederholbaren Codes als Positivliste explizit gemacht wird. Schreibt man es als „die dauerhaften Fehler aufzählen und ausschließen“, wird jeder vergessene Code (405 Method Not Allowed, 415 Unsupported Media Type usw.) als vorübergehend behandelt, sodass Sie eine Anfrage wiederholen, die niemals erfolgreich sein wird, und am Ende nur mit einer unerklärlichen „Retry-Limit“-Meldung dastehen. Versuchen Sie nur die Codes erneut, von denen Sie wissen, dass sie vorübergehend sind, und lassen Sie alles andere sofort mit dem Fehlertext scheitern. Zum Retry-Design im Allgemeinen siehe „Fehlerbehandlung und Retry-Design in PowerShell“.
7. Paging
Eine API liefert so gut wie nie alles auf einmal zurück. Es gibt hauptsächlich zwei Verfahren.
(1) Das Link-Header-Verfahren (unter anderem von GitHub verwendet) lässt Sie mit -FollowRelLink automatisch der nächsten Seite folgen.1
# Der nächsten Seite automatisch folgen (die Anzahl der abgerufenen Seiten lässt sich auch begrenzen)
$all = Invoke-RestMethod -Uri $uri -Headers $headers -FollowRelLink -MaximumFollowRelLink 20
(2) Das Cursor-/Offset-Verfahren erfordert eine eigene Schleife.
$items = [System.Collections.Generic.List[object]]::new()
$cursor = $null
$page = 0
$maxPages = 100
do {
$page++
# Das Trennzeichen hängt davon ab, ob die ursprüngliche URI bereits eine Abfragezeichenkette
# besitzt. Immer ? anzuhängen ergibt .../items?status=active?cursor=..., und für den Server
# sieht der Cursor wie ein Teil des Werts von status aus.
# Der Cursor selbst ist eine undurchsichtige Zeichenkette (kann + & = # usw. enthalten), daher stets kodieren
$u = if ($cursor) {
$sep = if ($uri.Contains('?')) { '&' } else { '?' }
"$uri$sep" + "cursor=$([uri]::EscapeDataString($cursor))"
}
else { $uri }
$res = Invoke-KsApi -Uri $u -Headers $headers
$items.AddRange([object[]]$res.items)
$cursor = $res.nextCursor
# Einen Abbruch immer bekannt geben. Stillschweigend auszusteigen lässt andere glauben, sie hätten alles erhalten
if ($page -ge $maxPages -and $cursor) {
Write-Warning "Reached the page limit ($maxPages) and stopped. Some records may be missing"
break
}
} while ($cursor)
"Records fetched: $($items.Count)"
Für $items wird List[T] verwendet, um zu vermeiden, dass das Array durch += neu aufgebaut wird (siehe „Wo Sie nachsehen sollten, wenn ein PowerShell-Skript langsam ist“).
Vergessen Sie auch nicht, stets ein Seitenlimit zu setzen. Fehler, bei denen der Server denselben Cursor immer wieder zurückgibt oder vergisst, nextCursor zu leeren, kommen tatsächlich vor. Ohne Limit stoppt das Skript nie und feuert einfach immer weiter Anfragen ab. Und die Tatsache, dass abgebrochen wurde, muss als Warnung sichtbar gemacht werden. Bricht man stillschweigend ab, verarbeitet der Aufrufer ein unvollständiges Ergebnis in dem Glauben, es sei die Gesamtmenge.
8. Fallstricke, die spezifisch für Windows PowerShell 5.1 sind
In Umgebungen, in denen 5.1 noch verbleibt, sind dies die ersten drei Dinge, die zu verdächtigen sind.4
(1) TLS 1.2 ist nicht aktiviert. Bei den alten Standardeinstellungen belassen, können Sie sich nicht mit APIs verbinden, die nur TLS 1.2 oder höher akzeptieren, und erhalten „Die zugrunde liegende Verbindung wurde geschlossen“.
# Die Zauberformel für Windows PowerShell 5.1 (am Anfang des Skripts)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
(2) -UseBasicParsing kann erforderlich sein. Das Invoke-WebRequest von 5.1 analysiert HTML standardmäßig mit der Internet-Explorer-Engine, sodass es unter Konten fehlschlägt, unter denen IE nie initialisiert wurde (etwa Dienstkonten). Ab PowerShell 6 entfällt diese Abhängigkeit, und -UseBasicParsing wird ignoriert, auch wenn es angegeben wird.4
(3) -SkipHttpErrorCheck und -Authentication existieren nicht. Unter 5.1 müssen Sie den Antwortstrom selbst lesen, um einen Fehlertext zu lesen. Wenn Sie API-Integration ernsthaft betreiben, ist die Einführung von PowerShell 7 die günstigste Lösung (siehe „Die Unterschiede zwischen Windows PowerShell 5.1 und PowerShell 7“).
9. Proxys und Zertifikate
Rufen Sie aus einem Unternehmensnetzwerk eine API im Internet auf, ist das Durchkommen durch den Proxy die erste Hürde.
# Den Proxy explizit angeben und mit den Anmeldeinformationen des angemeldeten Benutzers authentifizieren
Invoke-RestMethod -Uri $uri -Proxy 'http://proxy.example.co.jp:8080' -ProxyUseDefaultCredentials
Führen Sie es unter dem Dienstkonto der Aufgabenplanung aus, können sich die Proxy-Einstellungen von denen bei interaktiver Anmeldung unterscheiden. Das ist das klassische Muster „bei mir funktioniert es, aber nur der nächtliche Batch schlägt fehl“. Gleichen Sie beim Testen das Ausführungskonto ab (siehe „Wenn Aufgaben der Aufgabenplanung nicht laufen“).
Die Verwendung von -SkipCertificateCheck bei Zertifikatsfehlern sollte auf eine vorübergehende Notlösung in einer Testumgebung beschränkt bleiben. Die dauerhafte Lösung ist, das Zertifikat Ihrer internen CA im Speicher der vertrauenswürdigen Stammzertifizierungsstellen abzulegen und Serverzertifikate korrekt auszustellen.
10. Bewährte Praxis in der Praxis (Entscheidungstabelle)
| Frage | Optionen | Faustregel |
|---|---|---|
| Cmdlet | Invoke-RestMethod / Invoke-WebRequest |
Ersteres für JSON-APIs. Header und Codes über die dafür vorgesehenen Variablen erhalten12 |
| Authentifizierung | Direkt in den Header geschrieben / -Authentication |
Bei gemeinsamer Nutzung mit 5.1 den Header-Ansatz verwenden. Die Werte in SecretManagement speichern |
| JSON senden | Zeichenkette / UTF-8-Byte-Array + charset explizit | Vermeidet Zeichensalat durch Umgebungsunterschiede |
ConvertTo-Json |
Standard / -Depth angeben |
Der Standard ist 2. Bei Verschachtelung immer angeben3 |
| Fehler | Nur try/catch / den Text ebenfalls protokollieren | $_.ErrorDetails.Message (PS7). Entscheidet, ob Sie die Ursache finden können1 |
| Viele Verzweigungen | catch / -SkipHttpErrorCheck + Codeverzweigung |
Letzteres ist natürlicher, wenn der Status die Verarbeitung entscheidet1 |
| Erneute Versuche | -MaximumRetryCount / eigene |
Das eingebaute folgt Retry-After bei 429 automatisch. Es versucht aber alles von 400 bis 599 erneut, daher bei sofortigem Scheitern dauerhafter Fehler selbst implementieren1 |
| POST erneut senden | Nur mit Idempotenzschlüssel | Die Registrierung kann bei nur verlorener Antwort erfolgreich gewesen sein, ein naives erneutes Senden erzeugt einen doppelten Datensatz |
| Paging | -FollowRelLink / eigene Schleife |
Ersteres beim Link-Header-Verfahren1 |
| 5.1-Umgebungen | Unverändert / TLS 1.2 explizit + PS7 erwägen | Die meisten Verbindungsfehler lassen sich auf TLS-Einstellungen zurückführen4 |
| Zertifikatsfehler | -SkipCertificateCheck / der internen CA vertrauen |
Die Prüfung im dauerhaften Betrieb niemals deaktivieren |
11. Zusammenfassung
Invoke-RestMethodist die Standardwahl für JSON-APIs. Header und Statuscodes sind über-ResponseHeadersVariable/-StatusCodeVariableverfügbar.- Senden Sie Nicht-ASCII-JSON als UTF-8-Byte-Array und geben Sie
charset=utf-8explizit an. Vergisst manConvertTo-Json -Depth, fehlen verschachtelte Inhalte. - 4xx/5xx sind abbrechende Fehler. Lesen Sie den Text mit
$_.ErrorDetails.Messageund protokollieren Sie ihn. Bei vielen Verzweigungen ist-SkipHttpErrorCheckeinfacher zu handhaben. - Senden Sie nicht idempotente Anfragen wie
POSTnicht automatisch erneut. Ein Kommunikationsfehler oder ein 5xx kann bedeuten, „der Server hat bereits verarbeitet“, und ein erneutes Senden erzeugt einen doppelten Datensatz. Für erneutes Senden ist ein Idempotenzschlüssel-Mechanismus erforderlich. - Bei 429 ist der Grundansatz, gemäß
Retry-Afterzu warten. Verwenden Sie-MaximumRetryCount, und dies wird vom eingebauten Verhalten übernommen. Da das eingebaute Verhalten aber alles von 400 bis 599 erneut versucht, benötigen Sie eigene Retry-Logik, wenn 401 oder 404 sofort scheitern sollen. Dasselbe gilt für die Behandlung je Code und exponentiellen Backoff — dauerhafte Fehler sollten sofort scheitern statt erneut versucht zu werden. - Verwenden Sie für Paging
-FollowRelLinkbeimLink-Header-Verfahren und eine eigene Schleife beim Cursor-Verfahren. Verwenden Sie nicht+=, um die Ergebnisse anzusammeln. - In 5.1-Umgebungen sind die drei Hindernisse das explizite TLS 1.2, die Abhängigkeit von der IE-Engine und fehlende Funktionen. Wenn Sie die API-Integration fortsetzen möchten, ist die Einführung von PowerShell 7 die schnellste Lösung.
Beispielcode zum Download
Der in diesem Artikel behandelte Code ist in direkt ausführbarer Form gepackt. Er enthält einen API-Aufrufer mit implementierten erneuten Versuchen, Idempotenz und Paging sowie einen HTTP-Server für Tests.
Beispielcode herunterladen (zip)
Die Beispiele dieses Artikels wurden tatsächlich unter PowerShell 7.6 ausgeführt und verifiziert (21 Pester-Tests). Führen Sie das im zip enthaltene Invoke-SampleTests.ps1 aus, um dieselbe Verifikation auf Ihrer eigenen Maschine zu reproduzieren.
# Syntaxprüfung + statische Analyse + Pester-Tests
./Invoke-SampleTests.ps1
Die Konfigurationswerte (Pfade, Servernamen, Mandanten-IDs usw.) sind Beispiele. Führen Sie sie nicht unverändert in einer Produktivumgebung aus — passen Sie sie an Ihre eigene Umgebung an.
Verwandte Artikel
- Fehlerbehandlung und Retry-Design in PowerShell — Von der try/catch-Falle bis zu Exit-Codes und bewährten Retry-Verfahren
- Anmeldeinformationen in PowerShell sicher handhaben — Klartext-Passwörter aus Ihren Skripten verbannen
- Die Unterschiede zwischen Windows PowerShell 5.1 und PowerShell 7 — Ein praktischer Leitfaden zur Migration interner Skripte
- Wo Sie nachsehen sollten, wenn ein PowerShell-Skript langsam ist — Arrays, Pipelines und Abgleich
- Einstieg in Microsoft Graph PowerShell — Microsoft 365 betreiben nach der Abschaltung von AzureAD und MSOnline
- HttpClient nicht in ein using-Block einwickeln — HTTP-Kommunikation in C#-Geschäftsanwendungen in der Praxis
Verwandte Beratungsbereiche
Die KomuraSoft LLC übernimmt das Design und die Umsetzung interner Integrationen über APIs von Kernsystemen und SaaS, die Automatisierung, die bestehende manuelle Arbeit durch API-Integration ersetzt, sowie die Untersuchung kommunikationsbezogener Fehler.
- Entwicklung von Geschäftsanwendungen
- Technische Beratung & Design-Review
- Fehlersuche & Ursachenanalyse
- Kontakt
Referenzlinks
-
Microsoft Learn, Invoke-RestMethod. Zum Senden von Anfragen an REST-Endpunkte und zur Umwandlung des JSON/XML der Antwort in PowerShell-Objekte; zur Angabe von -Headers / -Body / -ContentType / -Method; zu -Authentication (Basic / Bearer / OAuth) und -Token, wobei die Authentifizierung über etwas anderes als HTTPS standardmäßig verweigert wird; dazu, dass -SkipHttpErrorCheck 4xx/5xx nicht mehr ausnahmebehaftet macht; zum Abruf über -StatusCodeVariable und -ResponseHeadersVariable; zu erneuten Versuchen über -MaximumRetryCount / -RetryIntervalSec; zum Link-Header-Paging über -FollowRelLink / -MaximumFollowRelLink; sowie zu -Proxy / -ProxyUseDefaultCredentials, -SkipCertificateCheck und -TimeoutSec. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16
-
Microsoft Learn, Invoke-WebRequest. Dazu, dass die Antwort als WebResponseObject zurückgegeben wird und Zugriff auf StatusCode, Headers und Content gewährt; dazu, dass Windows PowerShell 5.1 HTML standardmäßig mit der Internet-Explorer-Engine analysiert und -UseBasicParsing der Weg drumherum ist; sowie dazu, dass ab PowerShell 6 die IE-Abhängigkeit entfällt, sodass -UseBasicParsing ignoriert wird. ↩ ↩2 ↩3
-
Microsoft Learn, ConvertTo-Json. Dazu, dass -Depth standardmäßig 2 ist und tiefere Ebenen nicht umgewandelt werden, sowie dazu, dass -Compress Leerraum entfernt. ↩ ↩2 ↩3
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. Zu Verhaltensunterschieden bei den webbezogenen Cmdlets, zu Einschränkungen, die spezifisch für Windows PowerShell 5.1 sind, und zu in PowerShell 7 hinzugefügten Funktionen. Außerdem die Eigenschaft ServicePointManager.SecurityProtocol zur Angabe der TLS-Version. ↩ ↩2 ↩3 ↩4
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
PC-Kitting mit winget + PowerShell automatisieren — Aus dem Handbuch ein ausführbares Skript machen
Wie Sie die Einrichtung von PCs für neue Mitarbeitende reproduzierbar gestalten. Behandelt die Installation von Anwendungen mit winget so...
Wo Sie nachsehen sollten, wenn ein PowerShell-Skript langsam ist — Arrays, Pipelines und Abgleich
Die klassischen Ursachen langsamer PowerShell-Skripte im Überblick. Warum += bei einem Array O(n²) ist, der Unterschied zwischen Pipeline...
Schluss mit Write-Host — PowerShells Ausgabeströme und Log-Design
Wie Sie zwischen den sechs Ausgabeströmen von PowerShell wählen, welche Probleme Write-Host mit sich bringt und wo es tatsächlich hingehö...
Parallelverarbeitung in PowerShell — Die Wahl zwischen ForEach-Object -Parallel und Jobs
Ein praxisnaher Überblick über die Unterschiede zwischen ForEach-Object -Parallel, Start-ThreadJob und Start-Job und wann welches zum Ein...
Externe EXEs aus PowerShell korrekt aufrufen — Die Fallstricke bei Argument-Quoting, Exitcodes und Zeichensalat
Rufen Sie robocopy oder eine hauseigene EXE aus PowerShell auf, und die Argumente zerbrechen, der Exitcode ist nicht verfügbar, und die A...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Geschäftsanwendungen, Geräteintegration und Kommunikationstools von den Anforderungen bis zur Umsetzung.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Wie wähle ich zwischen Invoke-RestMethod und Invoke-WebRequest?
- Für den Umgang mit JSON oder XML einer API verwenden Sie Invoke-RestMethod. Es analysiert den Antworttext automatisch und wandelt ihn in PowerShell-Objekte um, sodass Sie ConvertFrom-Json nicht selbst aufrufen müssen. Invoke-WebRequest gibt die Antwort als HtmlWebResponseObject zurück und gewährt Zugriff auf Statuscode, Header und den rohen Text. Wählen Sie Invoke-WebRequest, wenn Sie den Statuscode oder die Header untersuchen möchten oder direkt mit HTML arbeiten wollen. Beachten Sie, dass Invoke-RestMethod ab PowerShell 6 -ResponseHeadersVariable und -StatusCodeVariable besitzt, sodass Sie bei Invoke-RestMethod bleiben können, wenn Sie nur Header oder Code benötigen.
- Wenn ich JSON mit deutschen Umlauten sende, kommt es beim Empfänger als Zeichensalat an.
- Das passiert, weil die Byte-Folge des Textkörpers und die Angabe in Content-Type nicht übereinstimmen. Zuverlässig ist es, die von ConvertTo-Json erzeugte Zeichenkette in ein UTF-8-Byte-Array umzuwandeln, dieses an -Body zu übergeben und in -ContentType charset=utf-8 explizit anzugeben. Unter Windows PowerShell 5.1 kann eine unverändert übergebene Zeichenkette mit der Standardkodierung gesendet werden, daher wirkt diese Korrektur dort besonders zuverlässig. Zeichensalat beim Empfang hat dieselbe Grundursache — prüfen Sie, ob das charset der Antwort korrekt deklariert ist.
- Wenn die API 404 oder 500 zurückgibt, möchte ich die Fehlermeldung im Antworttext lesen.
- Ab PowerShell 7 sehen Sie im catch-Block unter $_.ErrorDetails.Message den Antworttext. Der Statuscode ist über $_.Exception.Response.StatusCode verfügbar. Zusätzlich sorgt -SkipHttpErrorCheck dafür, dass 4xx/5xx-Antworten als normale Antworten statt als Ausnahmen empfangen werden, was den Umgang erleichtert, wenn Sie nach Statuscode verzweigen möchten. Unter Windows PowerShell 5.1 müssen Sie den Antwortstrom selbst lesen, was ein weiterer Grund ist, 7 zu empfehlen.
- Die API liefert 429 (Ratenbegrenzung). Wie sollte ich damit umgehen?
- Der grundlegende Ansatz ist, die im Retry-After-Header der Antwort angegebene Sekundenzahl abzuwarten und dann erneut zu versuchen. Fehlt der Header, weiten Sie das Intervall mit exponentiellem Backoff aus (2 Sekunden, 4 Sekunden, 8 Sekunden usw.). Ab PowerShell 6 gibt es -MaximumRetryCount und -RetryIntervalSec, und wenn eine 429-Antwort Retry-After enthält, wird statt des angegebenen Intervalls der Wert dieses Headers verwendet — für die reine Ratenbegrenzung reicht das eingebaute Verhalten also aus. Verwenden Sie eine eigene Retry-Funktion nur, wenn Sie unterschiedliche Statuscodes unterschiedlich behandeln müssen oder exponentiellen Backoff benötigen. Zudem riskiert das erneute Senden einer nicht idempotenten Anfrage wie POST, doppelte Datensätze zu erzeugen, vermeiden Sie das daher, sofern kein Idempotenzschlüssel-Mechanismus vorhanden ist. Grundsätzlich ist es zuverlässiger, von vornherein die Anzahl der Aufrufe zu reduzieren (nur benötigte Felder abrufen, APIs zum Massenabruf nutzen).
- Beim selbstsignierten Zertifikat einer internen API erhalte ich einen Fehler. Ist es in Ordnung, -SkipCertificateCheck zu verwenden?
- Vermeiden Sie das im dauerhaften Betrieb. Die Zertifikatsprüfung abzuschalten bedeutet, dass Sie nicht bestätigen, dass die Gegenstelle echt ist, was auch in einem internen Netzwerk Raum für einen Man-in-the-Middle-Angriff lässt. Die richtige Lösung ist, das Zertifikat Ihrer internen CA im Speicher der vertrauenswürdigen Stammzertifizierungsstellen der Ausführungsumgebung abzulegen und Serverzertifikate korrekt auszustellen. Auch bei vorübergehender Nutzung in einer Testumgebung sollten Sie dies über eine Konfigurationsdatei oder einen Parameter als expliziten Schalter gestalten, damit es niemals in ein Produktivskript gelangt.
Autorenprofil
Profilseite des Artikelautors.
Go Komura
Geschäftsführer von KomuraSoft LLC
Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.