Uri: node.js express REST show-function

Hallo,

ich möchte mit Express eine RESTful APP zum anlegen von Gebäuden bauen

meine Routen sind bisher

/buildings

/buildings/create

/buildings/store

soweit funktioniert es auch.

jetzt will ich eine funktion show schreiben mit der route:

/buildings/{id}

Habe aber keine idee wie das auf der controllerseite gehen soll und weiß auch nicht wirklich wonach ich suchen soll.

Wäre toll, wenn ihr mir helfen könntet.

Danke

Uri

  1. Tach!

    jetzt will ich eine funktion show schreiben mit der route:
    /buildings/{id}

    Habe aber keine idee wie das gehen soll und weiß auch nicht wirklich wonach ich suchen soll.

    Woran scheitert es denn, am Formulieren des richtigen Musters gemäß Dokumentation oder daran, dass die Route nicht angesprungen wird, oder ...?

    dedlfix.

    1. ich weiß nicht wie ich eine unbestimmte anzahl an routen für je eine ID machen soll.

      1. Tach!

        ich weiß nicht wie ich eine unbestimmte anzahl an routen für je eine ID machen soll.

        Gar nicht so viele. Du definierst lediglich eine einzelne Route, die aber mit Platzhalter für die ID. Das muss in der Dokumentation beschrieben stehen, wie man das notiert, und auch wie man dann im aufgerufenen Code den Parameter entgegennimmt.

        dedlfix.

  2. Lieber Uri,

    /buildings

    /buildings/create

    /buildings/store

    ist das sinnvoll?

    /buildings/{id}

    Aha! Logisch?

    Warum nicht so?

    /buildings
    /buildings/{id|new}
    /buildings/{id|new}/edit
    /buildings/{id|new}/store
    

    Der Pfad /buildings/{id|new}/create scheint mir dann unnötig. /buildings/{id|new} führt dann sinnvollerweise automatisch zu /buildings/{id|new}/edit.

    Liebe Grüße,

    Felix Riesterer.

    1. Tach!

      /buildings
      /buildings/create
      /buildings/store

      ist das sinnvoll?

      Warum nicht? Ohne den Anwendungsfall zu kennen, kann man das nur schlecht einer Sinnhaftigkeitsbewertung unterziehen.

      /buildings/{id}

      Aha! Logisch?

      Da muss nicht unbedingt eine Logik dahinterstecken. Üblicherweise klickt man sich durch eine Website, ohne die URL großartig zu beachten oder ihr eine großartige Bedeutung zuzumessen. Der Aufbau des Pfades spielt also meist keine übermäßige Rolle. Wichtig ist (aus technischer Sicht) im Prinzip nur, dass der Router sie auseinanderhalten kann.

      Warum nicht so?

      /buildings
      /buildings/{id|new}
      /buildings/{id|new}/edit
      /buildings/{id|new}/store
      

      Ist store denn auf ein einzelnes Element bezogen? Vermutlich hat das nichts mit Speichern zu tun, sondern mit einem Store, in dem mehrere Dinge zu sehen sind.

      Der Pfad /buildings/{id|new}/create scheint mir dann unnötig. /buildings/{id|new} führt dann sinnvollerweise automatisch zu /buildings/{id|new}/edit.

      Bei einem Create ist die ID in der Regel noch nicht bekannt. Stattdessen new als Wort zu verwenden macht es nicht besser. Die Kombination von new und edit ergibt auch keinen gesteigerten Sinn.


      Meine Vorgehensweise für CRUD

      /thing/ für eine Liste
      /thing/:id für das Edit-Formular
      /thing/add für das Edit-Formular für neue Elemente

      Detail-Ansicht ist keine eigenen Seite sondern ein modaler Dialog, und Löschen ist auch nur ein Ajax-Request aus der Listenansicht. Diese beiden brauchen also keinen eigenen Pfad. Anwendungsfall ist hier eine Angular-SPA ohne Ohne-Javascript-Fallback. Gegebenenfalls gibt es weitere Aktionen, die sind dann à la /thing/foo und /thing/bar hinzugefügt.

      dedlfix.

      1. Danke für die antworten erstmal. Die App dient eher zum lernen und ich möchte es erstmal überhaupt hinkriegen. Um design kann ich mir dann Gedanken machen, wenn ich weiß, wie man diese requests verarbeitet.

        
        app.get("/buildings/:id", function(req, res){
          console.log(req.param.id)     //undefined
          res.sendFile("show.html", {root: __dirname + '/views'}); 
        });
        
        

        meine funktion sieht jetzt so aus und wenn ich auf die url /buildings/1 gehe wird die funktion auch aufgerufen, aber wie hole ich die daten für die Detailansicht her? Mache ich eine verschachtelte get funktion mit einer anderen URL und dann in show.html per ajax auf die andere URL zugreifen?

        Gruß und Danke Uri

        1. Tach!

          app.get("/buildings/:id", function(req, res){
            console.log(req.param.id)     //undefined
            res.sendFile("show.html", {root: __dirname + '/views'}); 
          });
          

          meine funktion sieht jetzt so aus und wenn ich auf die url /buildings/1 gehe wird die funktion auch aufgerufen, aber wie hole ich die daten für die Detailansicht her?

          Ich nehme mal an, in show.html ist der eine oder andere Platzhalter für die Daten. Die müssten vor dem Senden mit den eigentlichen Daten ersetzt werden.

          dedlfix.

          1. Was genau meinst du mit Platzhalter? Für index habe ich eine funktion für View und eine für die Daten und in index.html hole ich die Daten aus /buildings_data mit ajax.

            app.get("/buildings", function(req, res){
              res.sendFile("index.html", {root: __dirname + '/views'}); 
            });
            
            
            app.get("/buildings_data", function(req, res) {
              var connection = mysql.createConnection({
                  host     : 'localhost',
                  user     : 'root',
                  password : '' 
                });
                connection.connect();
                var query = "SELECT * FROM re_manager.Gebaeude;"
                connection.query( query, function(err, rows, fields) {
                  if (err) throw err;
                  res.json({Gebaeuden:rows});
                });
                connection.end();
            });
            

            Mein problem ist, dass ich diese Vorgehensweise nicht auf die einzelnen Gebäuden umsetzen kann. Aber vielleicht wirds auch nicht so gemacht, ich weiß es nicht. Gibt es einen besseren weg?

            1. Tach!

              Was genau meinst du mit Platzhalter?

              Nun, ich nahm an, dass die show.html ein Template sein könnte. Du musst ja die eigentlichen Werte in eine HTML-Struktur einbetten. Bei klassischen Anwendungen, die ohne Ajax arbeiten, setzt man serverseitig die HTML-Struktur und die Daten aus dem DBMS zusammen, und liefert das als Gesamtpaket an den Browser. Template-Systeme können dabei helfen, und die arbeiten so, dass eine allgemein gehaltene HTML-Vorlage mit Platzhaltern existiert, und an diese Platzhalter die Daten des konkreten Datensatzes geschrieben werden.

              Für index habe ich eine funktion für View und eine für die Daten und in index.html hole ich die Daten aus /buildings_data mit ajax.

              Mein problem ist, dass ich diese Vorgehensweise nicht auf die einzelnen Gebäuden umsetzen kann.

              Warum nicht? Prinzipiell geht das da auch. Der Teil, der das Ajax mit dem Abruf der Daten startet muss nur auf irgendeine Weise die ID-Information bekommen, damit man mit der die passenden Datensatz vom Server abfragen kann.

              Du kannst die show.html vor dem Ausliefern so verändern, dass darin die ID zu liegen kommt, so dass das Javascript darauf zugreifen kann. Andererseits kann man sie auch mit Javascript aus der URL extrahieren.

              Gibt es einen besseren weg?

              Der klassische mit dem Zusammenbau auf der Serverseite ist nach wie vor möglich und spart auch Laufzeit, weil nur ein Request-Response statffindet statt zwei einzelnen nach Seite und Inhalt.

              dedlfix.

              1. Also die Klassische weise macht sinn. Mir ging ja schon zuvor durch den Kopf "Was, wenn javascript im Browser deaktiviert ist?...".

                Leider habe ich kein plan wie das geht und wonach ich suchen soll. In php mach ich im HTML dokument einen php-Tag auf und schreib da meine PHP. Wie funktioniert das mit express?

                1. Tach!

                  In php mach ich im HTML dokument einen php-Tag auf und schreib da meine PHP. Wie funktioniert das mit express?

                  Andersrum. Du erzeugst mit Code die Ausgabe, beziehungsweise den String, der den Inhalt der Response darstellt. Ein Hilfsmittel ist dabei die in modernem Javascript vorhandene Template-Syntax für Ersetzungen in kleinerem Stil. Für größere Vorhaben solltest du nach Template Engines Ausschau halten. Da kann ich dir aber mangels Wissens keine empfehlen.

                  dedlfix.

                  1. ist da pug das richtige Stichwort?

                    1. Tach!

                      ist da pug das richtige Stichwort?

                      „Ein Leben ohne Mops ist möglich, aber sinnlos.“

                      Wenn du pugjs.org meinst, das ist mal wieder eine schöne Seite, wie man seine neuen Besucher nicht begrüßen sollte. Keinerlei einleitender Satz, worum es bei dem Projekt geht.

                      Naja, wenigstens auf Github haben sie es hingeschrieben: "Pug is a high-performance template engine heavily influenced by Haml and implemented with JavaScript for Node.js and browsers."

                      </rant>

                      Sieht jedenfalls nach Template-Engine aus.

                      dedlfix.

                      1. Hallo

                        „Ein Leben ohne Mops ist möglich,

                        Plural!

                        Gruß
                        Kalk

                        1. Hallo Tabellenkalk,

                          „Ein Leben ohne Mops ist möglich,

                          Plural!

                          Leben ohne Mops sind möglich?

                          Bis demnächst
                          Matthias

                          --
                          Rosen sind rot.
                          1. Hallo,

                            Plural!

                            Leben ohne Mops sind möglich?

                            Hat zwar 8 Stunden gedauert, aber du hast mich nicht enttäuscht! 😎

                            Gruß
                            Kalk

                      2. Ok, vielen Dank. Hab mich da eingelesen und es soweit hinbekommen die seite mit pug zu bauen und habe da statt zwei requests nur einen einzigen request.

                        app.get("/buildings", function(req, res){
                          var buildings=[];
                          var connection = mysql.createConnection({
                            host     : 'localhost',
                            user     : 'root',
                            password : '' 
                          });
                          connection.connect();
                          var query = "SELECT * FROM re_manager.Gebaeude;"
                          connection.query( query, function(err, rows, fields) {
                            if (err) throw err;
                            for(var i=0; i<rows.length; i++){
                              var building=
                                {
                                  "ID":rows[i].ID,
                                  "gesamte_Wohnflaeche":rows[i].gesamte_Wohnflaeche,
                                  "Baujahr":rows[i].Baujahr,
                                  "Strasse":rows[i].Strasse,
                                  "Strassennummer":rows[i].Strassennummer,
                                  "Postleitzahl":rows[i].Postleitzahl,
                                  "Stadt":rows[i].Stadt
                                }
                              buildings.push(building);
                            }
                          });
                          res.render('index', {"buildings":buildings});
                          connection.end();
                        });
                        

                        wenn ich im Template jedoch console.log(buildings) rufe, wird auf der konsole ein leeres String angezeigt, was einerseits gut ist, weil ich den gewünschten array einerseits im template aufrufen kann, andererseits schlecht ist, weil er leer ist. Liegt es daran, dass node asynchron ist und wie kann man das umgehen?

                        Danke und Gruß

                        Uri

                        1. Tach!

                          wenn ich im Template jedoch console.log(buildings) rufe, wird auf der konsole ein leeres String angezeigt, was einerseits gut ist, weil ich den gewünschten array einerseits im template aufrufen kann, andererseits schlecht ist, weil er leer ist. Liegt es daran, dass node asynchron ist und wie kann man das umgehen?

                          Nicht node an sich, sondern in diesem Fall das connection.query(). Das liefert sein Ergebnis erst wenn es die Callback-Funktion aufruft. Alles was von dem Ergebnis abhängig ist, darf erst und muss in diesem Callback stattfinden, oder in davon aufgerufenen Funktionen. Wenn etwas nach dem connection.query() steht, wird das sofort ausgeführt, ohne dass die Query abgearbeitet ist und deren Ergebnis zur Verfügung steht.

                          dedlfix.

                          1. Super, es funktioniert. Und get /buildings/:id geht auch mit nur einer einzigen Funktion. Danke nochmal.

                            Gruß

                            uri

    2. Hi Felix,

      Warum nicht so?

      /buildings
      /buildings/{id|new}
      /buildings/{id|new}/edit
      /buildings/{id|new}/store
      

      REST bildet die Aktionen auf Request-Methoden ab. Eine neue Ressource wird z.B. mit PUT angelegt. Also nicht mit new im URL. MfG

      1. Hallo pl,

        fast richtig. Zum Neuanlegen verwendet man POST. Siehe RFC 7231, da steht eindeutig:

        POST is used for the following functions (among others):
        (...)
        Creating a new resource that has yet to be identified by the origin server
        .

        Damit bleibt für PUT das Ersetzen, also:

        GET     Index Abrufen    /buildings
        GET     Einzeln abrufen  /buildings/id
        POST    Neu anlegen      /buildings    - mit Gebäudedaten im Body
        PUT     Ändern           /buildings/id - mit Gebäudedaten im Body
        DELETE  Entfernen        /buildings/id
        

        Die ID beim POST vergibt der Server und liefert sie in der Response zurück. Die Trennung POST/PUT ist nicht zwingend nötig, man kann an der Existenz einer ID erkennen ob ein Satz neu angelegt oder ersetzt werden soll. Es gehört aber zu REST dazu, die CRUD-Operationen auf die Verben zu verteilen.

        Rolf

        --
        sumpsi - posui - clusi
        1. hi,

          fast richtig. Zum Neuanlegen verwendet man POST. Siehe RFC 7231, da steht eindeutig:

          Nun, ob PUT oder POST, irgendwie ist das sowieso in sich widersprüchlich: Wie kann man eine Ressource requesten die es gar nicht gibt!?

          Also ich finde, man sollte sich bei Webservices nicht an den Requestmethoden festkrallen, sondern bspw. seine serverseitigen Parser auf weitere Content-Types erweitern. In dem WS die ich für mein Content-Management nutze und überhaupt in meinen Anwendungen hat es sich auch ganz gut bewährt, Schlüsselparameter von Datenparametern strikt zu trennen. So könnte man Schlüsselparameter im querystring unterbringen und Daten im Messagebody. application/body+query haben ich diesen Content-Type genannt und das ist nur ein Beispiel. MfG

          1. Tach!

            Nun, ob PUT oder POST, irgendwie ist das sowieso in sich widersprüchlich: Wie kann man eine Ressource requesten die es gar nicht gibt!?

            Natürlich kann man Dinge erfragen, die es nicht gibt und die dann in dem Moment individuell angefertig werden. Das macht doch (d)ein CMS auch nicht anders, solange es nicht fertige Seiten aus einem Cache holt.

            Also ich finde, man sollte sich bei Webservices nicht an den Requestmethoden festkrallen,

            Ich finde, man sollte bei einer REST-Schnittstelle genau das tun. REST ist ein Standard oder zumindest ein Quasi-Standard. Bei Abweichungen von diesem Standard kann man sonst keine REST-Tools out of the box verwenden.

            dedlfix.

            1. Tach!

              Nun, ob PUT oder POST, irgendwie ist das sowieso in sich widersprüchlich: Wie kann man eine Ressource requesten die es gar nicht gibt!?

              Natürlich kann man Dinge erfragen, die es nicht gibt und die dann in dem Moment individuell angefertig werden. Das macht doch (d)ein CMS auch nicht anders, solange es nicht fertige Seiten aus einem Cache holt.

              Grundsätzlich ja. Da mein FW URLs an Klassen bindet, gibt es eine Default-Klasse was dieser Anforderung Genüge tut. Diese Klasse heißt NotFound und da steht im Namen drin, was die macht.

              Unabhängig davon ist Not Found ja auch ein Fall für den Webserver. So oder so ist es fragwürdig, das Anlegen einer neuen Resource über Status 404 abzuwickeln.

              Aber REST ist ja auch nicht der Weisheit letzter Schluss. MfG

            2. Tach!

              Nun, ob PUT oder POST, irgendwie ist das sowieso in sich widersprüchlich: Wie kann man eine Ressource requesten die es gar nicht gibt!?

              Natürlich kann man Dinge erfragen, die es nicht gibt und die dann in dem Moment individuell angefertig werden. Das macht doch (d)ein CMS auch nicht anders, solange es nicht fertige Seiten aus einem Cache holt.

              Doch, macht es. Beim meinem CMS wird ein neuer Inhalt an eine Ressource (Webservice) gesendet die es mit Sicherheit schon gibt. Anhand des gesendeten Content-Types weiß die Frameworkinstanz wie die Daten zu parsen sind, erhält body, title, descr usw., macht diese Daten persistent und schickt bei einer fehlerfreien Ausführung den Status 200 OK.

              Also ich finde, man sollte sich bei Webservices nicht an den Requestmethoden festkrallen,

              Für die Datenübertragung ist die Requestmethode völlig egal, die kannst Du auch Zitrone nennen. Entscheidend dafür, daß serverseitig Daten in STDIN anstehen ist nicht etwa POST oder PUT sondern die CGI-Ungebungsvariable Content-Length und das kann man in einschlägigen FRCs nachlesen.

              Man kann sogar mit Request Methode GET einen Message Body senden und mit der neuen FetchAPI kann man den Namen der Requestmethode beliebig frei wählen.

              Ich finde, man sollte bei einer REST-Schnittstelle genau das tun. REST ist ein Standard oder zumindest ein Quasi-Standard. Bei Abweichungen von diesem Standard kann man sonst keine REST-Tools out of the box verwenden.

              Ich finde, wenn man schon ein Framework hat, daß man damit Requests für Webservices genauso behandelt wie jeden anderen Request auch. D.h. u.a. daß auf einer konfigurierten Webressource eine Instanz erstellt wird und so wie ich das hier sehen kann, passiert das auch in node.js wo man in der zur Ressource gehörigen Methode je eine Request- und eine Responseinstanz bekommt. Das ist in meinem FW ganz genauso nur halt anders umgesetzt.

              Schönen Sonntag.