ImpressumFrank Seitz Wassermühlenstr. 2 25436 Uetersen E-Mail: fsfseitz.de Tel.: +49-176-78243503 Alle Artikel Inhaltsverzeichnis Rechtliche Hinweise Code auf GitHub Code auf meta::cpan KategorienAbonnieren |
Freitag, 20. August 2021Mojolicious: Webanwendung auf Entwicklungsrechner einrichtenWir entwickeln auf Basis von Mojolicious eine Webanwendung NAME und möchten, dass diese
Diese Anforderungen lassen sich leicht mit morbo als Webserver in Kombination mit der Prozesssteuerung systemd realisieren. Hierbei ist morbo der Entwicklungs-Webserver von Mojolicious und systemd die fundamentale Prozesssteuerung vieler Linux-Systeme (ein moderner Ersatz für init). Ein zusätzlicher Webserver wie nginx oder apache wird nicht benötigt. Die Unit-Datei NAME.service, mit der wir den Service gegenüber systemd definieren, ist recht einfach (Erläuterungen zur einzig interessanten Zeile ExecStart siehe im Folgenden): [Unit]
Description=DESCRIPTION
After=network.target
[Service]
Type=simple
User=USER
ExecStart=bash -lc "morbo PROGRAM --listen http://*:PORT --watch SOURCEDIR --verbose >LOGFILE.log 2>&1"
[Install]
WantedBy=multi-user.target
Hierbei ist:
Die systemd Unit-Datei kopieren wir in das Verzeichnis /etc/systemd/system. Mit folgender Kommandofolge machen wir die Anwendung dauerhaft und sofort verfügbar: # systemctl daemon-reload # Konfigurationsänderung systemd bekannt machen # systemctl enable NAME # Service aktivieren, so dass die Anwendung beim Booten gestartet wird # systemctl start NAME # Service sofort verfügbar machen (ohne Rebooten zu müssen) Den Status überprüfen wir mit: # systemctl status NAME Die Anwendung kann jederzeit gestoppt und gestartet werden mit: # systemctl stop NAME # systemctl start NAME Der automatische Start beim Booten lässt sich ab- und anschalten mit: # systemctl disable NAME # systemctl enable NAME Die Liste aller vorhandenen Unit-Files und ihres jeweiligen Status: # systemctl list-unit-files Vorschlag für eine Verzeichnisstruktur im Homeverzeichnis von USER: ~/etc/systemd/NAME.service # Unit-Datei, per Symlink referenziert von /etc/systemd/system aus, Qwner ist USER ~/var/log/NAME.log # Logdatei ~/opt/NAME/... # Projektverzeichnis Die konkrete Kommandozeile zum Starten der Anwendung lautet dann: morbo ~/opt/NAME/bin/PROGRAM --listen http://*:PORT --watch ~/opt/NAME --verbose >~/var/log/NAME.log 2>&1 nohupnohup morbo ~/opt/NAME/bin/PROGRAM --listen http://*:PORT --watch ~/opt/NAME --verbose >~/var/log/NAME.log 2>&1 & Links
Donnerstag, 12. November 2020Ajax: Cross-Origin Resource Sharing (CORS) implementierenAus Sicherheitsgründen lassen moderne Browser Ajax-Requests über Domaingrenzen hinweg nur dann zu, wenn die angefragte Resource die anfragende Domain "kennt". Ob dies der Fall ist, teilt die Resource dem Browser über den HTTP-Header Access-Control-Allow-Origin mit: Access-Control-Allow-Origin: <origin>
Liefert die angefragte Resource diesen Header nicht oder passt <origin> nicht zur anfragenden Seite, verwirft der Browser die Response. Firefox z.B. schreibt dann die Warnung ins Console Log Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ... mit dem Zusatz (Reason: CORS header 'Access-Control-Allow-Origin' missing) wenn der Header fehlt, oder (Reason: CORS header 'Access-Control-Allow-Origin' does not match '...') wenn <origin> nicht passt. Möchte die angefragte Resource Zugriffe von jeglichen Domains zulassen, liefert sie einfach Access-Control-Allow-Origin: *
Möchte sie den Zugriff von mehreren (aber nicht allen) Domains zulassen, muss sie <origin> dynamisch zur jeweils anfragenden Seite setzen, die ihre Origin unter dem Header Origin: sendet. LinksMontag, 9. November 2020Plotly.js: Plotten und analysieren einer Gruppe von ZeitreihenDie folgenden Diagramme zeigen Zeitreihen-Plots (am Beispiel von Umweltdaten) mit "Rangeslider" auf Basis des JavaScript Plot-Frameworks Plotly.js. Die Zeitreihen behandele ich als eine Einheit, wobei ich folgende Bedienlogik realisiert habe:
Donnerstag, 29. November 2018Apache: Bessere access.log-Datei definieren31.16.4.127 - - [15/Jul/2015:12:27:34 +0200] "GET /path/script.cgi?a=b HTTP/1.1" 302 583 "http://host.domain/page" "Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.1.0" Der Aufbau der access.log-Datei, die ein Apache HTTP-Server per Default schreibt, ist in mehrfacher Hinsicht suboptimal:
Aufgrund dieser Ungereimtheiten definiert man sich am besten ein eigenes Logfile-Format. Zum Glück ist dies mit der Apache-Direktive LogFormat leicht möglich. Ich verwende folgende Definition, mit einem senkrechten Strich als eindeutigem Feldtrenner: LogFormat "%{%Y-%m-%d %H:%M:%S}t|%h|%>s|%L|%D|%I|%O|%{Content-Type}o| %m|%v|%p|%U%q|%H|%{Referer}i|%{User-Agent}i" NAME Verwendete Formatelemente (die mit + gekennzeichneten Informationen kommen im ursprünglichen Format nicht vor): %{%Y-%m-%d %H:%M:%S}t Request-Zeitpunkt %h .................. Client-IP oder -Name %>s ................. finaler HTTP Status (200, ...) %L .................. + error.log wurde geschrieben %D .................. + Ausführungsdauer in Mikrosekunden %I .................. + Bytes empfangen %O .................. Bytes gesendet %{Content-Type}o .... + Content-Type Header der Response %m .................. Request-Methode (GET, POST, ...) %v .................. + Hostname %p .................. + Port %U .................. URL-Pfad %q .................. Query-String %H .................. Request-Protokoll (HTTP/1.0, ...) %{Referer}i ......... Referer Header des Requests %{User-Agent}i ...... User-Agent Header des Requests NAME ................ Name es Log-Formats Ein weiteres interessantes Formatelement ist %{COOKIE}C .......... + Wert des Cookie COOKIE das in Anwendungen, die Cookies nutzen, nützlich sein kann. Ich füge es bei Bedarf am Ende hinzu. Links: 2015-07-15 12:27:34|31.16.4.127|302|-|7273|400|583|text/html|GET|myhost.mydomain|80|/path/script.cgi?a=b|HTTP/1.1|http://host.domain/page|Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.1.0 Samstag, 14. Mai 2016Ein Plugin für Mojolicious schreibenÜber die Plugin-Schnittstelle kann Mojolicious oder eine Mojolicious-Applikation um jede denkbare Funktionalität erweitert werden. Die Plugin-Schnittstelle ist sehr einfach gehalten. Denn sie gibt nur vor, wie eine Funktionalität zum System hinzugefügt wird, nicht jedoch, um welche Art von Funktionalität es sich handelt. Die Implementierung eines Mojolicious-Plugin erfolgt in zwei Schritten:
In der Dokumentation zur Basisklasse Mojolicious::Plugin wird die Implementierung so beschrieben: package Mojolicious::Plugin::MyPlugin;
use Mojo::Base 'Mojolicious::Plugin';
sub register {
my ($self, $app, $conf) = @_;
# Magic here! :)
}
Der Aufwand der Plugin-Implementierung besteht natürlich darin, den mit # Magic here! :)
bezeichneten Teil mit Leben zu füllen. Ist das Plugin implementiert, wird es durch einen einzigen Aufruf zur Applikation hinzugefügt: $app->plugin(MyPlugin => \%config);
Der analoge Aufruf unter Mojolicious::Lite: plugin MyPlugin => \%config;
Hierbei ist %config ein Hash mit Schlüssel/Wert-Paaren - typischerweise als Hash-Literal angegeben - durch den das Plugin konfiguriert wird. Ist keine Konfigurierung des Plugin nötig, kann das Argument weggelassen werden. BeispielAls einfaches Beispiel implementieren wir ein Plugin Hello, das bei jedem hereinkommenden Request die Zeichenkette 'Hello' und die IP-Adresse des Aufrufers ins Log ausgibt. Dies erreichen wir, indem wir in der Methode register() einen before_routes-Handler aufsetzen, der genau dies tut. package Mojolicious::Plugin::Hello;
Mojo::Base 'Mojolicious::Plugin';
sub register {
my ($self, $app, $conf) = @_;
$app->hook(before_routes=>sub {
my $c = shift;
$c->app->log->debug('Hello '.$c->tx->remote_address);
});
return;
}
Das Plugin wird durch $app->plugin('Hello');
oder im Falle von Mojolicious::Lite durch plugin 'Hello';
in der Applikation aktiviert. Eine Konfiguration ist bei den Aufrufen nicht angegeben, da das Plugin keine Konfigurierungsmöglichkeit vorsieht. Links zu MojoliciousDienstag, 26. April 2016CSS: Header beliebiger Höhe am oberen Seitenrand fixierenHeader mit beliebigem Inhalt:
Header am Seitenanfang fixieren:
Am Seitenanfang Platz für den fixierten Header reservieren:
Demo: Sonntag, 24. April 2016jQuery UI: Accordion Panels per Ajax ladenDie jQuery UI Widgets Tabs und Accordion könnten - bis auf die Ausrichtung ihrer Reiter (Tabs horizontal, Accordion vertikal) - identisch sein. Sind sie aber nicht. Während das Tabs-Widget das Laden von Inhalten per Ajax direkt unterstützt, ist dies beim Accordion-Widget nicht vorgesehen. Es ist jedoch möglich, dies durch einen beforeActivate Event-Handler und bestimmte Einstellungen zu realisieren. Struktur des Accordion<div id="ID">
<h3><a href="URL">TITLE</a></h3>
<div></div>
...
</div>
ID ist die DOM-Id des Accordion. Der TITLE des Reiters wird in einen a-Tag eingefasst, dessen href-Attribut den URL definiert, von dem wir den Inhalt des Accordion-Panel per Ajax abrufen. Der div-Container für den Panel-Content ist leer, dieser wird per Ajax gefüllt. Instanziierung des Accordion$('#ID').accordion({
beforeActivate: function (event,ui) {
var url = ui.newHeader.find('a').attr('href');
if (url)
ui.newPanel.load(url);
},
active: false,
collapsible: true,
heightStyle: 'content'
});
Das Laden des Panel-Inhalts per Ajax realisiert der beforeActivate-Handler, den wir bei der Instanziierung des Accordion-Widget definieren (Zeilen 2-6). Wir nutzen den beforeActivate-Handler und nicht den activate-Handler, da er vor dem Öffnen des Reiters gerufen wird. D.h. zum Zeitpunkt des Öffnens ist der Inhalt bereits geladen, was einen flüssigen Ablauf ergibt. Die Setzungen active: false collapsible: true bewirken in dieser Kombination, dass zunächst alle Reiter geschlossen bleiben, denn das initiale Öffnen feuert nicht die beforeActivate- und activate-Events. Das initiale Öffnen realisieren wir durch das Auslösen eines Click-Event nach der Accordion-Instanziierung (s.u.). Die Setzung heightStyle: 'content' bewirkt, dass die Höhe des Panel automatisch an den geladenen Inhalt angepasst wird. Dies ist wichtig, da der Inhalt vorab nicht bekannt ist. Öffnen des ersten Accordion-Reiters$('#ID a:first').trigger('click');
Den ersten Accordion-Reiter öffnen wir durch das Auslösen eines Click-Event, so als hätte der Anwender auf den ersten Reiter geklickt. Auf diese Weise ist sichergestellt, dass der beforeActivate-Handler gerufen und damit der Inhalt geladen wird. Links: Montag, 21. März 2016DataTables: Hover Color definierenMit der Class Option hover kann beim jQuery Plug-In DataTables eingestellt werden, dass die Tabellen-Zeile unter der Maus hervorgehoben wird. Leider ist die Hervorhebung so schwach, dass sie in Kombination mit Class Option stripe auf den dunkleren Zeilen kaum sichtbar ist. Hier der CSS-Code, mit dem sich die Hover-Farbe ändern lässt: #ID.dataTable.hover tbody tr:hover, #ID.dataTable.display tbody tr:hover { background-color: COLOR; } Hierbei ist ID die DOM-Id der Tabelle und COLOR die gewünschte Farbe. Die Hover-Farbe ist per Default auf das sehr helle Grau #f6f6f6 eingestellt. Wählt man z.B. das dunklere #e8e8e8, ist die Hervorhebung deutlich erkennbar. Soll die Hover-Farbe für alle DataTables gelten: table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { background-color: COLOR; } LinksMittwoch, 16. März 2016DataTables: Filter-Suchfeld positionieren und gestaltenEin leistungsfähiges jQuery Plug-In für Tabellen ist DataTables. Es kann in vielfältiger Weise konfiguriert werden. Einige Anpassungen im Zusammenhang mit dem Filter-Suchfeld sind allerdings schlecht dokumentiert. Das Filter-Suchfeld ist per Default mit dem Label-Text "Search:" beschriftet und befindet sich rechts über der Tabelle. Hier eine kurze Beschreibung, wie dieses Setup geändert werden kann. Im folgenden Code stehen ID, LABEL und WIDTH für die frei wählbaren Angaben DOM-Id der Tabelle, Label-Text und CSS-Breite. Label-Text setzen (JS): $('#ID').DataTable({
...
'language': {
'search': 'LABEL'
}
});
Suchfeld links positionieren (CSS): #ID_filter {
float: left;
}
Suchfeld mittig positionieren (CSS): #ID_filter {
width: 100%;
text-align: center;
}
Größe des Suchfelds ändern (CSS): #ID_filter input[type="search"] {
width: WIDTH;
}
Eigenes Suchfeld definieren: <input type="text" id="searchField" ...>
$('#searchField').on('keyup',function () {
tab.search(this.value).draw();
});
Das von DataTables erzeugte Suchfeld unterdrücken: $('#ID').DataTable({
dom: 't', // nur die Tabelle selbst, ohne Suchfeld etc.
...
});
LinksDonnerstag, 17. September 2015CSS: Erstes Kind- bzw. Folgeelement gestaltenMitunter soll das erste Element eines HTML-Konstrukts per CSS speziell gestaltet werden, z.B. was dessen Außenabstände angeht. Diese Anforderung gibt es in zwei Ausprägungen:
Diese beiden Element-Anordnungen erfordern unterschiedliche CSS-Selektoren. 1. Element folgt auf Bezugselement<X>...</X>
<Y>
...
</Y>
...
Der CSS-Selektor lautet: X + * {
...
}
Bezugselement ist X. Der Selektor + selektiert das unmittelbar folgende Element. Der Universelle Selektor * füllt hier syntaktisch die zweite Argumentposition des Selektors und nimmt keine weitere Einschränkung vor. Beispiel: Jedes erste Element nach einer <h1>-Überschrift soll einen oberen Außenabstand von 0.5em erhalten: h1 + * {
margin-top: 0.5em;
}
2. Element ist Bezugselement untergeordnet<X>
<Y>
...
</Y>
</X>
Der CSS-Selektor lautet: X > *:first-child {
...
}
Bezugselement ist X. Der Selektor > selektiert alle Elemente, die dem Bezugselement direkt untergeordnet sind. Die Pseudoklasse *:first-child schränkt diese Menge auf das erste Kindelement ein (der Stern kann auch weggelassen werden). Beispiel: Jedes erste Unterelement <Y> eines <dd>-Definitionsabschnitts soll keinen oberen Außenabstand besitzen, sondern direkt an den Definitionsterminus <dt> anschließen: HTML: <dl>
<dt>...</dt>
<dd>
<Y>
...
</Y>
</dd>
...
</dl>
CSS: dd > *:first-child {
margin-top: 0;
}
Montag, 14. September 2015HTTP: Datei-Download mit NamensvorschlagEine Datei per HTTP-Response mit Dateinamens-Vorschlag FILE_NAME vom Server zum Client zu transferieren geht so: Per Location Redirection Location: URL Content-Disposition: attachment; filename="FILE_NAME" Per direkter Übertragung Content-Type: TYPE/SUBTYPE Content-Disposition: attachment; filename="FILE_NAME" FILE_CONTENT Die direkte Übertragung hat den Vorteil, dass die Datei nach dem Download serverseitig sofort gelöscht werden kann, falls sie nicht mehr gebraucht wird. Das ist bei einer Location Redirection nicht möglich, da der Client sie asynchron abruft. LinksFreitag, 3. Juli 2015Perl: Speedy (CGI-SpeedyCGI) unter Perl 5.10 und höher kompilierenDer schon etwas in die Jahre gekommene, aber für Webanwendungen immer noch hervorragende persistente Perl-Interpreter speedy kompiliert unter neueren Perl-Versionen nicht mehr. Der Versuch endet mit dem Fehler: speedy_perl.c: In function ?find_scr?: speedy_perl.c:258:24: error: expected expression before ?SpeedyScript? speedy_new(retval, 1, SpeedyScript); ^ ../src/speedy_backend_main.h:41:39: note: in definition of macro ?speedy_new? #define speedy_new(s,n,t) New(123,s,n,t) Ursache ist, dass das C-Makro New() aus dem Perl-CORE (CORE/handy.h), nicht mehr existiert. Dies ist offenbar seit Perl 5.10 der Fall. Die Lösung ist, anstelle des Makros New() das Makro Newx() zu benutzen. Hierzu muss in src/speedy_backend_main.h #define speedy_new(s,n,t) New(123,s,n,t) durch #define speedy_new(s,n,t) Newx(s,n,t) ersetzt werden. Dann kompilieren die Quellen fehlerfrei. Getestet unter Perl 5.20.2. Ein weiteres Problem tritt bei Perl 5.22.1 (mit gcc 5.3.1) auf: In file included from ../src/speedy_inc.h:90:0, from speedy.h:2, from speedy_backend_main.c:24: ../src/speedy_file.h:54:19: warning: inline function ?speedy_file_set_state? declared but never defined SPEEDY_INLINE int speedy_file_set_state(int new_state); ^ Dies lässt sich dadurch beheben, dass in src/speedy_inc.h die Macro-Definition SPEEDY_INLINE geändert wird zu #ifdef __GNUC__ #define SPEEDY_INLINE /* __inline__ */ #else #define SPEEDY_INLINE #endif Zum Testen (Perl 5.28.1) muss entgegen dem üblichen make test im Wurzelverzeichnis erst in das Unterverzeichnis speedy gewechselt werden: $ cd speedy $ make test Mittwoch, 24. Juni 2015Apache: Aufsetzen einer CGI WebapplikationDas Aufsetzen einer CGI-Applikation ist kein Zauberwerk. Es führen allerdings viele Wege nach Rom und es kann leicht passieren, dass ein "from scratch" erstelltes Setup überkomplex und krautig wird. Es folgt - in vier Schritten - das Setup, mit dem ich beim Erstellen von CGI-Applikationen starte. Es ist aufgeräumt und beruht auf technisch sauberen Konzepten. Von diesem Ausgangspunkt aus lässt sich eine beliebig umfangreiche Applikation aufbauen, ohne später den am Anfang gesteckten Rahmen verlassen zu müssen. 1. Applikationsstruktur im DateisystemJede nicht-triviale Applikation sollte sich im Dateisystem auf drei Bereiche verteilen. Wir wählen nach moderner Unix-Konvention hierfür eine /opt-Struktur:
<application> ist der Name der Applikation. /opt/<application>/ +-- ... +-- www/ +-- bin/index.cgi /etc/opt/<application>/ +-- ... +-- apache.conf /var/opt/<application>/ +-- ... +-- access.log +-- error.log Erläuterung:
2. CGI-Programm (index.cgi)Hier ein minimales CGI-Programm. Es gibt die IP-Adresse des Client aus. #!/usr/bin/perl -T use strict; use warnings; print <<"__HTTP__"; Content-Type: text/plain $ENV{'REMOTE_ADDR'} __HTTP__ # eof 3. Apache-Konfiguration (apache.conf)Die Apache-Konfigurationsdatei vereinbart einen VirtualHost und zwei Verzeichnisse: # Apache Config for <application> <VirtualHost *:80> ServerName [host].[domain] DocumentRoot /opt/[application]/www ScriptAlias /index.cgi /opt/[application]/bin/index.cgi RedirectMatch ^/$ /index.cgi ErrorLog /var/opt/[application]/error.log CustomLog /var/opt/[application]/access.log combined </VirtualHost> <Directory /opt/[application]/bin> Options ExecCGI Require all granted </Directory> <Directory /opt/[application]/www> Require all granted </Directory> # eof Erläuterung:
4. Kommandos (für Apache 2.0 und höher)Mit folgenden Kommandos wird das oben beschriebene Apache-Setup im Webserver aktiviert. Hierfür sind root-Rechte erforderlich. 1 - Apache-Konfiguration der Applikation verlinken (Debian): # ln -s /etc/<application>/apache.conf /etc/apache2/sites-available/<application>.conf 2 - Apache-Konfiguration der Applikation aktivieren: # a2ensite <application> 3 - Apache-Modul für CGI-Ausführung aktivieren: # a2enmod cgid 4 - Apache-Setup in den laufenden Server übernehmen (Debian): # service apache2 reload Variante: Verdecktes CGI-ProgrammWird in die VirtualHost-Definition anstelle von ScriptAlias /index.cgi /opt/<application>/bin/index.cgi RedirectMatch ^/$ /index.cgi die Definition RewriteEngine on RewriteRule ^/([A-Z0-9a-z/]+)$ /opt/<application>/bin/index.cgi/$1 [H=cgi-script] RedirectMatch ^/$ /<start> eingesetzt, tritt das CGI-Programm im URL nicht mehr in Erscheinung. Zusätzlich werden alle Pfade, die ausschließlich aus den Zeichen A-Z0-9a-z/ bestehen, via $PATH_INFO an das CGI-Programm übergeben. Die RewriteEngine muss zuvor verfügbar gemacht werden mit: # a2enmod rewrite Donnerstag, 11. Juni 2015Apache: Redirection auf SubdirectoryMit der Direktive Redirect kann ein Seitenzugriff auf eine andere Seite umgeleitet werden. Die Umleitung schließt alle Subpfade ein, also Seiten mit gleichem Pfadanfang. Möchte man ein Directory auf ein Subdirectory umleiten, klappt es mit Redirect nicht, da dies zu einer Endlos-Rekursion führt. Die Redirect-Regel Redirect /a /a/b führt bei Aufruf von /a zu der endlosen Kette von HTTP-Aufrufen /a /a/b /a/b/b /a/b/b/b ... bis der Browser dies erkennt und mit einer Meldung wie Redirect Loop: Redirection limit for this URL exceeded. abbricht. Die Lösung liefert die (leistungsfähigere) Direktive RedirectMatch. Mit dieser lässt sich der umzulenkende Pfad per Regex präzise eingrenzen: RedirectMatch /a$ /a/b Das Dollarzeichen verankert den Pfad /a am Ende des URL, so dass Subpfade nicht mehr matchen und die Rekursion unterbleibt. Da durch die Regel keine Subpfade weitergeleitet werden, müssen die Zugriffe innerhalb von /a/b relativ sein. Ist das Ausgangsdirectory das Root-Directory, muss auch der Anfang verankert werden, da jedes Verzeichnis auf / endet: RedirectMatch ^/$ /a Sonntag, 6. Juli 2014Bilder / Grafiken in HTML einbettenAnstelle eines URL können dem src-Attribut des <img>-Tag auch Bilddaten zugewiesen werden. Damit ist es möglich, Bilder direkt in HTML einzubetten. Die Syntax hierfür lautet: <img src="data:image/TYPE;base64,DATA" .../> Hierbei ist TYPE der Typ des Bildes (jpeg, png, gif, ...) und DATA sind die Base64-encodierten Bilddaten (s.u.). Dies ist für dynamisch generierte HTML-Seiten mit dynamisch generierten Grafiken, wie z.B. Plots, nützlich. Die Grafiken können so mit dem umgebenden HTML zusammen erzeugt und in einer einzigen (HTML-)Datei ausgeliefert werden. Unter Perl findet sich die Funktion für die Umwandlung von binären Daten nach Base64 im Core-Modul MIME::Base64: use MIME::Base64 (); ... $data = MIME::Base64::encode_base64($image,''); Die binären Bilddaten stehen hier auf $image. Der Leerstring als zweiter Parameter bewirkt, dass die Base64-Zeichenkette einzeilig erzeugt wird. Samstag, 22. Juni 2013Perl: Web Services mit SOAP::WSDL ansprechenDas Perl-Modul SOAP::WSDL stellt Mittel bereit, um Web Services ansprechen zu können, für die eine WSDL-Definition existiert. Der bevorzugte Weg ist, aus der WSDL-Definition eine Client-Schnittstelle zu generieren und diese zur Interaktion mit dem Web-Service zu nutzen. Die generierte Schnittstelle ist objektorientiert, besteht also aus einer Sammlung von Klassen. Die Schnittstelle wird von dem Programm wsdl2perl.pl generiert, das Bestandteil des Moduls SOAP::WSDL ist. Ein typischer Aufruf ist: $ wsdl2perl.pl -b DIR -p PREFIX URL DIR : Zielverzeichnis (Default: ".") PREFIX : Präfix für alle generierten Klasse (Default: "My") URL : URL der WSDL-Definition BeispielDer Web Service "Global Weather" ist ein einfacher Dienst, der aktuelle Wetterinformation über größere Städte der Welt liefert. Als Ausgangsinformation steht zur Verfügung:
Aus der formalen WSDL-Definition generieren wir mittels wsdl2perl.pl eine objektorientierte Client-Schnittstelle für Perl: $ wsdl2perl.pl -b lib -p GW:: http://www.webservicex.net/globalweather.asmx?wsdl Creating element class GW/Elements/GetWeather.pm Creating element class GW/Elements/GetWeatherResponse.pm Creating element class GW/Elements/GetCitiesByCountry.pm Creating element class GW/Elements/GetCitiesByCountryResponse.pm Creating element class GW/Elements/string.pm Creating typemap class GW/Typemaps/GlobalWeather.pm Creating interface class GW/Interfaces/GlobalWeather/GlobalWeatherSoap.pm Nun können wir einen Client programmieren, der das Wetter abfragt: #!/usr/bin/env perl
use strict;
use warnings;
use lib 'lib';
use GW::Interfaces::GlobalWeather::GlobalWeatherSoap;
if (@ARGV != 2) {
die "Usage: gw COUNTRY CITY\n";
}
my ($country,$city) = @ARGV;
my $soap = GW::Interfaces::GlobalWeather::GlobalWeatherSoap->new;
my $res = $soap->GetWeather({
CountryName=>$country,
CityName=>$city,
});
printf "%s\n",$res->get_GetWeatherResult;
Aufruf und Resultat: $ gw germany hamburg <?xml version="1.0" encoding="utf-16"?> <CurrentWeather> <Location>Hamburg-Finkenwerder, Germany (EDHI) 53-32N 009-50E 13M</Location> <Time>Jun 22, 2013 - 09:20 AM EDT / 2013.06.22 1320 UTC</Time> <Wind> from the SSW (200 degrees) at 15 MPH (13 KT) (direction variable):0</Wind> <Visibility> greater than 7 mile(s):0</Visibility> <SkyConditions> mostly cloudy</SkyConditions> <Temperature> 69 F (21 C)</Temperature> <DewPoint> 57 F (14 C)</DewPoint> <RelativeHumidity> 64%</RelativeHumidity> <Pressure> 29.85 in. Hg (1011 hPa)</Pressure> <Status>Success</Status> </CurrentWeather> Dienstag, 4. Juni 2013JavaScript: Existenz eines Bildes prüfenvar img = new Image(); img.onload = function () { // Bild existiert }; img.onerror = function () { // Bild existiert nicht }; img.src = URL; Freitag, 14. Oktober 2011Perl: Klassische Formularprogrammierung(dieser Eintrag wird fortgeführt) Die Eingabeelemente eines Web-Formulars empfangen ihre Werte idealerweise aus dem Request-Objekt. Drei Fälle sind zu unterscheiden:
In Perl:
$cgi ist das Request-Objekt. $action zeigt an, ob die Daten nach einem Submit an das Formular zurückgeliefert wurden. Die Formularwerte kommen dann aus den CGI-Parametern. $objId ist die Id des Modell-Objekts. Wenn $action nicht gesetzt ist, wird das Formular aus dessen Attributen initialisiert. Object ist die Modell-Klasse. Diese implementiert die Methode copyTo(), welche die Datensatz-Attribute auf das Request-Objekt kopiert. $db ist das Datenbank-Objekt, über das auf ddie Datenbank zugegriffen wird. @keyVal ist die Liste aus Schlüssel/Wert-Paaren für die Initialisierung mit den Defaultwerten. Freitag, 28. Januar 2011mod_perl: Eigenen Perl-Interpreter für Virtual HostPer Default wird bei mod_perl derselbe Perl-Interpreter für alle Virtual Hosts genutzt. Das kann zu Problemen führen, wenn die Applikationen unterschiedliche Versionen derselben Module nutzen. Dies kann bei mod_perl 2.0 mit der PerlOption +Clone ausgeschlossen werden: <VirtualHost ...> PerlOptions +Clone </VirtualHost> Die Option +Clone bewirkt, dass für den betreffenden Virtual Host ein eigener Interpreter-Pool genutzt wird. Dieser entsteht durch Klonen des Parent-Interpreters (welcher eventuell schon eine Startup-Initialisierung erfahren hat). Ein Interpreter-Pool mit einem gänzlich neuen Parent-Interpreter wird bei Angabe von +Parent erzeugt: <VirtualHost ...> PerlOptions +Parent </VirtualHost> Um dem Interpreter einen (oder mehrere) eigene Suchpfade mitzugeben, kann die Perl Standard-Option -I verwendet werden: PerlSwitches -I/var/www1/modules Dienstag, 4. Januar 2011Seamonkey installieren (Unix)Installation
Icon auf GNOME Panel legenMit der rechten Maustaste das Panel-Menü öffnen, "Add to panel" auswählen und einen "Custom Application Launcher" für das Executable anlegen. Das Seamonkey-Icon findet sich unter seamonkey/<VERSION>/chrome/icons/default/default.png Donnerstag, 28. Oktober 2010Cookies per JavaScript setzenServerseitig via HTMLWenn serverseitig kein Zugriff auf die Header der HTTP-Antwort besteht, kann ein Cookie auch per HTML gesetzt werden:
Zustand eines Select-Menü (per CGI.pm generiert) sichernEin onchange-Handler setzt einen Cookie, der den Zustand eines Select-Menü anwendungsglobal speichert (Perl/CGI.pm):
Dienstag, 26. Oktober 2010Javascript: HTML-Formular abbrechenEin HTML-Formular soll mittels Abbruch-Button beendet werden, d.h. der Button soll die Formulardaten nicht abschicken, sondern auf eine andere Seite URL verzweigen, z.B. die Seite, von der aus das Formular aufgerufen wurde. Implementierung: <input type="button" value="Abbrechen" onclick="location.href = 'URL'">
Donnerstag, 21. Oktober 2010Perl: Apache2::Reload installierenApache2::Reload ist ein Perl-Modul, das Module einer mod_perl-Applikation automatisch neu lädt, wenn diese geändert wurden. Andernfalls müsste der HTTP-Server neu gestartet werden um die Änderungen sichtbar zu machen, was während der Entwicklung umständlich ist und Zeit kostet.
Montag, 31. Mai 2010CSS: Listen portabel einrückenDie Browser benutzen unterschiedliche Wege, Listen einzurücken. Einige rücken per Padding ein, andere per Margin. Soll die Einrückung portabel verändert werden, müssen margin-left uns padding-left also zusammen gesetzt werden, und zwar eine Angabe auf die gewünschte Einrückung und die andere auf 0.
oder
Sonntag, 30. Mai 2010HTML-Konstrukte mit CSS gestaltenProblemEin aus mehreren Elementen bestehendes HTML-Konstrukt soll in seinem Aussehen frei gestaltbar sein. Wie lässt sich dies mit CSS erreichen? BeispielGegeben sei ein Inhaltsverzeichnis, bestehend aus einer Überschrift (h1) und verschachtelten Listen (ul, li) mit Links (a) auf die Dokument-Abschnitte.
Der HTML-Code wird ohne CSS vom Browser (Firefox) ungefähr so dargestellt (die Strukturelemente für den Titel (h1) und die Listen (ul) sind zur Verdeutlichung grau hinterlegt):
LösungUm ein HTML-Konstrukt aus mehreren Elementen per CSS anpassbar zu machen, gehen wir folgendermaßen vor:
Donnerstag, 28. Januar 2010Email-Adressen in HTML vor Spammern schützenIn Mailto-URLsStatt den Mailto-URL per href zu setzen, wird der mailto-URL zum Zeitpunkt des Klicks via JavaScript generiert. Nicht gut: <a href="mailto:rudi.ratlos@host.dom">...</a> Besser: <a href="#" onclick="this.href = 'mailto:rudi.ratlos'+'@'+'host.dom'">...<a/> Dies setzt natürlich JavaScript voraus. Aber schreibt heutzutage noch jemand Web-Anwendungen ohne JavaScript? Im TextStatt die Email-Adresse im Klartext hinzuschreiben, wird das @-Zeichen ausgetauscht: entweder durch ein HTML-Entity, eine Zeichenkette wie "<AT>" oder eine Grafik: Nicht gut: rudi.ratlos@host.dom Besser: rudi.ratlos@host.dom Noch besser, mit Text: rudi.ratlos<AT>host.dom Mit Grafik: rudi.ratlos<img src="at.png" alt="AT" ... />host.dom Bei einer Grafik besteht lediglich das Problem, dass diese vom Aussehen statisch ist, das Aussehen des umgebenden Textes u.U. nicht immer gleich ist, z.B. in unterschiedlichen Browsern oder durch Farbänderung beim Überfahren eines Link. Dienstag, 26. Januar 2010HTML: String in Entity-Schreibweise wandelnEine einfache (eventuell nicht besonders wirksame) Methode, um Email-Adressen in HTML-Seiten unkenntlich zu machen, um sie vor Spammern zu verbergen, ist, sie in Entity-Schreibweise zu wandeln. Der folgende Perl-Code wandelt einen beliebigen String in Entity-Schreibweise:
Beispiel: $ ./str-to-entity 'rudi.ratlos@...' rudi.ratlos @... Montag, 11. Januar 2010CSS: Text-Eingabefelder gestaltenEingabefeld<input type="text" id="e" name="text" size="20" />
Text:
UmrahmungRahmen von 1px Breite statt der normalen Dekoration. #e { border: 1px #999 solid; } HintergrundfarbeDie Hintergrundfarbe des Eingabefeldes ändern, wenn es mit der Maus überfahren wird (:hover) oder den Fokus bekommt (:focus). Besitzt das Feld weder den Fokus noch befindet sich die Maus darüber, wird die ursprüngliche Hintergrundfarbe automatisch wieder hergestellt. Dies braucht nicht vereinbart werden. #e:hover, #e:focus { background-color: #eee; } FontEin Text-Eingabefeld sollte m.E. normalerweise einen monospaced Font eingestellt haben, nur dann entspricht die optische Feldbreite exakt der Anzahl der Zeichen, die in das Feld passen. #e { font-family: monospace; } Sonntag, 10. Januar 2010CSS: Table Cellspacing unterdrückenIn HTML:
Per CSS (#t sei die Tabelle):
Samstag, 9. Januar 2010CSS: Dreispaltiges SeitenlayoutLayout mit Kopf, Fuss und drei Spalten:
Der HTML- und CSS-Code, der dies realisiert: <div id="header"> Header </div> <div id="left"> Left </div> <div id="right"> Right </div> <div id="middle"> Middle </div> <div id="footer"> Footer </div> #header { clear: both; } #left { float: left; width: 80px; } #right { float: right; width: 80px; } #middle { padding: 0 80px 0 80px; } #footer { clear: both; } Anmerkungen:
Freitag, 8. Januar 2010Validierung von HTML- und CSS-CodeDas W3C stellt zwei Services zur Validierung von HTML- und CSS-Code zur Verfügung:
Um den HTML- bzw. CSS-Code einer Seite direkt zu validieren, können folgende Links in die Seite eingebaut werden. Aus dem Referer-Header ermittelt der Validator die Ausgangsseite und ruft diese ab. Das geht natürlich nur, wenn die Seite öffentlich zugreifbar ist. Validierung von XHTML-Code<a href="http://validator.w3.org/check/referer?ss=1"> <img src="valid-xhtml10.png" width="88" height="31" alt="Valid XHTML 1.0!" /> </a> Der URL-Parameter ss=1 sorgt dafür, dass der Validator ein Source-Listing erzeugt. Dies ist sehr sinnvoll, da dann von einem Fehler aus direkt an die beanstandete Stelle gesprungen werden kann. Validierung von CSS2-Code<a href="http://jigsaw.w3.org/css-validator/check/referer"> <img src="valid-css2.png" width="88" height="31" alt="Valid CSS!" /> </a> Donnerstag, 7. Januar 2010Änderungen an SerendipityFavicon hinzugefügtZu Datei: templates/default/index.tpl Homelink2 auf Artikelübersicht geändertZu index.php?/archives/summary.html in Datei: templates/default/index.tpl Mittwoch, 6. Januar 2010Style der Serendipity Admin-Oberfläche ändernDie Admin-Oberfläche bietet keine getrennte Einstellung für den eigenen Style - entweder der Haupt-Style ändert den Style der Admin-Oberfläche mit oder es bleibt bei dem (für meinen Geschmack) nicht sonderlich schönen Default. Die Admin-Styles sind in den Verzeichnissen der Haupt-Styles in templates/STYLE/admin definiert. Die Haupt-Styles, die einen Admin-Style mitbringen, lassen sich folgendermaßen ermitteln: $ find templates -type d | grep admin$ ./carl_contest/admin ./competition/admin ./contest/admin ./bulletproof/admin ./default-rtl/admin ./default/admin Ein Style, der keinen Admin-Style definiert, kann mit einem fremden Admin-Style ausgestattet werden, indem ein fremdes admin Verzeichnis (eines anderen Style) dorthin kopiert wird oder ein Symlink auf dieses angelegt wird: $ cd templates/MYSTYLE $ ln -s ../OTHERSTYLE/admin . Sonntag, 3. Januar 2010Installation von SerendipityMeine Wahl der Blog-Software ist auf Serendipity gefallen, da ich es neulich positiv erwähnt gefunden habe und meine anschließende Recherche ergeben hat, dass es wohl tatsächlich gut ist. Als ersten technischen Eintrag schreibe ich auf, wie ich Serendipity from scratch auf meinem Debian Web-Host installiert habe. Zwar gibt es Serendipity auch fix und fertig als Debian-Paket, aber das ist in Debian/Stable schon älter. Außerdem ist diese Anwendung für mich wichtig genug, dass ich alles im Detail kontrollieren möchte, einschließlich programmierung, und keinesfalls will, dass die Debian-Paketverwaltung mir mit Updates dazwischen kommt. Homepage von Serendipity: http://www.s9y.org/ Schritt 1: Herunterladen der SoftwareAuf der Homepage bekommt man die aktuelle Version als Tarball und auch eine Beschreibung für die manuelle Installation. Diese hat den Titel Fresh Installation. Sie ist gut, aber in einigen Punkten nicht ausführlich genug. Ich beschreibe hier die Dinge, die ich bei meiner Installation dort nicht gefunden habe. Schritt 2: Auspacken des PaketsDas Paket, in meinen Fall serendipity-1.5.1.tar.gz, kann irgendwo im Dateisystem ausgepackt werden. In der Webserver-Konfiguration wird später mitgeteilt, wo es sich befindet. Ich entscheide mich als Installationsort für /opt/serendipity/1.5.1/. Schritt 3: Benötigte Debian-Pakete installieren# deb-install php5 # apt-get install postgresql # apt-get install php5-pgsql # apt-get install imagemagick Serendipity ist in PHP5 programmiert. Als DBMS verwende ich PostgreSQL. Imagemagick wird von Serendipity zur Bildbearbeitung gebraucht. Schritt 4: Apache KonfigurationIm Serendipity-Paket scheint keine Apache-Config dabei zu sein. Ich habe diese von der Debian-Version übernommen, mit der ich zuvor herumgespielt habe. Lediglich die Pfade brauchte ich anpassen. Der URL des Blog wird http://SERVER.DOMAIN/blog lauten. Alias /blog /opt/serendipity/1.5.1 <Directory /opt/serendipity/1.5.1> Options -Indexes +FollowSymlinks DirectoryIndex index.php <IfModule mod_php5.c> php_flag session.use_trans_sid off php_flag register_globals off </IfModule> AllowOverride All order allow,deny allow from all <Files *.tpl.php> deny from all </Files> <Files *.tpl> deny from all </Files> <Files *.sql> deny from all </Files> <Files *.inc.php> deny from all </Files> <Files *.db> deny from all </Files> </Directory> Die Datei wird als serendiptiy.conf nach /etc/apacha2/conf.d kopiert und anschließend der Apache neu gestartet: # apache2ctl restart Schritt 5: PostgreSQL-Datenbank einrichtenFür die Datenbank-Einrichtung sind drei Dinge zu tun: 1. Datenbank erzeugen 2. Datenbank-User erzeugen, über den Serendipity auf die Datenbank zugreift 3. Datenbanknamen und User und Passwort in die Serendipity-Config eintragen Datenbank und User erzeugen: # su - postgres $ createdb serendipity $ createuser -P serendipity Password: Wichtig ist bei creatuser der Parameter -P, damit der User ein Passwort erhält. Schritt 6: Konfiguration und Administration von SerendipityAnschließend auf http://SERVER.DOMAIN/blog/serendipity_admin.php gehen und die Datenbank-Angaben in die Formularfelder eintragen Bei Anmeldeaufforderung per "John Doe" mit Passwort "john" anmelden. Benutzername und Passwort sollten nach der Anmeldung als erstes geändert werden. Danach kann Serendipity im Detail den eigenen Wünschen angepasst werden.
(Seite 1 von 1, insgesamt 34 Einträge)
|
Kalender
StatistikLetzter Artikel:
08.07.2024 21:11 157 Artikel insgesamt
Links
|