API:Implementationsstrategie

From mediawiki.org
This page is a translated version of the page API:Implementation Strategy and the translation is 100% complete.

Dies erklärt die Implementation der MediaWiki-API-Maschine in den Kern. Wenn du in deinem Code eine API für Klienten zum Konsumieren anbieten möchtest, lese API:Erweiterungen .

Datei- / Modulaufbau

  • api.php ist der Eingangspunkt und befindet sich im Wiki-Root. Siehe API:Hauptseite#Der Endpunkt.
  • includes/api wird alle Dateien mit Bezug zur API enthalten, jedoch wird keine von ihnen als Eingangspunkt erlaubt sein.
  • Alle API-Klassen leiten sich von einer gewöhnlichen abstrakten Klasse ApiBase ab. Die Basisklasse bietet übliche Funktionen wie das Parsen von Parametern, Profilierung und Umgang mit Fehlern.
  • ApiMain ist die von api.php instantiierte Hauptklasse. Sie bestimmt, welches Modul basierend auf dem Parameter action=XXX ausgeführt wird. ApiMain erstellt auch eine Instanz der Klasse ApiResult, die das Ausgabe-Datenarray und verwandte Hilfsfunktionen enthält. Schließlich instantiiert ApiMain die Formatierungsklasse, die die Daten aus ApiResult in XML/JSON/PHP oder einem anderen Format an den Klienten ausgibt.
  • Jedes von ApiBase abgeleitete Modul wird einen Verweis auf eine Instanz von ApiMain während der Instantiierung erhalten, sodass das Modul während der Ausführung gemeinsame Quellen wie das Ergebnisobjekt nutzen kann.

Abfragemodule

  • ApiQuery verhält sich ähnlich wie ApiMain, in dem es seine Submodule ausführt. Jedes Submodul leitet sich von ApiQueryBase ab (außer ApiQuery selbst, wobei es sich um ein Top-Level-Modul handelt). Während der Instantiierung erhalten Submodule einen Verweis auf die ApiQuery-Instanz.
  • Alle Abfragemodule von Erweiterungen sollten ein Präfix aus 3 oder mehr Buchstaben nutzen. Die Kernmodule nutzen Präfixe aus 2 Buchstaben.
  • ApiQuery Ausführungsplan:
    1. Erhalte gemeinsame Abfrageparameter list/prop/meta, um benötigte Submodule zu bestimmen.
    2. Erstelle ein ApiPageSet-Objekt und befülle es durch die titles/pageids/revids-Parameter. Das pageset-Objekt enthält die Liste von Seiten oder Versionen, mit denen die Abfragemodule arbeiten werden.
    3. Sofern angefragt wird ein Generatormodul ausgeführt, um ein anderes PageSet zu erstellen. Ähnlich wie die Pipingstreams in UNIX. Angegebene Seiten sind die Eingabe für den Generator, der eine andere Reihe von Seiten für alle anderen Module erstellt.
  • Voraussetzungen für eine Fortsetzung der Abfrage:
    • Die SQL-Abfrage muss vollständig korrekt sein. In anderen Worten muss die Abfrage alle Spalten der einzigartigen Schlüssel sowie Konstanten in der WHERE-Klausel oder den ORDER BY-Klauseln nutzen.
      • In MySQL ist dies ein Exklusiv-oder, an dem Punkt, an dem Foo und Bar abgefragt werden, muss nach Titel, jedoch nicht nach Namensraum sortiert werden (Namensraum ist immer 0), Foo und Talk:Foo müssen nach Namensraum, jedoch nicht nach Titel sortiert werden (Titel ist immer "Foo") und Foo und Talk:Bar müssen nach Namensraum und Titel sortiert werden.
    • Die SQL-Abfrage muss nicht Dateisortieren.
    • Der an setContinueEnumParameter() gegebene Wert muss alle Spalten der ORDER BY-Klausel enthalten.
    • Bei Fortführung sollte eine einzelne Verbindungsbedingung zur WHERE-Klausel hinzugefügt werden. Wenn die Abfrage ORDER BY column_0, column_1, column_2 hat, sollte die Bedingung in etwa so aussehen:
(column_0 > value_0 OR (column_0 = value_0 AND
 (column_1 > value_1 OR (column_1 = value_1 AND
  (column_2 >= value_2)
 ))
))

Tausche natürlich "<" durch ">" aus, wenn deine ORDER BY-Spalten DESC nutzen. Stelle sicher, SQL-Einschübe in den Werten zu vermeiden.

Interne Datenstrukturen

  • Die Abfrage-API ist in eine sehr erfolgreiche Struktur einer global verschachtelten array()-Struktur eingebunden. Unterschiedliche Module würden einzelne Daten zu vielen Unterschiedlichen Punkten des Arrays hinzufügen, bis sie schließlich für den Klienten durch die Ausgabe (Ausgabemodule) gerendert werden würden. Für die API schlagen wir vor, dieses Array als eine Klasse mit Hilfefunktionen einzuschließen, um einzelne Knoten anzuhängen.

Fehler- / Statusmeldungen

Wir haben zunächst entschieden, Fehlerinformationen in der gleichen strukturierten Ausgabe einzubinden, wie ein normales Ergebnis (Option #2).

Für das Ergebnis können wir entweder die Standard-HTTP-Fehlercodes nutzen oder immer angemessen formatierte Daten zurückgeben:

Nutzung von HTTP-Code
void header( string reason_phrase [, bool replace [, int http_response_code]] )

header() kann genutzt werden, um den Rückgabe-Status der Operation zu setzen. Wir können alle möglichen Werte von reason_phrase definieren, sodass wir für die fehlgeschlagene Anmeldung code=403 und phrase="BadPassword" ausgeben können, während wir für jeden Erfolg einfach die Antwort ohne Änderung des Headers ausgeben würden.

Pro: Es ist ein Standard. Der Klient muss immer mit HTTP-Fehlern umgehen, sodass die Nutzung von HTTP-Code für Ergebnisse jede andere Fehlerbehandlung, die der Klient ausführen müsste, entfernen würde. Da der Klient Daten in mehreren Formaten anfordern kann, würde ein ungültiger Formatparameter immer noch korrekt behandelt werden, da es einfach ein anderer HTTP-Fehler-Code sein wird.

Kontra: ...

Fehlerinformationen in einer korrekten Antwort enthalten

Diese Methode wird immer ein korrekt formatiertes Antwortobjekt ausgeben, der Fehlerstatus / die Fehlerbeschreibung wird aber der einzige Wert in dem Objekt sein. Dies ähnelt der Art und Weise, auf die die Abfrage-API Statuscodes zurückgibt.

Pro: HTTP-Fehlercodes werden nur für Netzwerk-Probleme genutzt, nicht für Daten (logische Fehler). Wir sind nicht an die vorhandenen HTTP-Fehlercodes gebunden.

Kontra: Wenn das Datenformat nicht korrekt angegeben ist, was ist das Format der ausgegebenen Daten? Die Anwendung muss das Objekt parsen, um von dem Fehler zu erfahren (perf?). Code, der auf Fehler prüft, muss sich sowohl auf Ebene der Verbindung als auch auf Ebene des Daten-Parsings befinden.

Boilerplate-Code

Einfaches API-Modul
<?php

class Api<Modulname> extends ApiBase {
	public function __construct( $main, $action ) {
		parent::__construct( $main, $action );
	}

	public function execute() {
		
	}

	public function getAllowedParams() {
		return array(
			'<Parametername>' => array(
				ApiBase::PARAM_TYPE => array( 'foo', 'bar', 'baz' ),
			),
		);
	}

	public function getParamDescription() {
		return array(
			'<Parametername>' => '<Parameterbeschreibung>',
		);
	}

	public function getDescription() {
		return '<Modulbeschreibung hier>';
	}

	public function getExamples() {
		return array(
			'api.php?action=<Modulname>&<Parametername>=foo'
		);
	}

	public function getHelpUrls() {
		return '';
	}
}