Astropaul: Codeigniter API Nginx

Hallo,

ich hab da ein kleines Problem und vielleicht kann mir jemand den nötigen Hinweis geben. Sämtliche Recherchen und scheinbare Lösungen haben mich nicht ans Ziel geführt, ich denke, mir fehlen da ein paar Kentnisse.

Es geht um Folgendes. Ich habe eine Codeigniter-Anwendung (DB mysql). Die ganze Applikation läuft unter Ubuntu 18.04 Server auf einem nginx-Server. Unter der URL "example.com/api" können Json-Daten abgeholt werden.

zum Beispiel wäre ein Aufruf, um alle nötigen Json-Daten abzuholen: example.com/api/get

wobei natürlich get die Methode ist, auf die Codeigniter die URI mappt. Api.php ist der controller.

Gebe ich jetzt diese URL in den Browser ein, werden mir wunderbar die Datenausgegeben. Versuche ich jetzt das gleiche über ajax zu machen, bekomme ich immer eine 404-Fehlermeldung. (CORS ist nicht das Problem).

im nginx-log finde ich immer, dass die Datei "/home/user/websites/myserver/api/get/index.php" nicht gefunden wurde. Nginx misinterpretiert da was völlig falsch, er mappt Teile des URLpath auf den physischen Dateipfad.

a) warum tut nginx das? b) warum tut er das nur bei einem Ajax-Request? Was ist bei einem Ajax-Request genau anders, als bei einem Request über Browserzeile?

Hatte jemand schon mal dieses Problem? Wo kann ich suche? Oder wo muss ich da was drehen?

Lieben Dank Paul Heimann

