In­halts­ver­zeich­nis

Bereich UrsAsyncWebSite

Klasse UrsAsyncRealm

Klasse UrsAsyncFileProcessorResponse

Klasse UrsAsyncWebSite

Member-Übersicht (öffentliche Member)

Member-Übersicht (geschützte Member)

Member-Übersicht (zu überschreibende Member)

Request-Handler

Template-Prozessor


Bereich UrsAsyncWebSite

Die Klasse UrsAsyncWebSite dient als abstrakte Basisklasse für die Erstellung von Web-Seiten. Sie arbeitet eng mit der Klasse UrsAsyncWebServer zusammen. Unterstützt wird die Klasse durch die Klasse UrsAsyncRealm, über die Zugangsdaten zu geschützten Seiten verwaltet werden können. Die Klasse UrsAsyncFileProcessorResponse überschreibt die Klasse AsyncFileResponse und erleichtert die Verwendung von Template-Prozessoren, wenn Dateiinhalte zurück gesendet werden sollen (s. ESP8266AsyncWebServer: Respond with content coming from a File containing templates). Die Klasse UrsAsyncWebSiteBase versteckt der Übersichtlichkeit wegen viele der internen Member.

Klasse UrsAsyncRealm

Die Klasse UrsAsyncRealm dient der Aufnahme von Zugangsdaten für geschützten Seiten. Sie ist im Wesentlichen ein Container für drei Strings.

// Zugangsdaten für geschützte Bereiche
class UrsAsyncRealm {
public:
  String name;         // Bezeichnung des geschützten Bereichs
  String username;     // Authentifizierung: Benutzername
  String password;     // Authentifizierung: Passwort

  UrsAsyncRealm() {}
  UrsAsyncRealm(String name, String username, String password) : name(name), username(username), password(password) {}
};

Wird eine Instanz dieser Klasse per setRealm an eine Web-Site gebunden, ist zum Aufruf der Seite im Browser die Angabe der hinterlegen Zugangsdaten notwendig. Hier ein Beispiel, wie es unter FireFox aussieht. "ESP8266-Energy-Meter" ist der Name des geschützten Bereichs:

Authentifizierung unter FireFox

Klasse UrsAsyncFileProcessorResponse

Siehe ESPAsyncWebSever Respond with content coming from a File

// Template-Prozessor mit Zwischenspeicherung der Platzhalter-Daten
class UrsAsyncFileProcessorResponse : public AsyncFileResponse {
public:
  UrsAsyncFileProcessorResponse(const String& path, const String& contentType = String(),
                                const bool download = false);
  virtual String templateProcessor(const String& var) = 0;
};

Der Konstruktor initialisiert die Basisklasse AsyncFileResponse mit den übergebenen Parametern und registriert dabei die Methode templateProcessor als Callback-Funktion für die Platzhalterersetzung. Die abstrakte Methode templateProcessor muss in einer konkreten Ableitung dieser Klasse passend überschrieben werden (s.u. Template-Prozessor mit Zwischenspeicherung der Platzhalter-Daten).

Klasse UrsAsyncWebSite

Die Klasse UrsAsyncWebSite ist eine abstrakte Klasse, die als Grundlage für eigene Web-Site-Klassen dient. In der Klassendefinition sind nur die Member enthalten, die für das eigentliche Programm relevant sind. Nur intern erforderliche Member sind in der Klasse UrsAsyncWebSiteBase versteckt.

 Die abstrakten geschützten Member dienen der Konfiguration bzw. der Bearbeitung von HTTP-Anfragen. Sie müssen in einer Ableitung implementiert werden.

Member-Übersicht (öffentliche Member)

Member Funktion Anmerkung
void setRealm (UrsAsyncRealm& realm) Legt Authentifizierungsdaten fest. Ohne die Übergabe eines UrsAsyncRealm-Objekts wird die Ressource ohne vorhergehende Authentifizierung zurück geliefert.

Member-Übersicht (geschützte Member)

