hotti: CGI mit Ajax-Erweiterung (Design Patterns)

Hallo,

am Besten ein einfaches Beispiel: ein CGI hat zwei Eingabefelder, Faktor 1, Faktor 2, einen Submit-Button und auf Klick wird das Produkt berechnet. Das CGI hat eingangs eine Kontrollstruktur, in welcher die Parameter abgefragt werden, für einen reinen CGI-Betrieb wird der Parameter 'berechne' abgefragt und im Ergebnis dessen die Seite/Template mit den richtigen Zahlen befüllt, neu zum Browser gesendet (f1, f2, produkt werden in die Seite eingebaut).

Ajax-Erweiterung: Es kommt der Parameter 'x_berechne' hinzu, bei diesem Parameter wird auch das Ergebnis berechnet, aber anstelle einer kompletten Seite wird nur das Ergebnis (f1=2, f2=3, produkt=6) zum Browser gesendet und in die bereits vorhandene Seite per DOM eingebaut. Soweit sogut.

Was auffällt: Die Berechnung des Produkts (abstrakt: Verarbeitung der Eingabe) wird in beiden Fällen fällig. Und nicht nur das: In beiden Fällen landet das Ergebnis (f1, f2, produkt) kurzzeitig in einunddemselben Objekt. Es könnte also auch so gemacht werden, dass es nur den einen Parameter 'berechne' gibt, das Objekt jedoch ein Flag bekommt (ajax=1 || ajax=0).

In Summa, Entscheidung ob der Response:

  1. anhand der Namen der Parameter (berechne, x_berechne)
  2. anhand eines Attributes/Flag im Objekt, was die Methode zur Response ruft

Frage nach der Best Practice und einem passenden Entwurfsmuster.

