[ zurück ]

Datenbankabfragen mittels ISAPI


Die wohl am meisten benötigte Zielrichtung aktiver Servererweiterungen sind Datenbankanbindungen im Web. Hier spielt auch Delphi seine Stärken aus. Eine Webabfrage an eine Datenbank ist mit dem ISAPI-Experten hot schnell programmiert. Und der Leistungsfähigkeit sind kaum Grenzen gestellt.

Eine Datenbankabfrage kann als Ergebnis eine Textantwort, meist in Tabellenform liefern. Aber auch grafische Ausgaben beispielsweise als Chart sind denkbar. Als erstes Projekt soll hier eine Suchanfrage an eine Telefonverzeichnis programmiert werden. Die DLL "DbfQuery" wird mittels des Experten als Content-Typ "text/html" generiert. Innerhalb der Prozedur IsapiAction realisieren Sie im Einzelnen folgende Details:

Zuerst separieren Sie die Anfrageparameter. Die Servererweiterung soll sowohl die Abfragemethode "POST" aus einem Suchformular heraus akzeptieren als auch mit Anfragen vom Typ "GET" aus der Adreßzeile des Browsers zurechtkommen.

 if AnsiUpperCase(StrPas(ECBMethod))='POST' then begin
  Liste:=TStringList.Create;
  GetContentData(Liste);
  sQuery:=Liste.Values['Name'];
  Liste.Free;
 end
 else sQuery:=ECBQueryString;               // bei GET

Danach erzeugen Sie den Kopf der Antwortseite. Er soll noch unabhängig vom Ergebnis der Suchanfrage sein. Zur Vereinfachung bedienen Sie sich der schon in den vorangegangenen Projekten beschrieben Hilfsroutinen aus der Sammlung "IsapiHlp.PAS". Damit erzeugen Sie hier bequem den gesamten Seitenvorspann unter Angabe von Seitentitel, Hintergrundfarbe und eventuell Hintergrundbild.

 s:=HtmlHeader('Abfrage Telefonverzeichnis','FFFFCC','')
    +'<H1>Abfrageergebnis</H1>'
    +'Sie haben nach <B>'+ sQuery +'</B> gesucht.';

Eine alternative Variante ist es, das prinzipielle Aussehen der Antwortseite in einer externen Vorlagendatei bereitzustellen. Diese müßte Ihr Programm an dieser Stelle laden und die variablen Suchergebnisse in den Mustertext hineinschreiben. Ein solches Vorgehen hat den Vorteil, daß die Ergebnisseite in gewissen Grenzen verändert werden kann, ohne die DLL neu kompilieren zu müssen. Andererseits stellt der Zugriff auf die separate Vorlagendatei eine zusätzliche Belastung und mögliche Fehlerquelle für das Serverscript dar.

Dann beginnt im Kern die Datenbankarbeit. In diesem simplen Beispiel soll per BDE auf eine auf dem Server verfügbare dBASE-Tabelle zugegriffen werden. In eigenen Projekten können Sie selbstverständlich mit Ihrer gewohnten Datenbankumgebung z.B. Interbase-Server oder MS-SQL-Server arbeiten.

Da hierbei Laufzeitfehler nie ganz auszuschließen sind, empfiehlt es sich, die ganze Konstruktion in einen try-finally-Block einzubetten. Als erste Aktion wird eine neue und eindeutig identifizierte Datenbanksitzung eröffnet. Das ist innerhalb von ISAPI-DLLs deshalb wichtig, weil ein anderer Client zur gleichen Zeit in einem parallelen Thread innerhalb der selben Datenbank suchen kann. Im Beispiel wird die Thread-ID zur Bildung eines unverwechselbaren Sitzungsnamen herangezogen.

 try
  Ses:=Sessions.OpenSession('Xyz'+IntToStr(Abs(GetCurrentThreadID)));
  :
 finally
Anschließend wird ein Query-Objekt angelegt und mit den Abfrageparametern spezifiziert. Voraussetzung für das Beispiel ist, daß ein Datenbankalias "WebFirma" angelegt sei, unter dem die Tabelle "TelefDat.DBF" zu finden ist.