Member Funktion Anmerkung
UrsAsyncWebSite (UrsAsyncWebServer::FileUpload upload WebRequestMethodComposite method) Konstruktor Details s.u.
Zum Datei-Upload s. auch UrsAsyncFSHelper::fileUploadHandler.
Siehe auch UrsAsyncWebServer::registerWebSite.
Weitere Konstruktoren mit Default-Werten   Der Default-Wert für den Parameter upload ist UrsAsyncWebServer::FileUpload::none, d.h. der Datei-Upload für diese Seite wird nicht freigegeben.
Der Default-Wert für den Parameter method ist HTTP-ANY, d.h. die Web-Site behandelt jede Art von Request.
bool sendFileResponse (AsyncWebServerRequest * request, const String& path, const String& contentType = String(), bool download = false) Sendet eine Datei-Antwort und bindet ggf. die implementierte Template-Prozessor-Methode ein. Für den Fall, dass lediglich eine Datei zurück gesendet werden soll, bei der es nur darum geht, Platzhalter zu ersetzen. Die (überschriebene) Template-Prozessor-Funktion templateProcessor wird als Template-Prozessor beim ESPAsyncWebServer registriert.
void SysTimeWebSiteClass::handleRequest(
                          AsyncWebServerRequest * request) {
  sendFileResponse(request, systimeFileName);
}

Der Rückgabewert gibt an, ob die angeforderte Datei gefunden wurde: false = Datei nicht gefunden.
bool sendFileResponse (AsyncWebServerRequest * request, bool download = false) -dito- Ruft sendFileResponse mit Default-Werten auf und nimmt dabei den Dateinamen request.url.
Der Rückgabewert gibt an, ob die angeforderte Datei gefunden wurde: false = Datei nicht gefunden.
bool sendFileResponse (AsyncWebServerRequest * request, UrsAsyncFileProcessorResponse* pr) Sendet eine Datei-Antwort und bindet das übergebene Template-Prozessor-Objekt ein. Der Dateiname wird dem Template-Prozessor-Objekt pr entnommen.
Der Rückgabewert gibt an, ob die angeforderte Datei gefunden wurde: false = Datei nicht gefunden.
siehe unten Template-Prozessor mit Zwischenspeicherung der Platzhalter-Daten
Konstruktor

Über den Konstruktor wird Einiges zum Verhalten der Seite geregelt.

Das erste Argument upload steuert, ob über den in der Klasse UrsAsyncFSHelper definierten File-Upload-Handler ein File-Upload ermöglicht werden soll. Um sprechende Einstellungswerte zu erhalten, wurde die Enumeration UrsAsyncWebServer::FileUpload definiert:

// Einstellungswerte für das File-Upload-Verhalten
enum class FileUpload {
  none,       // Die Seite soll kein Upload unterstützen
  allowed,    // Die Seite soll ein Upload unterstützen
  asDesigned  // Upload wie in der Klassendefinition vorgesehen
};

FileUpload::asDesigned ist in diesem Kontext nicht sinnvoll. Dieser Wert wird durch FileUpload::none ausgetauscht.

Der Default-Wert für dieses Argument ist FileUpload::none.

Das zweite Argument method gibt an, welche Request-Typen die Seite bedienen soll. Die Angabe 0 übernimmt den Eintrag aus der Web-Site-Instanz. Ansonsten sind Kombinationen (Oder-Verknüpfung) folgender Konstanten möglich. Ungültige Werte werden durch HTTP_ANY ersetzt.

Der Default-Wert für dieses Argument ist HTTP_ANY.

Beide Werte können bei Bedarf beim Registrieren der Site beim Web-Server durch die Methode UrsAsyncWebServer::registerWebSite überschrieben werden.

Member-Übersicht (zu überschreibende Member)

Member Funktion Anmerkung
String getUrl () Liefert die URL für diese Seite.

Das Voranstellen eines "/" ist nicht notwendig. Die Methode registerWebSite überprüft dies und fügt ggf. ein "/" am String-Anfang hinzu.

// Liefert die URL für diese Seite.
virtual String getUrl() override { return "index.html"; }
String getRewrites () Liefert alternative Ressourcen-Bezeichnungen. Die verschieden Alternativen müssen durch ein "|" getrennt werden.

Das Voranstellen eines "/" ist nicht notwendig. Die Methode registerWebSite überprüft dies und fügt ggf. ein "/" am String-Anfang hinzu.

// Liefert alternative Ressourcen-Bezeichnungen.
virtual String getRewrites() override 
        { return "index|index.htm"; }
