Du möchtest deine App gerne in die GitHub-Plattform integrieren? Damit befindest du dich in guter Gesellschaft. In dieser Anleitung erfährst du, wie du eine App erstellst, die deinen Benutzerinnen eine optimale Oberfläche bietet und* verlässlich mit der API interagiert.
Schützen von Nutzlasten, die von GitHub bereitgestellt werden
Die von GitHub gesendeten Nutzlasten sollten unbedingt geschützt werden. Auch wenn in einer Nutzlast keine persönlichen Informationen (wie z. B. Kennwörter) übertragen werden, sollte jegliche Art von Datenverlust vermieden werden. Möglicherweise vertrauliche Informationen sind etwa E-Mail-Adressen von Committern oder die Namen privater Repositorys.
Führe zum Schutz der von GitHub übermittelten Nutzlasten die folgenden Schritte aus:
- Stelle sicher, dass der Empfangsserver über eine HTTPS-Verbindung verfügt. Standardmäßig überprüft GitHub beim Bereitstellen von Nutzlasten die SSL-Zertifikate.
- Stelle über ein geheimes Token sicher, dass die Nutzlasten definitiv von GitHub stammen. Indem du ein geheimes Token erzwingst, kannst du dir sicher sein, dass alle von deinem Server empfangenen Daten auch wirklich von GitHub stammen. Idealerweise erhalten alle Benutzerinnen deines Diensts ein unterschiedliches geheimes Token. Auf diese Weise sind im Fall eines kompromittierten Tokens keine weiteren Benutzerinnen betroffen.
Ausführen asynchroner Arbeit anstelle von synchroner Arbeit
GitHub erwartet, dass Integrationen innerhalb von 30 Sekunden nach Empfang der Webhooknutzlast reagieren. Benötigt dein Dienst länger, beendet GitHub die Verbindung, und die Nutzlast geht verloren.
Da die Geschwindigkeit deines Diensts nicht vorhergesagt werden kann, solltest du die „eigentliche Arbeit“ in einem Hintergrundauftrag ausführen. Resque (für Ruby), RQ (für Python) oder RabbitMQ (für Java) sind Beispiele für Bibliotheken, die Warteschlangen und die Verarbeitung von Hintergrundaufträgen behandeln.
Beachte, dass GitHub auch bei einem parallel ausgeführten Hintergrundauftrag erwartet, dass dein Server innerhalb von dreißig Sekunden reagiert. Dein Server muss den Empfang der Nutzlast durch eine Antwort bestätigen. Es ist entscheidend, dass dein Dienst die Nutzlast so schnell wie möglich überprüft, damit du korrekt darüber berichten kannst, ob der Server die Anforderung fortsetzt.
Verwenden geeigneter HTTP-Statuscodes für die Antwort an GitHub
Jeder Webhook verfügt über den Abschnitt „Aktuelle Übermittlungen“, in dem aufgelistet wird, ob eine Bereitstellung erfolgreich war oder nicht.
Verwende geeignete HTTP-Statuscodes, um deine Benutzer*innen zu informieren. Bestätige mit den Codes 201
oder 202
den Empfang einer Nutzlast, die nicht verarbeitet wird (etwa weil sie von einem anderen Branch als dem Standardbranch übermittelt wurde). Nutze den Fehlercode 500
nur für schwerwiegende Fehler.
Bereitstellen möglichst vieler Informationen
Deine Benutzer*innen können die Serverantworten, die du an GitHub sendest, genauer untersuchen. Stelle daher sicher, dass die Meldungen klar und aussagekräftig sind.
Folgen von Umleitungen, die die API sendet
Wurde eine Ressource verschoben, gibt GitHub dies direkt über einen Umleitungsstatuscode an. Diesen Umleitungen solltest du unbedingt folgen. Jede Umleitungsantwort legt im Header Location
den neuen URI fest, zu dem gewechselt werden soll. Wenn du eine Umleitung erhältst, solltest du deinen Code mit dem neuen URI aktualisieren, falls du einen veralteten Pfad anforderst, der möglicherweise entfernt wird.
Informationen zur Konfiguration deiner App für Umleitungen findest du in der Liste der HTTP-Statuscodes.
Vermeiden manueller URL-Analysen
Häufig enthalten API-Antworten Daten in Form von URLs. Wenn du beispielsweise ein Repository anforderst, erhältst du einen Schlüssel namens clone_url
mit einer URL zum Klonen des Repositorys.
Aus Stabilitätsgründen solltest du diese Daten nicht analysieren oder versuchen, das Format zukünftiger URLs daraus zu erstellen. Deine App funktioniert bei einer Änderung der URL möglicherweise nicht mehr.
Wenn du beispielsweise mit paginierten Ergebnissen arbeitest, erscheint es zunächst verlockend, URLs durch Anfügen von ?page=<number>
zu erstellen. Widerstehe dieser Versuchung. Weitere Informationen zum sicheren Folgen paginierter Ergebnisse findest du unter Verwenden der Paginierung in der REST-API.
Überprüfen von Ereignistyp und Aktion vor der Verarbeitung des Ereignisses
Es gibt mehrere Typen von Webhookereignissen, von denen jedes Ereignis wiederum über mehrere Aktionen verfügen kann. Mit steigender Anzahl von GitHub-Features werden gelegentlich neue Ereignistypen bzw. vorhandenen Ereignistypen neue Aktionen hinzugefügt. Stelle sicher, dass deine Anwendung Typ und Aktion eines Ereignisses explizit überprüft, bevor Webhooks verarbeitet werden. Frage mit dem Anforderungsheader X-GitHub-Event
den Typ des empfangenen Ereignisses ab, um eine geeignete Verarbeitung zu gewährleisten. Analog dazu kannst du mit dem action
-Nutzlastschlüssel der obersten Ebene abfragen, welche Aktion für das entsprechende Objekt ausgeführt wurde.
Wenn du beispielsweise einen GitHub-Webhook für „Alles senden“ konfigurierst, empfängt deine Anwendung neue Ereignistypen und Aktionen, wenn diese hinzugefügt werden. Die Nutzung einer ELSE-Klausel vom Typ „catch-all“ wird daher nicht empfohlen. Betrachte das folgende Codebeispiel:
# Not recommended: a catch-all else clause
def receive
event_type = request.headers["X-GitHub-Event"]
payload = request.body
case event_type
when "repository"
process_repository(payload)
when "issues"
process_issues(payload)
else
process_pull_requests
end
end
In diesem Codebeispiel werden die Methoden process_repository
und process_issues
ordnungsgemäß aufgerufen, wenn ein Ereignis vom Typ repository
oder issues
empfangen wird. Jeder andere Ereignistyp würde jedoch zum Aufruf von process_pull_requests
führen. Werden neue Ereignistypen hinzugefügt, würde dies zu einem falschen Verhalten führen, bei dem neue Ereignistypen auf dieselbe Weise wie das Ereignis pull_request
verarbeitet werden.
Es wird daher empfohlen, Ereignistypen explizit zu überprüfen und entsprechend zu handeln. Im folgenden Codebeispiel wird explizit überprüft, ob das Ereignis pull_request
vorhanden ist, und die else
-Klausel protokolliert, dass ein neuer Ereignistyp empfangen wurde:
# Recommended: explicitly check each event type
def receive
event_type = request.headers["X-GitHub-Event"]
payload = JSON.parse(request.body)
case event_type
when "repository"
process_repository(payload)
when "issues"
process_issue(payload)
when "pull_request"
process_pull_requests(payload)
else
puts "Oooh, something new from GitHub: #{event_type}"
end
end
Da jedes Ereignis zudem mehrere Aktionen umfassen kann, wird auch die Überprüfung der Aktionen empfohlen. Das Ereignis IssuesEvent
verfügt beispielsweise über mehrere mögliche Aktionen. Dazu gehören opened
beim Erstellen des Issues, closed
beim Schließen des Issues und assigned
beim Zuweisen des Issues.
Zusätzlich zu Ereignistypen sollen möglicherweise vorhandenen Ereignissen neue Aktionen hinzugefügt werden. Daher wird auch bei der Überprüfung von Ereignisaktionen die Nutzung einer ELSE-Klausel vom Typ „catch-all“ nicht empfohlen. Führe stattdessen eine explizite Überprüfung der Ereignisaktionen ähnlich wie bei den Ereignistypen aus. Das folgende Codebeispiel weist viele Gemeinsamkeiten mit dem vorherigen Beispiel für die Ereignistypen auf:
# Recommended: explicitly check each action
def process_issue(payload)
case payload["action"]
when "opened"
process_issue_opened(payload)
when "assigned"
process_issue_assigned(payload)
when "closed"
process_issue_closed(payload)
else
puts "Oooh, something new from GitHub: #{payload["action"]}"
end
end
In diesem Beispiel wird zunächst die Aktion closed
überprüft, bevor die Methode process_closed
aufgerufen wird. Alle nicht identifizierten Aktionen werden zur späteren Verwendung protokolliert.
Umgang mit API-Fehlern
Obwohl dein Code keine Fehler enthält, kann es vorkommen, dass beim Zugreifen auf die API nacheinander mehrere Fehler auftreten.
Wiederholte Statuscodes vom Typ 4xx
und 5xx
solltest du nicht ignorieren. Stelle stattdessen sicher, dass die Interaktion mit der API korrekt ausgeführt wird. Wenn z. B. ein Endpunkt eine Zeichenfolge anfordert, du aber einen numerischen Wert übergibst, erhältst du einen Überprüfungsfehler vom Typ 5xx
, und dein Aufruf wird nicht erfolgreich ausgeführt. Ebenso verursacht der Zugriffsversuch auf einen nicht autorisierten oder nicht vorhandenen Endpunkt einen Fehler vom Typ 4xx
.
Wenn du wiederholte Überprüfungsfehler bewusst ignorierst, wird deine App möglicherweise wegen missbräuchlicher Nutzung gesperrt.