Danke im Vorab,
Hotti

  1. Hi,

    In Summa, Entscheidung ob der Response:

    1. anhand der Namen der Parameter (berechne, x_berechne)
    2. anhand eines Attributes/Flag im Objekt, was die Methode zur Response ruft
    1. anhand eines zusätzlichen Request-Headers

    Frage nach der Best Practice und einem passenden Entwurfsmuster.

    1. oder 3).
    2. keinesfalls.

    MfG ChrisB

    --
    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    1. hi danke,

      In Summa, Entscheidung ob der Response:

      1. anhand der Namen der Parameter (berechne, x_berechne)
      2. anhand eines Attributes/Flag im Objekt, was die Methode zur Response ruft
      1. anhand eines zusätzlichen Request-Headers

      Ok, Header statt Parameter

      Frage nach der Best Practice und einem passenden Entwurfsmuster.

      1. oder 3).
      2. keinesfalls.

      Warum keinesfalls 1?

      Hotti

      1. hi,

        In Summa, Entscheidung ob der Response:

        1. anhand der Namen der Parameter (berechne, x_berechne)
        2. anhand eines Attributes/Flag im Objekt, was die Methode zur Response ruft
        1. anhand eines zusätzlichen Request-Headers

        Ok, Header statt Parameter

        Frage nach der Best Practice und einem passenden Entwurfsmuster.

        1. oder 3).
        2. keinesfalls.

        Warum keinesfalls 1?

        Meine Antwort in Perl ist mittlerweile fertig (komplettes Script untenstehend). Alle Methoden sind private in Klasse myCGI::Einmaleins, d.h., die Kontrolle der Parameter findet bereits bei der Objekterstellung im Konstruktor statt. So ergibt es sich fast 'wie von selbst', dass in der public method response() auf ein _Attribut_ des Objekts zurückgegriffen wird, ungeschickt wäre es, die Parameter/Header ein zweitesmal abzufragen, denn das ist bereits erledigt ;)

        Zum Testen habe ich nur mit Paramteren gearbeitet, danke für den Hinweis auf einen zusätzlichen Request-Header, damit ergeben sich weitere Möglichkeiten.

        @Matti: Du hast mail ;)

        Grüße an Alle,
        Hotti

          
        #!/usr/bin/perl  
        # CGI-Script mit Ajax  
        ###########################################################################  
        use strict;  
        use warnings;  
          
        my $u = myCGI::Einmaleins->new;  
        print $u->header, $u->response;  
          
        exit 0; # Fertig ist das CGI-Script  
        ###########################################################################  
        
        
        1. Meine Antwort in Perl ist mittlerweile fertig (komplettes Script untenstehend).

          Vorfreude!

          Alle Methoden [...] denn das ist bereits erledigt ;)

          Kopfschmerzen! Aber egal, Code sagt ja oft mehr als viele Worte.

            
          
          > #!/usr/bin/perl  
          > use strict;  
          > use warnings;  
          > my $u = myCGI::Einmaleins->new;  
          > print $u->header, $u->response;  
          > exit 0; # Fertig ist das CGI-Script  
          
          

          Das ist nicht Dein Ernst, oder? Bis auf die skurile Situation, dass myCGI hier nicht in den Namensraum geladen wird, kann man hier nichts erkennen, geschweige denn über best oder worst practice sinnieren.

          Wobei.. die Shebang-Zeile ist schön, strict und warnings löblich, exit prägnant!

          1. hi,

            Kopfschmerzen! Aber egal, Code sagt ja oft mehr als viele Worte.

            Oder auch nicht ;)

            #!/usr/bin/perl
            use strict;
            use warnings;
            my $u = myCGI::Einmaleins->new;
            print $u->header, $u->response;
            exit 0; # Fertig ist das CGI-Script

            
            > Das ist nicht Dein Ernst, oder? Bis auf die skurile Situation, dass myCGI hier nicht in den Namensraum geladen wird, kann man hier nichts erkennen, geschweige denn über best oder worst practice sinnieren.  
              
            In diesem Fall (Testscript) stehen die packages innerhalb derselben Datei und müssen nicht mit use ... eingebunden werden (sorry, hatte ich vergessen zu erwähnen).  
              
            
            >   
            > Wobei.. die Shebang-Zeile ist schön, strict und warnings löblich, exit prägnant!  
              
            Die Pragmatica strict und warnings mutieren zum Witz bei dem Code ;)  
              
            Btw., Du kannst das Script [hier testen](http://rolfrost.de/cgi-bin/templ.cgi)  
              
            Viele Grüße,  
            Hotti  
              
              
            
            
            1. Seufz,

              womit du eigentlich überhaupt einmal anfangen solltest, anstatt frisch gehörte Buzzwords zu verwursten, wäre folgendes: http://en.wikipedia.org/wiki/Separation_of_concern

              Ansonsten, Schuster, bleib bei deinen Leisten.

              Hth

              1. Moin,

                womit du eigentlich überhaupt einmal anfangen solltest, anstatt frisch gehörte Buzzwords zu verwursten, wäre folgendes: http://en.wikipedia.org/wiki/Separation_of_concern

                Ähm, was meinst du, was ich hier die ganze Zeit mache :D

                Ansonsten, Schuster, bleib bei deinen Leisten.

                Aber sicher: Perl!

                Hth

                Nein. Die Frage, welches Entwurfsmuster passt, ist immer noch offen.

                Hotti

                1. Nein. Die Frage, welches Entwurfsmuster passt, ist immer noch offen.

                  Zeig Code - dann können wir diskutieren!

                  1. Nein. Die Frage, welches Entwurfsmuster passt, ist immer noch offen.

                    Zeig Code - dann können wir diskutieren!

                    Gerne!

                    Das CGI-Script

                      
                    #!/usr/bin/perl  
                    use myCGI;  
                    use strict;  
                    use warnings;  
                    my $u = myCGI::Einmaleins->new;  
                    print $u->header, $u->response;  
                    # thats all  
                    
                    

                    Method response in Klasse myCGI::Einmaleins

                      
                    sub response{  
                    	my $self = shift;  
                      
                    	if($self->{AJAX}){  
                    		# gebe nur das Resultat aus  
                    		if(scalar @{$self->{ERR}}){  
                    			return join(",", @{$self->{ERR}});  
                    		}  
                    		else{  
                    			return $self->{TEMPLVAL}->{ergebnis};  
                    		}  
                    	}  
                    	else{  
                    		if(scalar @{$self->{ERR}}){  
                    			return sprintf "<p>%s</p>", join "</p><p>", @{$self->{ERR}};  
                    		}  
                    		else{  
                    			$self->{TEMPLVAL}->{action} = $ENV{SCRIPT_NAME};  
                      
                    			# Liste mit den Platzhaltern durchgehen  
                    			# wenn Werte anliegen, werden die Platzhalter ersetzt  
                    			foreach my $s(@{$self->{TEMPLIST}}){  
                    				$s = defined $self->{TEMPLVAL}->{$s} ? $self->{TEMPLVAL}->{$s} : '';  
                    			}  
                    			return sprintf $self->{TEMPLATE}, @{$self->{TEMPLIST}};  
                    		}  
                    	}  
                    }  
                    
                    
                    1. Nein. Die Frage, welches Entwurfsmuster passt, ist immer noch offen.

                      Zeig Code - dann können wir diskutieren!

                      Gerne! [...]

                      Die Kalkulation, New und Header fehlen... Vermutlich noch mehr...

                      1. Nein. Die Frage, welches Entwurfsmuster passt, ist immer noch offen.

                        Zeig Code - dann können wir diskutieren!

                        Gerne! [...]

                        Die Kalkulation, New und Header fehlen... Vermutlich noch mehr...

                        Suchfunktion "ajax" auf meiner HP, da steht der Code ;-)

                        --
                        Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
                    2. Hi,

                      if($self->{AJAX}){
                      # gebe nur das Resultat aus
                      }
                      else{
                      }
                      [/code]

                      Ich finde es nicht sinnvoll, serverseitig eine Fallunterscheidung zu machen, welche Client-Architekturen verwendet werden.

                      Du hast momentan zwei Ausgabemethoden implementiert, einmal das reine Ergebnis, einmal eine komplette HTML-Seite.

                      Ich empfahl dir, den Accept-Header zu verwenden. Der Header sagt aus, welche Ergebnis-Arten der Client verarbeiten kann. Fragt der Client mit z.B. "Accept: application/json" an, so weißt du, dass du JSON ausgeben willst (ein einzelner Wert wäre valides JSON). Du kannst XMLHttpRequest so einstellen, dass es den entsprechenden Header sendet.

                      Ein Browser fragt üblicherweise mit anderen Accept-Headern an. Dann lieferst du (entsprechend der Anfrage) etwas anderes aus (z.B. wird ein "Accept: text/html" üblicherweise die Ausgabekomponente für HTML auslösen).

                      Warum Accept und nicht als anderer Parameter?
                      Zum einen wäre die Fallunterscheidung über Accept die natürliche Weise, wie man es mit HTTP machen würde. Zum anderen willst du irgendwann noch andere Ausgabemethoden implementieren. Vielleicht willst du das Ergebnis mal als PDF.
                      Dann wäre es sinnvoller, anhand des Accept-Headers ein Template aufzurufen. Der Controller entscheidet anhand des Headers, welches Template aufgerufen wird. Dann implementierst du deine n Templates, dasjenige für die "AJAX"-Ausgabekomponente ist dann einfach nur das formatierte Ergebnis, also sehr elementar. Wenn weitere Ausgabemöglichkeiten hinzukommen, dann brauchst du nur die Templates dafür implementieren, und ansonsten nichts anpassen.

                      Bis die Tage,
                      Matti

                      1. hi Matti,

                        ja, HTTP einbeziehen, Header Accept. Das ist ein Request-Header der beim Server ankommt, ergo muss am Server entschieden werden, wie die Response auszusehen hat.

                        Es ist nur noch die Frage, an welcher Stelle diese Entscheidung gefällt wird. Vor wenigen Minuten postete ich die Variante "diese Entscheidung wird in der response()-method" getroffen.

                        Wenn ich zukünftig in meinem CMS das Protokoll ein bischen mehr mit einbeziehe (Request-Header) und nebenher auch mit Templates arbeite, könnte ich es auch so machen, dass es mehrere Templates gibt, Templates für die kompletten HTML-Seiten und Templates für JSON & co.

                        Meine zukünftige response()-Method wird dann nur noch das Template mit Werten bestücken und ausgeben. Die Entscheidung ob der Reponse wird dann an der Stelle getroffen, wenn es um die Frage geht, welches Template verwendet wird.

                        @Mitleser, sorry, hier muss ich erst noch bischen aufräumen, bevor es weitergeht. Die Frage nach dem Design Patterns stelle ich zurück ;-)

                        Hotti

                        --
                        Kurze Antwort: Egal. Lange Antwort: Scheißegal.
                      2. hi Matti,

                        Dann wäre es sinnvoller, anhand des Accept-Headers ein Template aufzurufen. Der Controller entscheidet anhand des Headers, welches Template aufgerufen wird. Dann implementierst du deine n Templates, dasjenige für die "AJAX"-Ausgabekomponente ist dann einfach nur das formatierte Ergebnis, also sehr elementar. Wenn weitere Ausgabemöglichkeiten hinzukommen, dann brauchst du nur die Templates dafür implementieren, und ansonsten nichts anpassen.

                        Habs getestet, wow! Das rockt ja richtig ab und schafft zukunftsträchtige Möglichkeiten ;)

                        Aber es kommt noch dicker. Die ganze http-accept-family wird demnächst in meinem Framework Einzug halten, u.a. auch accept-language und nun ists auch klar, an welcher Stelle die Auswahl des Templates erfolgt (Mehrsprachigkeit).

                        DAnke Euch allen, Schmatz!!!11

                        Hotti

                        1. Aber es kommt noch dicker. Die ganze http-accept-family wird demnächst in meinem Framework Einzug halten, u.a. auch accept-language und nun ists auch klar, an welcher Stelle die Auswahl des Templates erfolgt (Mehrsprachigkeit).

                          "Ganz dicke" wäre es, Templates sprachfrei zu halten (Variablen).

                          1. Aber es kommt noch dicker. Die ganze http-accept-family wird demnächst in meinem Framework Einzug halten, u.a. auch accept-language und nun ists auch klar, an welcher Stelle die Auswahl des Templates erfolgt (Mehrsprachigkeit).

                            "Ganz dicke" wäre es, Templates sprachfrei zu halten (Variablen).

                            Grundsätzlich ja. In meinem Framework wird es jedoch verschiedene Templates geben, Negative und Positive. Bei den Negativen sind die Variablen sprachfrei, das sind z.b. Eingabemasken für Datum/Uhrzeit/Zahlen o.ä., da ist die die Eingabemaske das Template und das wird es dann mehrsprachig geben. Bei den Positiven ist es genau andersherum ;)

                            Obwohl... (danke Rüdiger H.)

    2. Hi,

      1. anhand eines zusätzlichen Request-Headers

      Wobei der zusätzliche Header sinnvollerweise "Accept" ist.
       - ist der Accept-Header z.B. application/json (oder ein anderes Format, welches vom AJAX-Client verarbeitet wird), dann wird die "AJAX"-Ausgabekomponente aufgerufen
       - ansonsten wird das HTML ausgegeben

      Dann hätte man auch gleich einen kleinen REST-Service.

      Bis die Tage,
      Matti

      1. hi Matti,

        1. anhand eines zusätzlichen Request-Headers

        Wobei der zusätzliche Header sinnvollerweise "Accept" ist.

        • ist der Accept-Header z.B. application/json (oder ein anderes Format, welches vom AJAX-Client verarbeitet wird), dann wird die "AJAX"-Ausgabekomponente aufgerufen
        • ansonsten wird das HTML ausgegeben

        Ausgangsbasis/gegeben ist: Serverseitig wird ein Objekt erstellt. Dieses Objekt hat Methoden zum Parsen der Header und Methoden zum Parsen der Parameter, diese Methoden werden in der Anwendung explizit aufgerufen.

        Also in der Anwendung:

        • Objekt-Erstellung anhand des Request,
        • Kontrollstruktur, Aufruf verschiedener Methoden zum Parsen der Header oder Parsen der Parameter oder Beides,
        • Je nach Entscheidung aus der Kontrollstruktur heraus wird entweder die Methode zur Ausgabe von HTML oder die Methode zur Ausgabe von JSON gerufen.

        Um davon wegzukommen, könnte es auch so gemacht werden:

        In der Anwendung nur noch:

        • Objekt-Erstellung anhand des Request,
        • Objekt ruft Methode zur Ausgabe der Response.

        Dann hätten wir nur noch eine Public-Method, alle anderen Methoden sind privat.

        Dann hätte man auch gleich einen kleinen REST-Service.

        Könnte eine Public-Method sein.

        Hotti

  2. Hi!

    Frage nach der Best Practice und einem passenden Entwurfsmuster.

    Best Practice ist, Fachbegriffe richtig zu verwenden und nicht nach Gutdünken.

    am Besten ein einfaches Beispiel: ein CGI hat zwei Eingabefelder, Faktor 1, Faktor 2, einen Submit-Button und auf Klick wird das Produkt berechnet. Das CGI hat eingangs eine Kontrollstruktur, in welcher die Parameter abgefragt werden,

    CGI ist der Name einer Schnittstelle zwischen Webserver und Programmen, die den Request verarbeiten sollen. Es ist somit kein Ding, das Eingabefelder oder Kontrollstrukturen haben kann.

    für einen reinen CGI-Betrieb wird [...]
    Ajax-Erweiterung: [...]

    Wie glaubst du wohl, kommt der Ajax-Request zu deinem Perl-Script, wenn nicht ebenfalls über die CGI-Schnittstelle?

    Die Berechnung des Produkts (abstrakt: Verarbeitung der Eingabe) wird in beiden Fällen fällig. Und nicht nur das: In beiden Fällen landet das Ergebnis (f1, f2, produkt) kurzzeitig in einunddemselben Objekt. Es könnte also auch so gemacht werden, dass es nur den einen Parameter 'berechne' gibt, das Objekt jedoch ein Flag bekommt (ajax=1 || ajax=0).

    Je nach vertretbarem Aufwand kann man da verschiedene Wege gehen. Im einfachsten Fall kommt die Berechnung unabhängig von irgendwelchen Rahmenbedingungen des Requests in eine Funktion - Parameter rein, Ergebnis raus. Das Script unterscheidet je nach Gegebenheiten (Ajax oder nicht) ob Eingabedaten unterschiedlich entgegengenommen werden müssen und bringt sie gegebenenfalls für die weitere Verarbeitung - die ja in beiden Fällen gleich ist - auf einen gemeinsamen Nenner. Die Berechnung erfolgt in der Funktion. Anschließend wird wieder entschieden, welche Response zu generieren ist. - OOP braucht man dazu im Prinzip nicht, das Entwurfsmuster wäre schlicht und einfach EVA.

    Komplexer geht immer, beispielsweise über MVC - dann müsste aber die gesamte Webanwendung so aufgebaut sein, damit es sich lohnt. Ein Controller hat zwei Actions, eine für Ajax, eine für "normal". Die View für "normal" ist eine komplette HTML-Seite, für Ajax ist das Ergebnis so überschaubar klein, dass es vielleicht nicht mal eine eigene View braucht und die Controller-Action es generieren kann.

    Lo!