void handleRequest (AsyncWebServerRequest * request) Handler-Methode, liefert den HTML-Response. Diese Methode wird aufgerufen nachdem die Authentifizierung geprüft wurde. ggf. wurde vorher ein Datei-Upload durchgeführt. Die Default-Implementierung entnimmt der URL den Dateinamen für das SPIFFS und registriert die Template-Prozessor-Methode sofern diese implementiert wurde.
Zum Datei-Upload s. auch UrsAsyncFSHelper::fileUploadHandler.
Zur Implementierung des Request-Handler: s.u.
String templateProcessor (String templString) Template-Prozessor. Der Template-Prozessor liefert die Ersetzungstexte zu den Platzhaltern in der HTTP-Antwort.
Zur Funktionsweise siehe ESPAsyncWebServer: Template Processing
Zur Implementierung eines Template-Prozessors: s.u.

 

Request-Handler

Folgende Abschnitte zum ESPAsyncWebServer sollte man kennen:

Die folgende Grafik mach den Ablauf der Request-Verarbeitung noch einmal deutlich:

Ablauf der Request-Verarbeitung

Die Authentifizierung erfolgt intern dann, wenn über setRealm Zugangsdaten festgelegt wurden. Wenn vom Browser keine Zugangsdaten übermittelt werden, werden diese angefordert. Der Response ist in diesem Fall die Aufforderung, Zugangsdaten abzufordern (s.o. Klasse UrsAsyncRealm).

Wenn die Zugangsberechtigung geklärt ist, wird intern geprüft, ob ein File-Upload angefordert wird. Der Request des Browsers enthält in diesem Fall entsprechende Header-Attribute, die vom ESPAsyncWebServer erkannt werden. Ist dies der Fall, wird der in UrsAsyncFSHelper::fileUploadHandler implementierte Upload-Handler aufgerufen.

Zuletzt wird der überschriebene Request-Handler der Web-Site-Klasse aufgerufen. Dieser erstellt direkt oder indirekt ein AsyncFileResponse-Objekt, dass per request.send dem ESPAsnycWebServer zur Übermittlung an den Anforderer (Browser) übergeben wird.

Wie Request-Handler prinzipiell funktionieren, ist im Abschnitt Responses in der Dokumentation des ESPAsyncWebServer ausführlich beschrieben.

Default-Implementierung

Die Default-Implementierung entnimmt der URL den Dateinamen für das SPIFFS und registriert die Template-Prozessor-Methode beim ESPAsyncWebServer sofern diese implementiert wurde. Dies ist in vielen Fällen ausreichend. Authentifizierung und File-Upload können dazu geschaltet werden, wie in den beiden folgenden Absätzen beschrieben wird.

Authentifizierung

Der Request-Handler muss sich nicht um eine evtl. gewünschte Authentifizierung kümmern. Dies geschieht automatisch intern, wenn der Web-Site Zugangsdaten per setRealm übergeben wurden.

Aktivierung des File-Upload

Der File-Upload geschieht automatisch, wenn man in der von UrsWebSite abgeleiteten konkreten Web-Site den Konstruktor der Basis-Klasse entsprechend bedient. Die Voreinstellung ist, dass kein Upload unterstützt wird. Soll jedoch für die Web-Site standardmäßig ein Upload vorgesehen sein, kann der Parameter upload im Konstruktor mit dem Wert  UrsAsyncWebServer::FileUpload::allowed versehen werden.

class MyWebSiteClass : public UrsAsyncWebSite {
public:
  MyWebSiteClass() : UrsAsyncWebSite(UrsAsyncWebServer::FileUpload::allowed) {}
  // ...
};

Eine zweite Möglichkeit besteht bei der Registrierung des Web-Site-Objekts bei der UrsAsyncWebServer-Instanz. registerWebSite ist wie folgt definiert:

void registerWebSite(UrsAsyncWebSite & ws, 
                     FileUpload registerStdUploadHandler = FileUpload::asDesigned, 
                     WebRequestMethodComposite method = HTTP_ANY);

Über den zweiten Parameter registerStdUploadHandler kann die Voreinstellung geändert werden. Mit dem Default-Wert asDesigned wird der per Konstruktor eingestellt Wert wirksam. none bzw. allowed überschreiben das Verhalten der Seite für den Web-Server in dem die Seite registriert wird. Dies zeigt das folgende Beispiel: 

