CGI mit Ajax-Erweiterung (Design Patterns)
hotti
- programmiertechnik
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:
Frage nach der Best Practice und einem passenden Entwurfsmuster.
Danke im Vorab,
Hotti
Hi,
In Summa, Entscheidung ob der Response:
- anhand der Namen der Parameter (berechne, x_berechne)
- anhand eines Attributes/Flag im Objekt, was die Methode zur Response ruft
Frage nach der Best Practice und einem passenden Entwurfsmuster.
MfG ChrisB
hi danke,
In Summa, Entscheidung ob der Response:
- anhand der Namen der Parameter (berechne, x_berechne)
- anhand eines Attributes/Flag im Objekt, was die Methode zur Response ruft
- anhand eines zusätzlichen Request-Headers
Ok, Header statt Parameter
Frage nach der Best Practice und einem passenden Entwurfsmuster.
- oder 3).
- keinesfalls.
Warum keinesfalls 1?
Hotti
hi,
In Summa, Entscheidung ob der Response:
- anhand der Namen der Parameter (berechne, x_berechne)
- anhand eines Attributes/Flag im Objekt, was die Methode zur Response ruft
- anhand eines zusätzlichen Request-Headers
Ok, Header statt Parameter
Frage nach der Best Practice und einem passenden Entwurfsmuster.
- oder 3).
- 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
###########################################################################
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!
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
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
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
Nein. Die Frage, welches Entwurfsmuster passt, ist immer noch offen.
Zeig Code - dann können wir diskutieren!
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}};
}
}
}
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...
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 ;-)
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
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
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
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).
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.)
Hi,
- 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
hi Matti,
- 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:
Um davon wegzukommen, könnte es auch so gemacht werden:
In der Anwendung nur noch:
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
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!