Beim Formulieren des SQL-Ausdruckes entscheiden Sie über die Qualität der Suche. Im vorliegenden Fall wird eine einzelne Datentabelle nach Teilausdrücken des Datenfeldes "Name" durchsucht. Sie können hier aber auch viel leistungsfähigere Abfragen entwerfen, die sich über mehrere in Relation stehende Datentabellen erstrecken. All diese Aktionen verlaufen innerhalb des oben beschriebenen Schutzblockes.

 Q:=TQuery.Create(nil);
 Q.DatabaseName:='WebFirma';
 Q.SessionName:=Ses.SessionName;
 Q.SQL.Add('SELECT * FROM "TelefDat.dbf"');
 Q.SQL.Add('WHERE Name LIKE "%' + sQuery + '%"');
 Q.SQL.Add('ORDER BY Name,Vorname');
 Q.Prepare;

Beim Öffnen der Abfrage sollte Ihnen nun eine auszugebende Datenmenge zur Verfügung gestellt werden. Falls wider Erwarten Fehler beim Öffnen der Query entstehen, wird hier eine Fehlermeldung an den Browser gesandt. Das können Sie auch unterlassen, auf alle Fälle muß der mögliche Datenfehler von Ihrem Programm abgefangen werden.

 try
  Q.Open
   :
 except
  s:=s + '<BR>Fehler beim &Ouml;ffnen der Abfrage!'
 end;

Die Tabelle mit den gefundenen Daten erstellen Sie durch Ausgabe der entsprechenden HTML-Tags. Die einzelnen Datenzeilen und Zellenwerte lassen sich dabei unproblematisch über zwei ineinander verschachtelte Schleifen beschreiben.

 s:=s + '<TABLE BORDER=1 WIDTH=100%>'
  +'<TR><TH>Name</TH><TH>Vorname</TH><TH>Telefon</TH><TH>FAX</TH></TR>';
 while not Q.EOF do begin                                    // je Datensatz
  s:=s + '<TR>';
  for i:=0 to 3 do begin                                     // 4 Spalten
   sData:=Q.Fields[i].AsString;
   if Length(sData)=0 then sData:='&nbsp;';
   s:=s + '<TD>' + sData + '</TD>';
  end;
  s:=s + '</TR>';
  Q.Next;
 end;
 s:=s + '</TABLE>';
Damit ist die ISAPI-DLL zur Abfrage der Telefondatenbank bis auf Feinheiten fertig. Das komplette Projekt finden Sie als Quelltext am Ende der Seite unter dem Titel "DbfQuery". Der Aufruf erfolgt gewöhnlich aus einem Suchformular "DbfQuery.htm", wie in der Abbildung zu sehen. Das darin enthaltene Eingabefeld heißt intern "Name".

DBQuery

Aber auch Anfragen über die Adreßzeile des Browsers beantwortet die Serverextension korrekt. Die Syntax des Aufrufes lautet:

 http://myhost/scripts/DbfQuery.dll?Name

Datenbankeingaben mittels ISAPI

Projekte zum Eintragen von Daten in ferne Datenbanken bauen Sie mit dem ISAPI-Experten hot ähnlich zu den Abfragen aus. Für das Eröffnen der Datenbanksitzung gelten die gleichen Maßgaben wie im vorigen Beispiel. Bei nur einer Tabelle verwenden Sie statt des TQuery-Objektes bei dBASE-Datenbanken eine Datenmenge vom Typ TTable. Im Kern des Eingabeprogrammes dekodieren Sie die Eingabedaten aus dem HTML-Formular mit der Prozedur GetContentData aus dem Tool "IsapiHlp.PAS".

Unter der Voraussetzung, daß Sie die Felder des Eingabeformulars identisch zu den Feldern der Datenbank benannt haben, kann Ihnen eine Zählschleife über alle Felder der Datenbank die Handarbeit zum Eintragen aller Werte in den neuen Datensatz abnehmen.

 T.Open;
 T.Append;
 Liste:=TStringList.Create;
 GetContentData(Liste);
 for i:=0 to T.FieldCount-1 do begin
  sFldName:=T.Fields[i].FieldName;
  sValue:=Liste.Values[sFldName];
  T.Fields[i].AsString:=sValue;
 end;
 Liste.Free;
 T.Post;