class MyWebServerClass : public UrsAsyncWebServer {
protected:
  MyWebSiteClass MyWebSite;
// ...

public:
// ...

// Startet den Web.Server
  void begin(); 

// ...
};

void MyWebServerClass::begin() {
// Web-Sites registrieren (WICHTIG: muss vor serveStatic("/"...) geschehen!)
registerWebSite(MyWebSite, FileUpload registerStdUploadHandler = FileUpload::allowed); // <-- Upload freigeben

// ...
}

Response

Damit für den Request-Handler nur noch die Aufgabe, eine HTTP-Antwort, das Response-Objekt, zu generieren. Hier ein einfaches Beispiel:

void MyWebSiteClass::handleRequest(AsyncWebServerRequest * request) {
  AsyncResponseStream *response = request->beginResponseStream("text/html");
  response->print("<!DOCTYPE html><html><head><title>Hello</title>");
  response->print("</head><body><p>Hello World</p></body></html>");

  // Response absenden
  request->send(response);
}

Weitere Möglichkeiten finden sich unter ESPAsyncWebServer: Responses.

File-Response

Ein Überschreiben des Request-Handlers ist nur dann notwendig, wenn die Default-Implementierung für diesen Zweck nicht ausreichend ist, weil z.B. Query-Parameter überprüft werden müssen.

Das endgültige zurücksenden des Dateiinhalts kann dann einfach über die Methode sendFileResponse erfolgen. sendFileResponse überprüft zunächst, ob die angegebene Datei vorhanden ist. Ist dies nicht der Fall, wird per UrsAsyncWebServer::sendNotFoundResponse (s.o.) eine Fehlermeldung zurück gesandt. Falls die Überprüfung positiv ausfällt, wird per request.send die Datei an den Anfordernden zurück geliefert. Ist in der Klasse eine Template-Prozessor-Methode implementiert (s.u.), wird diese ebenfalls beim ESPAsyncWebServer registiert.

Die Parameter von sendFileResponse entsprechenden denen im Abschnitt ESPAsyncWebServer Respond with content coming from a File beschriebenen.

Template-Prozessor

Platzhalter (Templates) sind eigentlich nur dann sinnvoll, wenn in einem fest hinterlegten Antworttext Detail-Inhalte ausgetauscht werden sollen. Dies ist der Fall, wenn die Antwort im Flash abgelegt ist, als Datei oder als PROGMEN-Feld. UrsAsyncWebSite unterstützt die Bereitstellung von Ressourcen aus Dateien.

Die einfache Variante (s.u.) funktioniert immer dann, wenn die Platzhalter-Ersatztexte "on the fly" ermittelt werden können. Wenn die Daten nicht jederzeit konsistent abgerufen werden können, ist eine Zwischenspeicherung der Platzhalter-Daten notwendig. Das Programmbeispiele Beispiel 3 und Beispiel 4 zeigen den Unterschied.

Einfache Variante

Hier reicht es im Wesentlichen aus, die Methode templateProcessor in der Ableitung von UrsAsyncWebSite zu überschreiben.

HTML-Code

Das Programmbeispiel Beispiel 3 zeigt diese Verwendung bei der Seite SysTimeWebSiteClass. Für diese Erläuterung wurden Unwesentliches weggelassen.

Zunächst einmal muss ein HTTP-Code, in diesem Fall eine HTML-Seite, als Datei im SPIFFS abgelegt werden. Hier ein Beispiel:

Datei "time.html" im SPIFFS.

01: <!DOCTYPE html>
02: <html>
03:    <head>
04:       <meta content="text/html; charset=UTF-8" http-equiv="content-type">
05:    </head>
06: 
07:    <body>
08:       <p>Die aktuelle Systemzeit ist %millis% seit Start.</p>
09:    </body>
10: </html>

In der Zeile 8 findet man den Platzhalter %millis% der bei Abruf der Seite durch den aktuellen Wert von millis() ersetzt werden soll.

UrsAsyncWebSite ableiten

Wie immer wird UrsAsyncWebSite abgeleitet. Bereits in der Klassendefinition werden die benötigten Elemente angelegt. Die Anlage eine ".cpp" ist nicht notwendig. Der (einfache) Template-Prozessor reagiert nur auf den Platzhalter "%millis%".