ps. wie macht man hier im Editor denn Zeilenumbrüche? ☔️

  1. Tach!

    Wo kann ich suche?

    Browser haben eingebaute Entwicklerwerkzeuge. Untersuch erstmal dort im Netzwerk-Bereich, wie die beiden Requesttypen aussehen und wie sie sich unterscheiden.

    dedlfix.

    1. Hallo, ja, das weiss ich.

      ich hab diese jetzt nochmal verglichen. Einziger Unterschied ist im Grunde

      Ajax:

      Accept application/json, text/javascript, */*; q=0.01

      Browser:

      Accept text/html,application/xhtml+xm…plication/xml;q=0.9,*/*;q=0.8

      mime-types sind serverseitig in der entsprechenden conf so erfasst. Unter apache2 lief es einwandfrei.

      grüße Paul

      1. Hallo,

        kopier dir den Request als curl-Request und probier damit ein wenig herum (wenn du mit CLI und curl umgehen kannst) oder nimm ein API-Testtool wie postman, bau den Request nach und teste dann.

        Dann kannst du sichergehen, dass es wirklich am "Accept"-Header liegt.

        Erscheint mir sehr merkwürdig, dass nginx da einen Unterschied machen sollte.

        Viele Grüße Matti

        1. Probier mal wget.

          1. Tach!

            Probier mal wget.

            Dann doch lieber PostMan, ist komfortabler.

            dedlfix.

  2. Hallo Astropaul,

    ps. wie macht man hier im Editor denn Zeilenumbrüche? ☔️

    Oberhalb des Texteingabefeldes ist ein rotes Fragezeichen. Auf der verlinkten Wikiseite werden viele Formatierungsmöglichkeiten dargestellt.

    Bis demnächst
    Matthias

    -- Rosen sind rot.
  3. Hallo Astropaul,

    machst du den Ajax Request als GET oder POST?
    Ist dein URI-Routing so konfiguriert, dass es das verwendete HTTP Verb einbezieht?

    Rolf

    -- sumpsi - posui - clusi
    1. @Rolf B

      Ist dein URI-Routing so konfiguriert, dass es das verwendete HTTP Verb einbezieht?

      Selbst das würde nicht zu einem status 404 passen. Der richtige Status hierfür wäre 405 Method Not Allowed.

      MfG

      1. Hallo pl,

        ja, du hast prinzipiell recht, es halten sich nur nicht alle dran. Der URI Router sucht eine passende Route zu URI und Verb und findet nichts. Und dann zuckt er die Schultern und sagt: Not Found.

        Deswegen loht diese Rückfrage beim OP schon. Es sei denn, du kennst Codeigniter so genau, dass Du weißt, dass er dann 405 bringen würde. <spaß>Vielleicht ist das bei der Perl-Version ja so.</spaß>

        Rolf

        -- sumpsi - posui - clusi
        1. Naja lieber @Rolf B

          was hier bisher so beschrieben wurde passt schonmal überhaupt nicht zusammen. Und falls sich ein FW so verhalten täte, wäre das für die Fehlersuche, Wartung und Debugging eine Katastrophe.

          Wobei ich es sowieso schon bedenklich finde, Namen von Klassen und Methoden über den URI zu schleifen. Besser ists, OOP eben konsequent zu nutzen, sowas gehört in die Konfiguration und zwar als Eigenschaft und nach draußen nicht sichtbar.

          Ansonsten zöge ja eine Änderung des Klassenname eine Änderung des URL mit sich, brr. Es sei denn auch man möchte Hacker dazu einladen, beliebige Methoden aufzurufen. Spannende Frage, wirft das FW dann eine Exception oder einen Not Found? Oder Beides 😉

          MfG

          1. Hallo pl,

            nein, ganz so ist das nicht. Oder, ok, sollte es nicht sein.

            In einem MVC Framework hast Du - wenn Du mit generischen Routingregeln arbeitest - eine bestimmte Kategorie von Klassen, auf die diese Regeln zurückgreifen können: die Controller. Und darin hast Du public Methoden, auf die URIs gemappt werden können.

            Wenn Du etwas paranoider bist, dann baust Du keine generischen Routingregeln sondern mappst URIs einzeln oder mit relativ starren Regeln auf Methoden, ggf. noch zusammen mit einem HTTP-Verb.

            Der Aufruf beliebigen Codes ist in beiden Fällen nicht möglich. Dass geänderte Controllernamen bei generischem URI Mapping die erforderliche URI ändern, das ist richtig. Und weil man das nicht tut, ändert man auch die Controllernamen nicht sobald die URIs publiziert sind. Oder wenn es einen DOCH so sehr juckt, dass man den Namen einfach nicht stehen lassen kann, dann muss man eben die Routingregeln ändern, so dass die URIs gleich bleiben.

            Rolf

            -- sumpsi - posui - clusi
            1. hi @Rolf B

              nein, ganz so ist das nicht. Oder, ok, sollte es nicht sein.

              In einem MVC Framework hast Du - wenn Du mit generischen Routingregeln arbeitest - eine bestimmte Kategorie von Klassen, auf die diese Regeln zurückgreifen können: die Controller. Und darin hast Du public Methoden, auf die URIs gemappt werden können.

              Wenn Du etwas paranoider bist, dann baust Du keine generischen Routingregeln sondern mappst URIs einzeln oder mit relativ starren Regeln auf Methoden, ggf. noch zusammen mit einem HTTP-Verb.

              In meinem FW mache ich weder noch. Meine Routingtable hat einen statischen und einen dynamischen Teil. Während der statische Teil Klassennamen an URLs bindet, ist der dynamische Teil für die letzte Meile zuständig. Hierzu gehörige Methoden sind bei mir als Interface ausgebildet, also eine Handvoll an Methoden die namentlich immer dieselben sind (init, browse, control, trailer heißen die Methoden).

              Und die Default-Class heißt bei mir NotFound. Steht ein URL nicht in der statischen Routingtable, gibt es auch keine Klassenbindung. Von daher greift die Default Class.

              MfG

          2. Tach!

            Wobei ich es sowieso schon bedenklich finde, Namen von Klassen und Methoden über den URI zu schleifen. Besser ists, OOP eben konsequent zu nutzen, sowas gehört in die Konfiguration und zwar als Eigenschaft und nach draußen nicht sichtbar. Ansonsten zöge ja eine Änderung des Klassenname eine Änderung des URL mit sich, brr.

            Man ändert einen Klassennamen nicht ohne Grund, wenn sich daraus auch Teile der URL ergeben. Wenn man per Konvention routet (sprich wenn aus der URL per Regel der Controller- und Action-Name gebildet werden), dann benennt man seine Klassen passend, und dass Änderungen sich auf die URL auswirken ist dann auch gewollt. Sowas generell als schlecht abzutun, ohne einen konkreten Anwendungsfall zu betrachten, ergibt keinen Sinn. Zudem haben Router oft die Eigenschaft, neben dem Routen nach Konvention auch nach konfigurierten Einträgen routen zu können. Falls also der Klassename unabhängig von der URL geändert werden soll, setzt man da eine Konfiguration obendrauf und gut ist. Oder man ändert gleich die Strategie auf Routen nach explizit vergebenen Routen-Namen für Controller und Actions.

            Es sei denn auch man möchte Hacker dazu einladen, beliebige Methoden aufzurufen.

            Üblicherweise gestaltet man ein Framework so, dass die Controller von einer definierten Klasse abstammen und alle aufrufbaren Actions darin public sind. Alles andere wird vom Router nicht vermittelt. Da lässt sich also nicht beliebig im Framework herum Zeug aufrufen.

            dedlfix.

            1. Tach!

              Wobei ich es sowieso schon bedenklich finde, Namen von Klassen und Methoden über den URI zu schleifen. Besser ists, OOP eben konsequent zu nutzen, sowas gehört in die Konfiguration und zwar als Eigenschaft und nach draußen nicht sichtbar. Ansonsten zöge ja eine Änderung des Klassenname eine Änderung des URL mit sich, brr.

              Man ändert einen Klassennamen nicht ohne Grund, wenn sich daraus auch Teile der URL ergeben. Wenn man per Konvention routet (sprich wenn aus der URL per Regel der Controller- und Action-Name gebildet werden), dann benennt man seine Klassen passend, und dass Änderungen sich auf die URL auswirken ist dann auch gewollt. Sowas generell als schlecht abzutun, ohne einen konkreten Anwendungsfall zu betrachten, ergibt keinen Sinn.

              Es ist grottenschlecht, weil es zu sinnbefreiten Abhängigkeiten führt. Und es geht an OOP komplett vorbei. Wozu gibt es denn OOP wenn man sie nicht konsequent nutzt!?

              Zudem haben Router oft die Eigenschaft, neben dem Routen nach Konvention auch nach konfigurierten Einträgen routen zu können. Falls also der Klassename unabhängig von der URL geändert werden soll, setzt man da eine Konfiguration obendrauf und gut ist.

              Z.B. Das kann man aber auch gleich so machen: Klassenbindung und Eigenschaften.

              Üblicherweise gestaltet man ein Framework so, dass die Controller von einer definierten Klasse abstammen

              Wozu? Und was heißt hier üblich!? In meinem FW arbeite ich mit sog. Traits, die sind klassenunabhängig. Ein Trait, der Methoden des Interface definiert, wird erst infolge der Konfiguration einer bestimmten Klasse zugewiesen. Beispiel eine meiner Seiten:

              <!-- class: HTMLfile --> <!-- interface: date -->

              Die Klasse HTMLFile führt zum Boddy der auszugebenden Seite, hierzu wird die konfigurierte Eigenschaft file=home.html benutzt die den Pfad zum Template weist.

              Die Eigenschaft interface=date bindet den Trait. Da stecken die Methoden des FW Interface drin und auch der Controller ist eine solche Methode. Sind Parameter im Request, wird control() aufgerufen. Das ist bei allen URLs gleich. Wobei control() auch nur ausgeführt wird, wenn diese Methode definiert ist. Ist sie es nicht, kann der gebundene URL keine Benutzereingaben verabeiten.

              Ansonsten sind alle Methoden des FW Interface in der Lage, Platzhalter mit Daten zu befüllen. Interface date setzt z.B. das aktuelle Datum in einen Platzhalter.

              Du siehst also, hier gäbe es unendlich wiele Möglichkeiten Klassen sowie Traits an URLs zu binden, sozusagen eine n:n Beziehung. Konfigurierbar und unabhängig vom URL! Sowas macht Sinn!

              MfG

              1. Tach!

                Du siehst also, hier gäbe es unendlich wiele Möglichkeiten Klassen sowie Traits an URLs zu binden, sozusagen eine n:n Beziehung. Konfigurierbar und unabhängig vom URL! Sowas macht Sinn!

                Ich sehe da nichts, weil ich deine Ausführungen nicht verstehe. Vielleicht sind sie zu sehr Perl-belastet, wie die Dinge anderenorts nicht oder nicht in der Form oder nicht unter den genannten Begriffen vorliegen. Ich sehe aber, dass die Frameworks und die Konzepte dahinter, die ich kenne, problemlos ihre Arbeit verrichten, und auch von einer Vielzahl von Programmierern seit längerer Zeit verwendet werden. Wenn das grundlegend schlecht sein sollte, hätte es da schon die eine oder andere Revolution gegeben.

                dedlfix.

              2. Hallo pl,

                Es ist grottenschlecht, weil es zu sinnbefreiten Abhängigkeiten führt. Und es geht an OOP komplett vorbei. Wozu gibt es denn OOP wenn man sie nicht konsequent nutzt!?

                Du hast dich in deinem FW also für einen konfigurationsbasierten Ansatz entschieden. Und dafür, Framework- und Anwendungscode nicht per Vererbung, sondern über Traits zu koppeln. Zumindest verstehe ich das, was du schreibst, so. Das ist deine Entscheidung und funktioniert für dich, aber deswegen ist es noch keine Norm. Wie du selbst schreibst, führen viele Wege zur Lösung.

                Dynamische Traits sind in PHP oder .net mW nicht vorgesehen und müssten simuliert werden. In PHP über magic methods, in .net über dynamic Objekte. Beides langsam, daher wählt man deinen Ansatz dort nicht.

                Convention before Configuration mag dir suspekt sein, aber dieses Pattern spart dir viele Stunden Schreibarbeit in config files, und noch mehr Stunden Fehlersuche darin. Das Attribut „grottenschlecht“ sehe ich als dein Synonym für „ich mag das nicht“. Das steht dir frei. Es gibt andere Meinungen.

                Ob das OOP ist oder nicht, liegt meiner Ansicht nach auf einer ganz anderen Achse im Beurteilungsraum.

                Rolf

                -- sumpsi - posui - clusi
                1. hi @Rolf B

                  Es ist grottenschlecht, weil es zu sinnbefreiten Abhängigkeiten führt. Und es geht an OOP komplett vorbei. Wozu gibt es denn OOP wenn man sie nicht konsequent nutzt!?

                  Du hast dich in deinem FW also für einen konfigurationsbasierten Ansatz entschieden. Und dafür, Framework- und Anwendungscode nicht per Vererbung, sondern über Traits zu koppeln.

                  Nicht nur und nicht ausschließlich. Traits sind nur eine Art und Weise wie man Code organisiert. Also auch nichts Perlspezifisches. Genausowenig wie ein Interface etwas perlspezifisches ist. Und somit kann auch eine Klasse ein Interface implementieren, d.h. die zum IF gehörigen Methoden definiert dann die Klasse anstatt einen Trait zu binden.

                  Ob das OOP ist oder nicht, liegt meiner Ansicht nach auf einer ganz anderen Achse im Beurteilungsraum.

                  Es kommt darauf an OOP konsequent zu nutzen. Was mein FW auch tut. Genau deswegen gibt es ja OOP: Damit man mit Veränderungen besser zurechtkommt.

                  Das Attribut „grottenschlecht“ sehe ich als dein Synonym für „ich mag das nicht“.

                  Ich nicht, weil: Ich das nämlich auch begründen kann. Und auch begründet habe.

                  MfG

              3. Hallo pl,

                Du siehst also, hier gäbe es unendlich wiele Möglichkeiten Klassen sowie Traits an URLs zu binden, sozusagen eine n:n Beziehung.

                Don't do this. Mehrere Aktionen an eine URL zu binden ist ein Garant für schwer testbaren Code und schwer zu überblickende Seiteneffekte. Ein klassisches Antipattern.

                LG,
                CK

                -- https://wwwtech.de/about