Das Musterprojekt zur DLL "DbfEntry" und das darin benutzte Formular "DbfEntry.htm" demonstrieren ein solches Vorgehen. Für den Dateneintrag wurde hier nur die Methode "POST" zugelassen. Beachten Sie bitte auch bei diesem Beispiel, wie wiederum mögliche Fehler aufgefangen werden.

dynamische Datenbankgrafiken

Ein nächstes Vorhaben vereinigt das bisher Dargestellte zur Grafikerzeugung und Datenbankarbeit. Der Ausgabetyp der zu erzeugenden ISAPI-DLL ist grafischer Natur, auch wenn einzelne Textelemente in der Grafik enthalten sind. Die Zahlenbasis der Grafik entstammt einer Datenbank.

Das Beispiel "DbfChart" soll ein Umsatzdiagramm zeichnen, dessen Datenbasis aus der Tabelle "Umsatz.DBF" gewonnen wird. Darin seien zugeordnet zu Verkäufer-Nummern die Umsatzzahlen einzelner Monate und Jahre gespeichert. Als Abrufmethode der DLL wird "GET" geplant, um das Ergebnis später besser in HTML-Dateien einbetten zu können. Ein erster Aufrufparameter sei die vierstellige Verkäufer-Nr, als zweiter durch ein "+"-Zeichen separierte Parameter folgt die vierstellige Jahreszahl.

Zuerst muß einmal die Höhe des maximalen Monatswertes ermittelt werden, um eine Maßstabsfaktor für das Zeichnen des Diagramms zu errechnen. Sonst laufen Sie Gefahr, daß die besten Verkaufsgewinne einfach oben abgeschnitten werden. Auch hier ist eine SQL-Konstuktion hilfreich. Aus dem ermittelten Umsatzmaximum wird anschließend per Division durch die Bildhöhe unter Abzug von beispielsweise 20 Randpixeln der korrekte Streckungsfaktor gewonnen.

 Q.SQL.Add('SELECT MAX(Umsatz) FROM "Umsatz.dbf"');
 Q.SQL.Add('WHERE VK_Nr="' + sNr + '" AND Jahr=' + sYear);
 Q.Open;
 nMax:= Q.Fields[0].AsCurrency;
 nFak:=(MyBmp.Height-20)/nMax

Unter diesen Voraussetzungen ist die Datenbankabfrage im engeren Sinne nicht schwer zu formulieren.

 Q.SQL.Clear;
 Q.SQL.Add('SELECT * FROM "Umsatz.dbf"');
 Q.SQL.Add('WHERE VK_Nr="' + sNr + '" AND Jahr=' + sYear);
 Q.Prepare;
 Q.Open;
 while not(Q.EOF) do begin
  x:=Q.FieldByName('Umsatz').AsCurrency;
        :
 end;
Der Umsatzwert "x" eines Monats wird dann an eine Zeichenroutine namens DrawBar übergeben, die als separate Unterfunktion in der DLL realisiert ist. Diese zeichnet einen Balken entsprechender Höhe in die Zeichenfläche des Diagamm-Bitmaps.

Bei der Planung aller Zeichenarbeiten am Bitmap steht es Ihnen frei, ob sie von vornherein mit monochromen oder farbigen Bildern arbeiten. Beschriftungen im Diagramm werden über Aufrufe von

 MyBmp.Canvas.TextOut
erreicht.

Dieses abschließende Projekt finden Sie unter dem Namen "DbfChart" im Quelltext. Bei Tests mit diesem Programm fragen Sie bitte nach der Verkäufer-Nr 4711 oder 1234 und dem Geschäftsjahr 1999. Die Abbildung zeigt Ihnen, wie unter Einsatz einer weiteren ISAPI-DLL eine dynamische Abfrage entsteht, in die die soeben beschriebene Chart-Generierung eingebettet ist.

DBGraph

Nach oben sind also für Ihre eigene Vorhaben alle Grenzen offen. Gutes Gelingen!


Download der Projektquelltexte:
Disk DbfQuery.zip - die beschriebene Datenbankabfrage (6 kByte)
Disk DbfEntry.zip - die beschriebene Datenbankeingabe (6 kByte)
Disk DbfChart.zip - dynamisch generiertes Datenbankdiagramm (6 kByte)

[ Seitenanfang ] [ ISAPI ]

J. Hummel,   2000