class TimeWebSiteClass {
// ...
   // Ersetzt die Platzhalter
   String templateProcessor(String TemplString) override {
     if (TemplString == "millis") return String(millis());
     return String();
   }
// ...

Das Ganze im Kontext:

// TimeWebSite.h

#ifndef _TIMEWEBSITE_h
#define _TIMEWEBSITE_h

#include <UrsAsyncWebSite.h>

class TimeWebSiteClass : public UrsAsyncWebSite {
 protected:
   // Ersetzt die Platzhalter
   String templateProcessor(String TemplString) override {
     if (TemplString == "millis") return String(millis());
     return String();
   }

   // Liefert die URL für diese Seite. Gleichzeitig Dateiname der HTML-Datei.
   virtual String getUrl() override { return "time.html"; }

   // Liefert alternative Ressourcen-Bezeichnungen.
   virtual String getRewrites() override { return "time|time.htm";}
};
#endif
Instanziieren der der Klasse

Eine Instanz der Klasse muss angelegt werden und beim Web-Server registriert werden.

class MyWebServerClass : public UrsAsyncWebServer {
// ...
  TimeWebSiteClass TimeWebSite;

// ...

  void begin() {
    registerWebSite(TimeWebSite);
// ...
  }
};

Das war's! Für eine solch einfache Anwendung gibt es gewiss einfachere Lösungen. Aber wenn man in einem größeren Projekt den Überblick behalten will, macht es schon Sinn, möglichst viel möglichst strukturiert und standardisiert abzulegen.

Mit Zwischenspeicherung der Platzhalter-Daten

Spannender wird es, wenn die Seiten komplexer werden. Jetzt kommt die Klasse UrsAsyncFileProcessorResponse ins Spiel. Das Programmbeispiel Beispiel 4 zeigt diese Verwendung bei der Seite SysTimeWebSiteExClass. Für diese Erläuterung wurden Unwesentliches weggelassen.

class TimeWebSiteExClass : public UrsAsyncWebSite {
 protected:
   class Response; // nicht öffentliche Klasse

   // Handler-Methode, liefert den HTML-Response.
   virtual void handleRequest(AsyncWebServerRequest * request);

   // Liefert die URL für diese Seite. Gleichzeitig Dateiname der HTML-Datei.
   virtual String getUrl() override { return "systime.html"; }

   // Liefert alternative Ressourcen-Bezichnungen.
   virtual String getRewrites() override { return "systime|systime.htm|time|time.html|time.htm"; }
};

Der erste Unterschied ist, dass die Methode templateProcessor nicht überschrieben wurde. Für die Ersetzung der Platzhalter wird zur Zwischenspeicherung der Ersatzdaten eine eigene Klasse angelegt (class Response). Damit es nicht versehentlich zu Namenskonflikten kommt, wird die Klasse als Element der Web-Site definiert. Sie muss nicht öffentlich verfügbar sein, weil sie nur im Kontext des Request-Handlers benötigt wird. Sie wird deshalb auch nicht in der Header-Datei definiert sondern nur deklariert. Die Definition erfolgt dann in der Code-Datei zur Klasse TimeWebSiteExClass.

In dieser Klasse werden Variablen zur Ablage der Platzhalter-Daten (currentMillis) definiert. Im Konstruktor wird deren Inhalt festgelegt. Ebenso wird hier die Methode templateProcessor implementiert.

// TimeWebSiteEx.cpp

class TimeWebSiteExClass::Response : public UrsAsyncFileProcessorResponse {
public:
  String currentMillis;  // Speicher für die aktuelle Zeit

  // Konstuktor
  Response(const String & path) : UrsAsyncFileProcessorResponse(path, String(), false) {
    currentMillis = String(millis()); // Aktuellen Zeitwert zwischenspeichern
  }

  // Template-Prozessor
  virtual String templateProcessor(const String & var) override {
    if (var == "millis") return currentMillis;
    return String();
  }
};

Die Implementierung des Request-Handlers ist ebenfalls recht einfach:

void SysTimeWebSiteExClass::handleRequest(AsyncWebServerRequest * request) {
  sendFileResponse(request, new Response(request->url()));
}

 Um das neu erstellte Response-Objekt braucht man sich nicht weiter zu kümmern. Der ESPAsyncWebServer zerstört es, wenn es nicht weiter benötigt wird.