PHP API Design Fragen

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • PHP API Design Fragen

    Hallihallöchen,

    ich habe zur Zeit wieder einmal die Ehre ein PHP Web-API zu erstellen, bei der iOS/OS X die Clientsoftware darstellen.
    Da ich eine 'vorzeigbare' und 'professionelle' API bauen will und nicht so wirklich weiß wie, frage ich Euch. Ich hatte da schon mal einen ähnlichen Beitrag hier erstellt zu.
    Und es lässt mich nunmal nicht locker, ich weiß auch nicht an wen ich mich wenden soll außer an Euch.
    Das API soll halt nur eine Schnittstelle für die Datenbank auf dem Server darstellen, bei dem iOS/OS X darauf zugreifen und die Daten aus den Tables ordentlich anzeigen.
    Da ich diesmal das Ganze ordentlich angehen will, mache ich mir ziemlich Gedanken, die ich hier niederschreiben möchte. Vielleicht kann man das Thema ja noch mal ein wenig diskutieren und dadurch das beste aus der Sache rausholen.

    Grob sollen natürlich diese Sachen beachtet werden:
    - JSON
    - Detaillierte Status-Rückmeldungen
    - PHP, PDO
    - Authentifizierung (wie am besten? API-Tokens waren bisher immer mein Mittel der Wahl. OAuth bietet sich für den Anwendungsfall nicht an) (API-Tokens mit Hierarchie sind stark erwünscht) (Gibt es da ein Framework, was einem das Leben erleichtert?)
    - Unterscheidung der Geräte, die das API ansprechen
    - ordentlich dokumentierbar

    Aber wenn ich mir schon Gedanken mache, was das API alles machen können soll, komme ich schon zu der folgenden Frage:
    Mehr Code in der Clientsoftware oder mehr Code in die Serversoftware? Was ich damit meine: Soll das API schon quasi ein fertiges SQL-Statement vom Client bekommen oder übergibt der Client nur Parameter an das API und dort wird erst ein SQL Statement erzeugt?

    Dann stellt sich wiederum die Frage:
    Allgemeine Parameter (also einfach ein Array mit Parametern) oder für jede API-Funktion sprechende Parameter?

    Des Weiteren wäre es ja auch mal wichtig zu wissen, wie Cocoa am besten mit solchen Daten arbeiten kann. Ich habe bisher immer JSON->NSDictionary genommen, in meine Objekte umgewandelt, dargestellt.
    Gibt es bessere Alternativen? Oder sollte man ein REST-Framework unter Cocoa nutzen?

    Was mir weiterhin zu schaffen macht, ist die Möglichkeit das API zu updaten. Also neue Klassen hinterlegen, ein Installscript ausführen und so weiter… Wie macht man sowas? Also entferntes Updaten der API-Version möglichst einfach und trotzdem sicher? Also ums kurz klarer zu machen: Das API soll nicht nur einmal auf der Welt irgendwo existieren, sondern ist Bestandteil eines Produkts, das eben Teil der Apps ist. Man sollte also per App das API updaten können (die Dateien würden dann in der Client-App via Update hinterlegt, beim nächsten Starten mit dem neuen Update würden diese Dateien dann auf dem Server installiert). Ich hab sowas schon mal gemacht, weiß nur gerade nicht mehr wirklich, wie und ob's sicher war. Mir kommt es aber sicherer vor, das über die Client-Apps zu machen, als dass das API eine Anfrage von einem Server bekommt und das automatisch von dort holt.

    Wie ihr ja seht… Ziemlich viele Fragen. Vielleicht könnt ihr mal Statements abgeben, wie ich's angehen sollte.
  • Soll das API schon quasi ein fertiges SQL-Statement vom Client bekommen oder übergibt der Client nur Parameter an das API und dort wird erst ein SQL Statement erzeugt?
    Selbstverständlich letzteres, alles andere wäre eine riesige Sicherheitslücke, es sei denn, der Client wird ausschließlich von Personen verwendet, die sowieso Zugriff auf die Datenbank haben, aber das ist ja normalerweise nicht der Fall...

    Allgemeine Parameter (also einfach ein Array mit Parametern) oder für jede API-Funktion sprechende Parameter?
    Für jede Funktion eigene, ansonsten musst du ja jedes mal ganz viele leere Variablen übermitteln... was man aber machen kann, ist (in json) soetwas wie

    Quellcode

    1. {"call":"funktionsname", "params":{ /* funktionsspezifische parameter */ }}


    Was mir weiterhin zu schaffen macht, ist die Möglichkeit das API zu updaten. Also neue Klassen hinterlegen, ein Installscript ausführen und so weiter… Wie macht man sowas? Also entferntes Updaten der API-Version möglichst einfach und trotzdem sicher? Also ums kurz klarer zu machen: Das API soll nicht nur einmal auf der Welt irgendwo existieren, sondern ist Bestandteil eines Produkts, das eben Teil der Apps ist. Man sollte also per App das API updaten können (die Dateien würden dann in der Client-App via Update hinterlegt, beim nächsten Starten mit dem neuen Update würden diese Dateien dann auf dem Server installiert). Ich hab sowas schon mal gemacht, weiß nur gerade nicht mehr wirklich, wie und ob's sicher war. Mir kommt es aber sicherer vor, das über die Client-Apps zu machen, als dass das API eine Anfrage von einem Server bekommt und das automatisch von dort holt.
    Verstehe ich nicht ganz. Sollen dann immer alle APIs geupdated werden oder nur einzelne? Normalerweise haben doch die User die Clienten und warum sollten die irgendwelche PHP-Klassen dort hinterlegen?
  • Günther schrieb:

    Soll das API schon quasi ein fertiges SQL-Statement vom Client bekommen oder übergibt der Client nur Parameter an das API und dort wird erst ein SQL Statement erzeugt?
    Selbstverständlich letzteres, alles andere wäre eine riesige Sicherheitslücke, es sei denn, der Client wird ausschließlich von Personen verwendet, die sowieso Zugriff auf die Datenbank haben, aber das ist ja normalerweise nicht der Fall…

    Habe ich mir schon gedacht, das ist sicher besser so. Danke!

    Günther schrieb:


    Allgemeine Parameter (also einfach ein Array mit Parametern) oder für jede API-Funktion sprechende Parameter?
    Für jede Funktion eigene, ansonsten musst du ja jedes mal ganz viele leere Variablen übermitteln... was man aber machen kann, ist (in json) soetwas wie

    Quellcode

    1. {"call":"funktionsname", "params":{ /* funktionsspezifische parameter */ }}


    Ich denke, dann werde ich wohl sprechende Parameternamen nehmen, tendierte ich sowieso zu.

    Günther schrieb:


    Was mir weiterhin zu schaffen macht, ist die Möglichkeit das API zu updaten. Also neue Klassen hinterlegen, ein Installscript ausführen und so weiter… Wie macht man sowas? Also entferntes Updaten der API-Version möglichst einfach und trotzdem sicher? Also ums kurz klarer zu machen: Das API soll nicht nur einmal auf der Welt irgendwo existieren, sondern ist Bestandteil eines Produkts, das eben Teil der Apps ist. Man sollte also per App das API updaten können (die Dateien würden dann in der Client-App via Update hinterlegt, beim nächsten Starten mit dem neuen Update würden diese Dateien dann auf dem Server installiert). Ich hab sowas schon mal gemacht, weiß nur gerade nicht mehr wirklich, wie und ob's sicher war. Mir kommt es aber sicherer vor, das über die Client-Apps zu machen, als dass das API eine Anfrage von einem Server bekommt und das automatisch von dort holt.
    Verstehe ich nicht ganz. Sollen dann immer alle APIs geupdated werden oder nur einzelne? Normalerweise haben doch die User die Clienten und warum sollten die irgendwelche PHP-Klassen dort hinterlegen?


    Also, ums verständlicher zu machen:
    Das API ist Bestandteil einer App. Jeder User dieser App soll natürlich immer davon ausgehen können, möglichst nichts zu tun, um neue Funktionen zu erhalten. Wenn das System (also Client/Server) am Ende neue Funktionen (nicht nur Kochen sondern auch Kuchenbacken) bekommen soll, muss das API auf dem Server des Users das natürlich auch unterstützen. Damit ist die Anforderung, dass das API updatebar ist. Ich kann dem User zwar sagen: Nimm die Dateien und lade sie via SFTP auf deinen Server und lass das Updatescript laufen… Aber das kommt nicht so gut an. Es ist also wichtig, dass das API von Haus aus die Möglichkeit hat, seine Bestandteile von Außen ersetzten zu lassen und ein Updatescript auszuführen.
    Es ist halt die Frage, wie man das sicher umsetzt.
  • Also kommen die Updates immer von dir. Das ist ja unproblematisch, einfach per PHP auf einen von dir betriebenen Server zugreifen und den Patch runterladen, sinnvoll ist es sicherlich, ein .zip (oder beliebt: .tag.gz) -Archiv und eine install.php o.ä. runterzuladen. Dann wird die install.php einfach per include() o.ä. ausgeführt - und greift dann selbst auf die Daten im Archiv zu, die die entsprechenden Teile der API ersetzen.

    Problematisch könnte es sein, wenn der User erst den Clienten updated und dann die API - denn dann geht der Client von vorhandenen Funktionen aus, die noch gar nicht installiert sind. Du könntest einfach immer, wenn zwischen API und Client eine Verbindung hergestellt wird, die Client-Versionsnummer übermitteln. Der Server vergleicht das dann mit seiner Version und sollte diese zu klein sein, sucht er nach Updates. So sparst du dir auch lästige Cronjobs.

    Das ganze ist eben so lange sicher, wie garantiert werden kann, dass die unter der "Update-Domain" zur Verfügung gestellten Inhalte von dir stammen...
  • Günther schrieb:

    Also kommen die Updates immer von dir. Das ist ja unproblematisch, einfach per PHP auf einen von dir betriebenen Server zugreifen und den Patch runterladen, sinnvoll ist es sicherlich, ein .zip (oder beliebt: .tag.gz) -Archiv und eine install.php o.ä. runterzuladen. Dann wird die install.php einfach per include() o.ä. ausgeführt - und greift dann selbst auf die Daten im Archiv zu, die die entsprechenden Teile der API ersetzen.

    So in etwa hab ich das schon mal gemacht, also da gibt's keine super-hyper-Lösung?

    Günther schrieb:


    Problematisch könnte es sein, wenn der User erst den Clienten updated und dann die API - denn dann geht der Client von vorhandenen Funktionen aus, die noch gar nicht installiert sind. Du könntest einfach immer, wenn zwischen API und Client eine Verbindung hergestellt wird, die Client-Versionsnummer übermitteln. Der Server vergleicht das dann mit seiner Version und sollte diese zu klein sein, sucht er nach Updates. So sparst du dir auch lästige Cronjobs.

    Jaein. Die kommunizieren schon ordentlich! Also bei jedem Request soll App-Versionsnummer (und Gerätetyp) übermittelt werden. Nach einem App-Update, denke ich mal, werde ich es erzwingen, dass das API-Update durchgeführt wird (wenn erforderlich für das App-Update). Ich wäre auch gegen ein Auto-Update, weil API dann neuer als Client sein kann. Andersherum ist's mir wohl lieber, weil ich den Client nicht zum Updaten zwingen kann, das API aber schon.

    Günther schrieb:


    Das ganze ist eben so lange sicher, wie garantiert werden kann, dass die unter der "Update-Domain" zur Verfügung gestellten Inhalte von dir stammen...

    Also wärest Du dafür, dass das API die Daten direkt von der Update-Domain gezogen werden und nicht aus dem Client-Resources Ordner heraus?


    UPDATE:
    Und dann hätte ich da noch eine Frage… Wenn ich jetzt den Inhalt eines Tables liefern soll, wie mache ich das dann am besten für Cocoa? Soll ich den ganzen Table dann einfach als JSON-Array ausspucken? Oder muss immer eine Anfrage pro Row gestellt werden, die zurückgegeben wird? Ich hatte nämlich mal so ein Problem mit dem Cocoa-JSON-Converter, dass mein JSON zu lang war.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von rosi-janni ()

  • Nun ja, der Client könnte das Update auch hochladen, dann wäre es aber notwendig, dass man dem User, auf dessen Gerät der Client läuft, zu 100% vertrauen kann. Ich weiß ja nicht, was exakt du machen willst, aber sobald die API (auch) von jemandem genutzt wird, der den Server nicht betreibt, könnte dieser ja ein manipuliertes Update hochladen. Bei einer API <-> Client Kardinalität von 1:1 kann man das sicher so machen.

    //UPDATE: Das hab ich noch nie gemacht, da bin ich überfragt :S
    Musst du mal gucken, wie hoch das Limit ist, und dann, wie lang deine Daten theoretisch werden könnten, also was noch realistisch ist.
    Wenn man da aber schon an solche Grenzen stößt ist es wohl sinnvoll, das ganze aufzuteilen, also z.B. für je 10 Rows einen Request ö.ä.
    Und dann natürlich nur das laden / senden, was man auch gerade braucht.
  • Günther schrieb:

    Nun ja, der Client könnte das Update auch hochladen, dann wäre es aber notwendig, dass man dem User, auf dessen Gerät der Client läuft, zu 100% vertrauen kann. Ich weiß ja nicht, was exakt du machen willst, aber sobald die API (auch) von jemandem genutzt wird, der den Server nicht betreibt, könnte dieser ja ein manipuliertes Update hochladen. Bei einer API <-> Client Kardinalität von 1:1 kann man das sicher so machen.


    Sagen wir, die Client-Apps mit API-Token dieses APIs werden quasi vom Administrator vergeben. Auch haben diese API-Tokens schon ein Flag für normal/admin Rechte. Also der, der das API als erstes auf seinem Server installiert erstellt persönlich API-Tokens für Menschen, die er in seine Datenbank einlädt. Diese sind wiederum noch unterteilt, ob sie admin. Rechte haben oder nicht. Nur Admins dürften dann updaten.

    Persönlich würde ich jedoch trotzdem vorziehen, wenn API und Master-Server direkt miteinander kommunizieren. Ist nur die Frage, ob man das noch irgendwie besser absichern kann.
  • Der Königsweg wäre vielleicht eine HTTPS-ähnliche asymmetrischer Verschlüsselung, so dass nur von dir signierte Updates akzeptiert werden.
    Das sprengt jetzt hier etwas den Rahmen, aber vielleicht hast du davon ja schonmal etwas gehört / etwas damit gemacht, sonst google einfach mal...
  • Also dann tendiere ich wohl zum direkten Installieren von Updates via https && Signatur-Verifizierung.

    Was dann noch mal so bisschen zusammengefasst, was noch zur Diskussion stünde:
    - Row für Row laden oder einen ganzen Table auf einmal? Wo läge eine sinnvolle Grenze zum Fragmentieren für Cocoa? Bisher habe ich immer Row für Row geladen, was allerdings lang dauert und wohl nicht optimal ist. (Oder ein REST-Framework in Cocoa?)
    - Gibt es eine professionelle Authentifizierung außer es manuell mit API-Tokens zu machen? (OAuth ungeeignet)
    - Guter Codeaufbau?
    - Wie dokumentiert man am besten?


    UPDATE:
    Sagt mal, ich hab jetzt gerade mal eine simple PHP Implementierung der Signierung gemacht. Das gab mir kurz zu denken: Wenn ich das Zertifikat in jedem API hinterlege, ist das ja kein Problem, ist ja nur der pubKey. Aber die Signatur eines Patches (Zip oder tar.gz) muss ich dann ja trotzdem zu jedem API bringen. Die Signatur muss ja unabhängig der Patch-Datei bleiben, wie genau soll ich die denn dann dem API mitteilen? Soll das dann einfach in der Response für den Request einer Update-Suche drinstehen?
    Ablauf wäre dann ja in etwa so:
    - Anfrage an Update-Server wird gestellt
    -> Es liegt ein Update vor
    - Anfrage zum Download des Updates wird an Update-Server gestellt
    -> Zip/tar.gz wird übertragen
    -> Signatur wird separat übertragen
    -> Zip/tar.gz wird auf Signatur überprüft
    -> Wenn valide wird entpackt und installiert

    So meintest Du das, oder?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von rosi-janni ()